From ef7f2c845ebcd89a9ecc697a11d3af66c63959ae Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 4 Jan 2018 15:09:05 +0100 Subject: [PATCH 001/128] first stab a jgraphx based visualization --- Core/ivy.xml | 7 + Core/ivysettings.xml | 3 +- Core/nbproject/project.properties | 1 + Core/nbproject/project.xml | 6 +- .../communications/AccountsRootChildren.java | 39 ++- .../autopsy/communications/Bundle.properties | 2 + .../autopsy/communications/CVTEvents.java | 21 ++ .../communications/CVTTopComponent.form | 18 +- .../communications/CVTTopComponent.java | 26 +- .../OpenCommVisualizationToolAction.java | 20 +- .../communications/PinAccountAction.java | 7 + .../communications/PinAccountEvent.java | 24 ++ .../communications/RelationshipNode.java | 2 +- .../communications/VisualizationPanel.form | 44 +++ .../communications/VisualizationPanel.java | 258 ++++++++++++++++++ 15 files changed, 460 insertions(+), 18 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form create mode 100644 Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java diff --git a/Core/ivy.xml b/Core/ivy.xml index 35cb8de904..2a099d4e31 100755 --- a/Core/ivy.xml +++ b/Core/ivy.xml @@ -6,6 +6,13 @@ + + + + + + + diff --git a/Core/ivysettings.xml b/Core/ivysettings.xml index c27d905255..7a4d38c65e 100755 --- a/Core/ivysettings.xml +++ b/Core/ivysettings.xml @@ -3,7 +3,8 @@ - + + diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index d98228ef4e..ff1f4f81de 100755 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -5,6 +5,7 @@ file.reference.commons-dbcp2-2.1.1.jar=release\\modules\\ext\\commons-dbcp2-2.1. file.reference.commons-pool2-2.4.2.jar=release\\modules\\ext\\commons-pool2-2.4.2.jar file.reference.jdom-2.0.5-contrib.jar=release/modules/ext/jdom-2.0.5-contrib.jar file.reference.jdom-2.0.5.jar=release/modules/ext/jdom-2.0.5.jar +file.reference.jgraphx-v3.8.0.jar=release/modules/ext/jgraphx-v3.8.0.jar file.reference.jsoup-1.10.3.jar=release/modules/ext/jsoup-1.10.3.jar file.reference.jython-standalone-2.7.0.jar=release/modules/ext/jython-standalone-2.7.0.jar file.reference.mchange-commons-java-0.2.9.jar=release/modules/ext/mchange-commons-java-0.2.9.jar diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 1663abca2b..baaf061b92 100755 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -346,7 +346,11 @@ ext/commons-dbcp2-2.1.1.jar - release\modules\ext\commons-dbcp2-2.1.1.jar + release/modules/ext/commons-dbcp2-2.1.1.jar + + + ext/jgraphx-v3.8.0.jar + release/modules/ext/jgraphx-v3.8.0.jar ext/tika-parsers-1.14.jar diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java index 63e1e17d82..881b14b67d 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java @@ -18,10 +18,14 @@ */ 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; @@ -87,17 +91,18 @@ class AccountsRootChildren extends ChildFactory { } 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; - private AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { + AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager)); this.accountDeviceInstanceKey = accountDeviceInstanceKey; this.commsManager = commsManager; @@ -110,10 +115,18 @@ class AccountsRootChildren extends ChildFactory { 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(); } @@ -139,5 +152,27 @@ class AccountsRootChildren extends ChildFactory { accountDeviceInstanceKey.getDataSourceName())); // NON-NLS return s; } + + @Override + public Action[] getActions(boolean context) { + ArrayList 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)); + } + } } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 0f1b490d78..5e7d93adf0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -15,3 +15,5 @@ FiltersPanel.refreshButton.text=Refresh FiltersPanel.deviceRequiredLabel.text=Select at least one. FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. +VisualizationPanel.jButton1.text=jButton1 +CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java new file mode 100644 index 0000000000..d4c76ce2d8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -0,0 +1,21 @@ +/* + * 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. + */ +package org.sleuthkit.autopsy.communications; + +import com.google.common.eventbus.EventBus; + +/** + * + */ +public class CVTEvents { + +private final static EventBus cvtEventBus = new EventBus(); + + public static EventBus getCVTEventBus() { + return cvtEventBus; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form index df30cb548f..a24037b927 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form @@ -18,9 +18,9 @@ - + - + @@ -75,6 +75,20 @@ + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 1c0582a300..f19e998fb0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.communications; +import com.google.common.eventbus.Subscribe; import java.util.List; import java.util.stream.Collectors; import org.openide.explorer.ExplorerManager; @@ -37,11 +38,11 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; @RetainLocation("cvt") @NbBundle.Messages("CVTTopComponent.name= Communications Visualization") public final class CVTTopComponent extends TopComponent implements ExplorerManager.Provider { - + private static final long serialVersionUID = 1L; private final ExplorerManager messagesBrowserExplorerManager; private final ExplorerManager acctsBrowserExplorerManager; - + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public CVTTopComponent() { initComponents(); @@ -63,6 +64,14 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag * Nodes from the accounts browser to the messages browser. */ acctsBrowserExplorerManager = new ExplorerManager(); + + vizPanel.initVisualization(acctsBrowserExplorerManager); + CVTEvents.getCVTEventBus().register(this); + } + + @Subscribe + public void pinAccount(PinAccountEvent pinEvent) { + browseVisualizeTabPane.setSelectedIndex(1); } /** @@ -76,6 +85,7 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag splitPane = new javax.swing.JSplitPane(); browseVisualizeTabPane = new javax.swing.JTabbedPane(); accountsBrowser = new org.sleuthkit.autopsy.communications.AccountsBrowser(); + vizPanel = new org.sleuthkit.autopsy.communications.VisualizationPanel(); filtersPane = new org.sleuthkit.autopsy.communications.FiltersPanel(); splitPane.setDividerLocation(400); @@ -83,6 +93,7 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag browseVisualizeTabPane.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N + browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N splitPane.setLeftComponent(browseVisualizeTabPane); @@ -94,9 +105,9 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(6, 6, 6) - .addComponent(filtersPane, javax.swing.GroupLayout.PREFERRED_SIZE, 250, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(filtersPane, javax.swing.GroupLayout.PREFERRED_SIZE, 265, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(splitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1339, Short.MAX_VALUE) + .addComponent(splitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1324, Short.MAX_VALUE) .addGap(0, 0, 0)) ); layout.setVerticalGroup( @@ -116,6 +127,7 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag private javax.swing.JTabbedPane browseVisualizeTabPane; private org.sleuthkit.autopsy.communications.FiltersPanel filtersPane; private javax.swing.JSplitPane splitPane; + private org.sleuthkit.autopsy.communications.VisualizationPanel vizPanel; // End of variables declaration//GEN-END:variables @Override @@ -123,12 +135,12 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag super.componentOpened(); WindowManager.getDefault().setTopComponentFloating(this, true); } - + @Override public ExplorerManager getExplorerManager() { return acctsBrowserExplorerManager; } - + @Override public void open() { super.open(); @@ -140,7 +152,7 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag */ filtersPane.updateAndApplyFilters(); } - + @Override public List availableModes(List modes) { /* diff --git a/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java b/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java index fdccf56af7..a977ff2c09 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java +++ b/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java @@ -19,6 +19,8 @@ package org.sleuthkit.autopsy.communications; import java.awt.Component; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.ImageIcon; import javax.swing.JButton; import org.openide.awt.ActionID; @@ -29,9 +31,10 @@ import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; -import org.openide.util.actions.Presenter; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.core.RuntimeProperties; /** * Action that opens the CVT. Available through the Tools menu and the main @@ -41,18 +44,27 @@ import org.openide.windows.WindowManager; id = "org.sleuthkit.autopsy.communicationsVisualization.OpenCVTAction") @ActionRegistration(displayName = "#CTL_OpenCVTAction", lazy = false) @ActionReferences(value = { - @ActionReference(path = "Menu/Tools", position = 102)}) + @ActionReference(path = "Menu/Tools", position = 102) + , + @ActionReference(path = "Toolbars/Case", position = 102)}) @Messages("CTL_OpenCVTAction=Communications") -public final class OpenCommVisualizationToolAction extends CallableSystemAction implements Presenter.Toolbar { +public final class OpenCommVisualizationToolAction extends CallableSystemAction { private static final long serialVersionUID = 1L; - + PropertyChangeListener pcl; private final JButton toolbarButton = new JButton(getName(), new ImageIcon(getClass().getResource("images/emblem-web24.png"))); //NON-NLS public OpenCommVisualizationToolAction() { toolbarButton.addActionListener(actionEvent -> performAction()); setEnabled(false); //disabled by default. Will be enabled in Case.java when a case is opened. + pcl = (PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { + setEnabled(RuntimeProperties.runningWithGUI() && evt.getNewValue() != null); + } + }; + Case.addPropertyChangeListener(pcl); + } @Override diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java new file mode 100644 index 0000000000..f204507299 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java @@ -0,0 +1,7 @@ +/* + * 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. + */ +package org.sleuthkit.autopsy.communications; + diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java new file mode 100644 index 0000000000..181e160041 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java @@ -0,0 +1,24 @@ +/* + * 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. + */ +package org.sleuthkit.autopsy.communications; + +import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode; + +/** + * + */ +class PinAccountEvent { + + private final AccountDeviceInstanceNode accountDeviceInstance; + + public AccountDeviceInstanceNode getAccountDeviceInstanceNode() { + return accountDeviceInstance; + } + + PinAccountEvent(AccountDeviceInstanceNode accountDeviceInstance) { + this.accountDeviceInstance = accountDeviceInstance; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index 73fa033240..70d70b5325 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -127,7 +127,7 @@ public class RelationshipNode extends BlackboardArtifactNode { * @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) { + 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) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form new file mode 100644 index 0000000000..fd725cc140 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -0,0 +1,44 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java new file mode 100644 index 0000000000..fb2bd2eab0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -0,0 +1,258 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2017 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.communications; + +import com.google.common.collect.ImmutableSet; +import com.google.common.eventbus.Subscribe; +import com.mxgraph.layout.mxFastOrganicLayout; +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.view.mxGraph; +import java.awt.Color; +import java.util.Collection; +import static java.util.Collections.singleton; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.logging.Level; +import javax.swing.JPanel; +import org.apache.commons.lang3.StringUtils; +import org.openide.explorer.ExplorerManager; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode; +import static org.sleuthkit.autopsy.communications.RelationshipNode.getAttributeDisplayString; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AccountDeviceInstance; +import org.sleuthkit.datamodel.BlackboardArtifact; +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 org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + */ +public class VisualizationPanel extends JPanel { + + private ExplorerManager explorerManager; + private final mxGraph graph; + + private Map nodeMap = new HashMap<>(); + private final mxGraphComponent graphComponent; + + /** + * Creates new form VizPanel + */ + public VisualizationPanel() { + initComponents(); + graph = new mxGraph(); + graph.setCellsEditable(false); + graph.setCellsResizable(false); + graph.setCellsMovable(true); + graph.setCellsDisconnectable(false); + graph.setConnectableEdges(false); + graph.setDisconnectOnMove(false); + graph.setEdgeLabelsMovable(false); + graph.setVertexLabelsMovable(false); + graphComponent = new mxGraphComponent(graph); + graphComponent.setAutoScroll(true); + graphComponent.setOpaque(true); + graphComponent.setBackground(Color.WHITE); + this.add(graphComponent); + + CVTEvents.getCVTEventBus().register(this); + } + + public void initVisualization(ExplorerManager em) { + explorerManager = em; + + graph.getSelectionModel().addListener(null, (sender, evt) -> { + Object[] selectionCells = graph.getSelectionCells(); + if (selectionCells.length == 1) { + mxCell selectionCell = (mxCell) selectionCells[0]; + + if (selectionCell.isVertex()) { + try { + + CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); + + AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) selectionCell.getValue(); + explorerManager.setRootContext(new AccountDetailsNode( + singleton(adiKey.getAccountDeviceInstance()), + adiKey.getCommunicationsFilter(), + commsManager)); + } catch (TskCoreException tskCoreException) { + Logger.getLogger(VisualizationPanel.class.getName()).log(Level.SEVERE, + "Could not get communications manager for current case", tskCoreException); + } + } + } + }); + } + + private void addEdge(BlackboardArtifact artifact) throws TskCoreException { + BlackboardArtifact.ARTIFACT_TYPE artfType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); + if (null != artfType) { + + String from = null; + String[] tos = new String[0]; + + //Consider refactoring this to reduce boilerplate + switch (artfType) { + case TSK_EMAIL_MSG: + from = StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;"); + tos = StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;").split(";"); + break; + case TSK_MESSAGE: + from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM); + tos = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO).split(";"); + break; + case TSK_CALLLOG: + from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM); + tos = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO).split(";"); + break; + default: + break; + } + for (String to : tos) { + if (StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to)) { + + mxCell fromV = getOrCreateNodeDraft(from, 10); + mxCell toV = getOrCreateNodeDraft(to, 10); + + Object[] edgesBetween = graph.getEdgesBetween(fromV, toV); + + if (edgesBetween.length == 0) { + final String edgeName = from + "->" + to; + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, fromV, toV); + } else if (edgesBetween.length == 1) { + final mxCell edge = (mxCell) edgesBetween[0]; + edge.setValue(1d + (double) edge.getValue()); + edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); + } + } + } + } + } + + @Subscribe + public void pinAccount(PinAccountEvent pinEvent) { + + final AccountDeviceInstanceNode adiNode = pinEvent.getAccountDeviceInstanceNode(); + final AccountDeviceInstanceKey adiKey = adiNode.getAccountDeviceInstanceKey(); + + graph.getModel().beginUpdate(); + try { +// final String nodeId = /* +// * adiKey.getDataSourceName() + ":" + +// */ adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID(); + + mxCell v = getOrCreateNodeDraft(adiKey); + CommunicationsManager commsManager = adiNode.getCommsManager(); + + Collection relationshipSources = + commsManager.getRelationshipSources(ImmutableSet.of(adiKey.getAccountDeviceInstance()), adiNode.getFilter()); + + for (Content source : relationshipSources) { + if (source instanceof BlackboardArtifact) { + addEdge((BlackboardArtifact) source); + } + } + } catch (TskCoreException ex) { + Exceptions.printStackTrace(ex); + } finally { + // Updates the display + graph.getModel().endUpdate(); + revalidate(); + } + + new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); + } + + private mxCell getOrCreateNodeDraft(AccountDeviceInstanceKey accountDeviceInstanceKey) { + final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); + final String name =// accountDeviceInstance.getDeviceId() + ":" + + accountDeviceInstance.getAccount().getTypeSpecificID(); + mxCell nodeDraft = nodeMap.get(name); + if (nodeDraft == null) { + double size = accountDeviceInstanceKey.getMessageCount() / 10; + nodeDraft = (mxCell) graph.insertVertex( + graph.getDefaultParent(), + name, accountDeviceInstanceKey, + new Random().nextInt(200), + new Random().nextInt(200), + size, + size); + nodeMap.put(name, nodeDraft); + } + return nodeDraft; + } + + private mxCell getOrCreateNodeDraft(String name, long size) { + mxCell nodeDraft = nodeMap.get(name); + if (nodeDraft == null) { + nodeDraft = (mxCell) graph.insertVertex( + graph.getDefaultParent(), + name, + null, + new Random().nextInt(200), + new Random().nextInt(200), + size, + size); +// nodeDraft.setLabel(name); + nodeMap.put(name, nodeDraft); + } + return nodeDraft; + } + + /** + * 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") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jToolBar1 = new javax.swing.JToolBar(); + jButton1 = new javax.swing.JButton(); + + setLayout(new java.awt.BorderLayout()); + + jToolBar1.setRollover(true); + + jButton1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N + jButton1.setFocusable(false); + jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jToolBar1.add(jButton1); + + add(jToolBar1, java.awt.BorderLayout.PAGE_START); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jButton1; + private javax.swing.JToolBar jToolBar1; + // End of variables declaration//GEN-END:variables +} From c507a033b52791af7dcce035913d8e2a8df5e5ac Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 4 Jan 2018 23:49:58 +0100 Subject: [PATCH 002/128] build out jgraphx based visualization. --- .../AccountDeviceInstanceKey.java | 40 +++++ .../communications/AccountsRootChildren.java | 29 ++- .../autopsy/communications/Bundle.properties | 3 +- .../communications/MessageBrowser.java | 41 +++-- .../communications/VisualizationPanel.form | 16 ++ .../communications/VisualizationPanel.java | 169 +++++++++++++----- 6 files changed, 228 insertions(+), 70 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java index fbc2c17feb..343db541e2 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.communications; +import java.util.Objects; import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; @@ -57,4 +58,43 @@ class AccountDeviceInstanceKey { String getDataSourceName() { return dataSourceName; } + + @Override + public String toString() { + return accountDeviceInstance.getAccount().getTypeSpecificID(); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 37 * hash + Objects.hashCode(this.accountDeviceInstance); + hash = 37 * hash + (int) (this.messageCount ^ (this.messageCount >>> 32)); + hash = 37 * hash + Objects.hashCode(this.dataSourceName); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final AccountDeviceInstanceKey other = (AccountDeviceInstanceKey) obj; + if (this.messageCount != other.messageCount) { + return false; + } + if (!Objects.equals(this.dataSourceName, other.dataSourceName)) { + return false; + } + if (!Objects.equals(this.accountDeviceInstance, other.accountDeviceInstance)) { + return false; + } + return true; + } + } diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java index 881b14b67d..b0d59bd183 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java @@ -22,6 +22,7 @@ import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; @@ -78,7 +79,7 @@ class AccountsRootChildren extends ChildFactory { return new AccountDeviceInstanceNode(key, commsManager); } - private String getDataSourceName(AccountDeviceInstance accountDeviceInstance) { + static String getDataSourceName(AccountDeviceInstance accountDeviceInstance) { try { final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); for (DataSource dataSource : sleuthkitCase.getDataSources()) { @@ -153,6 +154,32 @@ class AccountsRootChildren extends ChildFactory { return s; } + @Override + public int hashCode() { + int hash = 3; + hash = 73 * hash + Objects.hashCode(this.accountDeviceInstanceKey); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final AccountDeviceInstanceNode other = (AccountDeviceInstanceNode) obj; + if (!Objects.equals(this.accountDeviceInstanceKey, other.accountDeviceInstanceKey)) { + return false; + } + + return true; + } + @Override public Action[] getActions(boolean context) { ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 5e7d93adf0..6f78ddf19b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -15,5 +15,6 @@ FiltersPanel.refreshButton.text=Refresh FiltersPanel.deviceRequiredLabel.text=Select at least one. FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. -VisualizationPanel.jButton1.text=jButton1 +VisualizationPanel.jButton1.text=redo layout CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize +VisualizationPanel.jButton2.text=pan diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index e7628e71f9..e58a1f027b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -77,27 +77,30 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager //reset panel when there is no selection messagesResultPanel.setPath(""); } else { - AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0]; - CommunicationsFilter filter = adiNode.getFilter(); - CommunicationsManager commsManager = adiNode.getCommsManager(); - final Set accountDeviceInstances; + final Node selectedNode = selectedNodes[0]; + if (selectedNode instanceof AccountDeviceInstanceNode) { + AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNode; + CommunicationsFilter filter = adiNode.getFilter(); + CommunicationsManager commsManager = adiNode.getCommsManager(); + final Set accountDeviceInstances; - if (selectedNodes.length == 1) { - final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance(); - accountDeviceInstances = Collections.singleton(accountDeviceInstance); - messagesResultPanel.setPath(accountDeviceInstance.getAccount().getTypeSpecificID()); - } else { - accountDeviceInstances = Stream.of(selectedNodes) - .map(node -> (AccountDeviceInstanceNode) node) - .map(AccountDeviceInstanceNode::getAccountDeviceInstance) - .collect(Collectors.toSet()); - messagesResultPanel.setPath(selectedNodes.length + " accounts"); + if (selectedNodes.length == 1) { + final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance(); + accountDeviceInstances = Collections.singleton(accountDeviceInstance); + messagesResultPanel.setPath(accountDeviceInstance.getAccount().getTypeSpecificID()); + } else { + accountDeviceInstances = Stream.of(selectedNodes) + .map(node -> (AccountDeviceInstanceNode) node) + .map(AccountDeviceInstanceNode::getAccountDeviceInstance) + .collect(Collectors.toSet()); + messagesResultPanel.setPath(selectedNodes.length + " accounts"); + } + AccountDetailsNode accountDetailsNode = + new AccountDetailsNode(accountDeviceInstances, filter, commsManager); + TableFilterNode wrappedNode = + new TableFilterNode(new DataResultFilterNode(accountDetailsNode, parentExplorerManager), true); + messagesResultPanel.setNode(wrappedNode); } - AccountDetailsNode accountDetailsNode - = new AccountDetailsNode(accountDeviceInstances, filter, commsManager); - TableFilterNode wrappedNode - = new TableFilterNode(new DataResultFilterNode(accountDetailsNode, parentExplorerManager), true); - messagesResultPanel.setNode(wrappedNode); } } }); diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index fd725cc140..3c45fd7568 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -37,6 +37,22 @@ + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index fb2bd2eab0..1a860c0671 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -18,22 +18,28 @@ */ package org.sleuthkit.autopsy.communications; -import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.mxFastOrganicLayout; +import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.model.mxCell; +import com.mxgraph.swing.handler.mxPanningHandler; import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxConstants; import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxStylesheet; import java.awt.Color; -import java.util.Collection; -import static java.util.Collections.singleton; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.beans.PropertyVetoException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Random; import java.util.logging.Level; import javax.swing.JPanel; import org.apache.commons.lang3.StringUtils; import org.openide.explorer.ExplorerManager; +import org.openide.nodes.Node; import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode; @@ -45,20 +51,26 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMA 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 org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; /** * */ public class VisualizationPanel extends JPanel { - + + static final private mxStylesheet mxStylesheet = new mxStylesheet(); private ExplorerManager explorerManager; private final mxGraph graph; - - private Map nodeMap = new HashMap<>(); + + private final Map nodeMap = new HashMap<>(); private final mxGraphComponent graphComponent; + + static { + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); + } /** * Creates new form VizPanel @@ -79,41 +91,67 @@ public class VisualizationPanel extends JPanel { graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); this.add(graphComponent); - + graphComponent.addMouseWheelListener(new MouseWheelListener() { + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + graphComponent.zoomTo(graphComponent.getZoomFactor() + e.getPreciseWheelRotation(), true); + } + }); + CVTEvents.getCVTEventBus().register(this); + + graph.setStylesheet(mxStylesheet); + } - + public void initVisualization(ExplorerManager em) { explorerManager = em; - + graph.getSelectionModel().addListener(null, (sender, evt) -> { Object[] selectionCells = graph.getSelectionCells(); if (selectionCells.length == 1) { mxCell selectionCell = (mxCell) selectionCells[0]; - - if (selectionCell.isVertex()) { - try { - - CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); - - AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) selectionCell.getValue(); - explorerManager.setRootContext(new AccountDetailsNode( - singleton(adiKey.getAccountDeviceInstance()), - adiKey.getCommunicationsFilter(), - commsManager)); - } catch (TskCoreException tskCoreException) { - Logger.getLogger(VisualizationPanel.class.getName()).log(Level.SEVERE, - "Could not get communications manager for current case", tskCoreException); + try { + CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); + + if (selectionCell.isVertex()) { + final AccountDeviceInstanceNode accountDeviceInstanceNode = + new AccountDeviceInstanceNode(((AccountDeviceInstanceKey) selectionCell.getValue()), + commsManager); + explorerManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); + + } else if (selectionCell.isEdge()) { + 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); } } }); } - + + private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { + + Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); + + if (edgesBetween.length == 0) { + final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, pinnedAccountVertex, relatedAccountVertex); + } else if (edgesBetween.length == 1) { + final mxCell edge = (mxCell) edgesBetween[0]; + edge.setValue(1d + (double) edge.getValue()); + edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); + } + } + private void addEdge(BlackboardArtifact artifact) throws TskCoreException { BlackboardArtifact.ARTIFACT_TYPE artfType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); if (null != artfType) { - + String from = null; String[] tos = new String[0]; @@ -136,12 +174,12 @@ public class VisualizationPanel extends JPanel { } for (String to : tos) { if (StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to)) { - + mxCell fromV = getOrCreateNodeDraft(from, 10); mxCell toV = getOrCreateNodeDraft(to, 10); - + Object[] edgesBetween = graph.getEdgesBetween(fromV, toV); - + if (edgesBetween.length == 0) { final String edgeName = from + "->" + to; mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, fromV, toV); @@ -154,29 +192,31 @@ public class VisualizationPanel extends JPanel { } } } - + @Subscribe public void pinAccount(PinAccountEvent pinEvent) { - + final AccountDeviceInstanceNode adiNode = pinEvent.getAccountDeviceInstanceNode(); final AccountDeviceInstanceKey adiKey = adiNode.getAccountDeviceInstanceKey(); - + graph.getModel().beginUpdate(); try { -// final String nodeId = /* -// * adiKey.getDataSourceName() + ":" + -// */ adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID(); - - mxCell v = getOrCreateNodeDraft(adiKey); + nodeMap.clear(); + graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); + + mxCell pinnedAccountVertex = getOrCreateNodeDraft(adiKey); CommunicationsManager commsManager = adiNode.getCommsManager(); - - Collection relationshipSources = - commsManager.getRelationshipSources(ImmutableSet.of(adiKey.getAccountDeviceInstance()), adiNode.getFilter()); - - for (Content source : relationshipSources) { - if (source instanceof BlackboardArtifact) { - addEdge((BlackboardArtifact) source); - } + final CommunicationsFilter commsFilter = adiNode.getFilter(); + List relatedAccountDeviceInstances = + commsManager.getRelatedAccountDeviceInstances(adiNode.getAccountDeviceInstance(), commsFilter); + + 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 = getOrCreateNodeDraft(relatedADIKey); + + addEdge(pinnedAccountVertex, relatedAccountVertex); } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); @@ -188,7 +228,7 @@ public class VisualizationPanel extends JPanel { new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); } - + private mxCell getOrCreateNodeDraft(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + @@ -203,23 +243,24 @@ public class VisualizationPanel extends JPanel { new Random().nextInt(200), size, size); + graph.getView().getState(nodeDraft, true).setLabel(name); nodeMap.put(name, nodeDraft); } return nodeDraft; } - + private mxCell getOrCreateNodeDraft(String name, long size) { mxCell nodeDraft = nodeMap.get(name); if (nodeDraft == null) { nodeDraft = (mxCell) graph.insertVertex( graph.getDefaultParent(), name, - null, + name, new Random().nextInt(200), new Random().nextInt(200), size, size); -// nodeDraft.setLabel(name); + graph.getView().getState(nodeDraft, true).setLabel(name); nodeMap.put(name, nodeDraft); } return nodeDraft; @@ -236,6 +277,7 @@ public class VisualizationPanel extends JPanel { jToolBar1 = new javax.swing.JToolBar(); jButton1 = new javax.swing.JButton(); + jButton2 = new javax.swing.JButton(); setLayout(new java.awt.BorderLayout()); @@ -245,14 +287,43 @@ public class VisualizationPanel extends JPanel { jButton1.setFocusable(false); jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); jToolBar1.add(jButton1); + jButton2.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N + jButton2.setFocusable(false); + jButton2.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton2.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton2.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton2ActionPerformed(evt); + } + }); + jToolBar1.add(jButton2); + add(jToolBar1, java.awt.BorderLayout.PAGE_START); }// //GEN-END:initComponents + private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed + graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); + + }//GEN-LAST:event_jButton2ActionPerformed + + private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed + new mxOrganicLayout(graph).execute(graph.getDefaultParent()); +// new mxCompactTreeLayout(graph).execute(graph.getDefaultParent()); + graphComponent.zoomAndCenter(); + }//GEN-LAST:event_jButton1ActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButton1; + private javax.swing.JButton jButton2; private javax.swing.JToolBar jToolBar1; // End of variables declaration//GEN-END:variables + } From 79437b9bacb8360891fb703d5acf0e8378f65a91 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 5 Jan 2018 12:46:09 +0100 Subject: [PATCH 003/128] WIP getting graph selection to populate MessageBrowser WIP --- .../communications/CVTTopComponent.java | 20 +++-- .../communications/MessageBrowser.java | 29 ++++--- .../communications/VisualizationPanel.java | 78 +++++++++++-------- 3 files changed, 77 insertions(+), 50 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index f19e998fb0..9e54b5d87f 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -38,15 +38,16 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; @RetainLocation("cvt") @NbBundle.Messages("CVTTopComponent.name= Communications Visualization") public final class CVTTopComponent extends TopComponent implements ExplorerManager.Provider { - + private static final long serialVersionUID = 1L; private final ExplorerManager messagesBrowserExplorerManager; private final ExplorerManager acctsBrowserExplorerManager; - + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public CVTTopComponent() { initComponents(); setName(Bundle.CVTTopComponent_name()); + /* * Associate an explorer manager with the GlobalActionContext (GAC) for * use by the messages browser so that selections in the messages @@ -64,11 +65,14 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag * Nodes from the accounts browser to the messages browser. */ acctsBrowserExplorerManager = new ExplorerManager(); - - vizPanel.initVisualization(acctsBrowserExplorerManager); + + vizPanel.initVisualization(acctsBrowserExplorerManager, messagesBrowserExplorerManager); + + CVTEvents.getCVTEventBus().register(this); + } - + @Subscribe public void pinAccount(PinAccountEvent pinEvent) { browseVisualizeTabPane.setSelectedIndex(1); @@ -135,12 +139,12 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag super.componentOpened(); WindowManager.getDefault().setTopComponentFloating(this, true); } - + @Override public ExplorerManager getExplorerManager() { return acctsBrowserExplorerManager; } - + @Override public void open() { super.open(); @@ -152,7 +156,7 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag */ filtersPane.updateAndApplyFilters(); } - + @Override public List availableModes(List modes) { /* diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index e58a1f027b..a97364de65 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -38,7 +38,7 @@ import org.sleuthkit.datamodel.CommunicationsManager; * other account details, and a ContentViewer to show individual */ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager.Provider { - + private static final long serialVersionUID = 1L; private final ExplorerManager gacExplorerManager; private final DataResultPanel messagesResultPanel; @@ -61,18 +61,27 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager splitPane.setTopComponent(messagesResultPanel); splitPane.setBottomComponent(messageDataContent); } - + @Override public void addNotify() { super.addNotify(); - ExplorerManager parentExplorerManager = ExplorerManager.find(this); - parentExplorerManager.addPropertyChangeListener(pce -> { + ExplorerManager parentEm = ExplorerManager.find(this); +// parentEm.addPropertyChangeListener(pce -> { +// gacExplorerManager.setRootContext(parentEm.getRootContext()); +// try { +// gacExplorerManager.setSelectedNodes(parentEm.getSelectedNodes()); +// } catch (PropertyVetoException ex) { +// Exceptions.printStackTrace(ex); +// } +// }); + + parentEm.addPropertyChangeListener(pce -> { if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { - final Node[] selectedNodes = parentExplorerManager.getSelectedNodes(); - + final Node[] selectedNodes = parentEm.getSelectedNodes(); + messagesResultPanel.setNumMatches(0); messagesResultPanel.setNode(null); - + if (selectedNodes.length == 0) { //reset panel when there is no selection messagesResultPanel.setPath(""); @@ -83,7 +92,7 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager CommunicationsFilter filter = adiNode.getFilter(); CommunicationsManager commsManager = adiNode.getCommsManager(); final Set accountDeviceInstances; - + if (selectedNodes.length == 1) { final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance(); accountDeviceInstances = Collections.singleton(accountDeviceInstance); @@ -98,7 +107,7 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager AccountDetailsNode accountDetailsNode = new AccountDetailsNode(accountDeviceInstances, filter, commsManager); TableFilterNode wrappedNode = - new TableFilterNode(new DataResultFilterNode(accountDetailsNode, parentExplorerManager), true); + new TableFilterNode(new DataResultFilterNode(accountDetailsNode, gacExplorerManager), true); messagesResultPanel.setNode(wrappedNode); } } @@ -112,7 +121,7 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager } messagesResultPanel.open(); } - + @Override public ExplorerManager getExplorerManager() { return gacExplorerManager; diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 1a860c0671..a2ec62f8ff 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -22,7 +22,6 @@ import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.model.mxCell; -import com.mxgraph.swing.handler.mxPanningHandler; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; import com.mxgraph.view.mxGraph; @@ -39,6 +38,8 @@ import java.util.logging.Level; import javax.swing.JPanel; import org.apache.commons.lang3.StringUtils; import org.openide.explorer.ExplorerManager; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.Case; @@ -59,14 +60,14 @@ import org.sleuthkit.datamodel.TskCoreException; * */ public class VisualizationPanel extends JPanel { - + static final private mxStylesheet mxStylesheet = new mxStylesheet(); private ExplorerManager explorerManager; private final mxGraph graph; - + private final Map nodeMap = new HashMap<>(); private final mxGraphComponent graphComponent; - + static { mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); @@ -97,29 +98,30 @@ public class VisualizationPanel extends JPanel { graphComponent.zoomTo(graphComponent.getZoomFactor() + e.getPreciseWheelRotation(), true); } }); - + CVTEvents.getCVTEventBus().register(this); - + graph.setStylesheet(mxStylesheet); - + } - - public void initVisualization(ExplorerManager em) { + + public void initVisualization(ExplorerManager em, ExplorerManager messageBrowserManager) { explorerManager = em; - + graph.getSelectionModel().addListener(null, (sender, evt) -> { Object[] selectionCells = graph.getSelectionCells(); if (selectionCells.length == 1) { mxCell selectionCell = (mxCell) selectionCells[0]; try { CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); - + if (selectionCell.isVertex()) { final AccountDeviceInstanceNode accountDeviceInstanceNode = new AccountDeviceInstanceNode(((AccountDeviceInstanceKey) selectionCell.getValue()), commsManager); - explorerManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); - + messageBrowserManager.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); + messageBrowserManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); + } else if (selectionCell.isEdge()) { System.out.println(selectionCell.getId()); // explorerManager.setRootContext(new CommunicationsBundleNode(adiKey, commsManager)); @@ -133,11 +135,11 @@ public class VisualizationPanel extends JPanel { } }); } - + private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { - + Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); - + if (edgesBetween.length == 0) { final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, pinnedAccountVertex, relatedAccountVertex); @@ -147,11 +149,11 @@ public class VisualizationPanel extends JPanel { edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); } } - + private void addEdge(BlackboardArtifact artifact) throws TskCoreException { BlackboardArtifact.ARTIFACT_TYPE artfType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); if (null != artfType) { - + String from = null; String[] tos = new String[0]; @@ -174,12 +176,12 @@ public class VisualizationPanel extends JPanel { } for (String to : tos) { if (StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to)) { - + mxCell fromV = getOrCreateNodeDraft(from, 10); mxCell toV = getOrCreateNodeDraft(to, 10); - + Object[] edgesBetween = graph.getEdgesBetween(fromV, toV); - + if (edgesBetween.length == 0) { final String edgeName = from + "->" + to; mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, fromV, toV); @@ -192,30 +194,30 @@ public class VisualizationPanel extends JPanel { } } } - + @Subscribe public void pinAccount(PinAccountEvent pinEvent) { - + final AccountDeviceInstanceNode adiNode = pinEvent.getAccountDeviceInstanceNode(); final AccountDeviceInstanceKey adiKey = adiNode.getAccountDeviceInstanceKey(); - + graph.getModel().beginUpdate(); try { nodeMap.clear(); graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); - + mxCell pinnedAccountVertex = getOrCreateNodeDraft(adiKey); CommunicationsManager commsManager = adiNode.getCommsManager(); final CommunicationsFilter commsFilter = adiNode.getFilter(); List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiNode.getAccountDeviceInstance(), commsFilter); - + 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 = getOrCreateNodeDraft(relatedADIKey); - + addEdge(pinnedAccountVertex, relatedAccountVertex); } } catch (TskCoreException ex) { @@ -225,10 +227,10 @@ public class VisualizationPanel extends JPanel { graph.getModel().endUpdate(); revalidate(); } - + new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); } - + private mxCell getOrCreateNodeDraft(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + @@ -248,7 +250,7 @@ public class VisualizationPanel extends JPanel { } return nodeDraft; } - + private mxCell getOrCreateNodeDraft(String name, long size) { mxCell nodeDraft = nodeMap.get(name); if (nodeDraft == null) { @@ -309,8 +311,8 @@ public class VisualizationPanel extends JPanel { }// //GEN-END:initComponents private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed - graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); - +// graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); +graphComponent.getPanningHandler().setEnabled(true); }//GEN-LAST:event_jButton2ActionPerformed private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed @@ -326,4 +328,16 @@ public class VisualizationPanel extends JPanel { private javax.swing.JToolBar jToolBar1; // End of variables declaration//GEN-END:variables + static class SimpleParentNode extends AbstractNode { + + static SimpleParentNode createFromChildNodes(Node... nodes) { + Children.Array array = new Children.Array(); + array.add(nodes); + return new SimpleParentNode(array); + } + + public SimpleParentNode(Children children) { + super(children); + } + } } From 3e49de2c1b9d85f85ef4593813a602a9ac52b42c Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 5 Jan 2018 16:00:15 +0100 Subject: [PATCH 004/128] WIP 2 getting graph selection to populate MessageBrowser WIP 2 --- .../communications/AccountsBrowser.java | 19 +++-- .../autopsy/communications/Bundle.properties | 2 + .../communications/CVTTopComponent.form | 84 ++++++++++--------- .../communications/CVTTopComponent.java | 68 ++++++++++----- .../communications/VisualizationPanel.java | 63 +++++++------- 5 files changed, 137 insertions(+), 99 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index 8e418d65fb..c90572bff8 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -31,7 +31,7 @@ import org.openide.explorer.ExplorerManager; * A panel that goes in the Browse tab of the Communications Visualization Tool. * Hosts an OutlineView that shows information about Accounts. */ -public class AccountsBrowser extends JPanel { +public class AccountsBrowser extends JPanel implements ExplorerManager.Provider { private static final long serialVersionUID = 1L; @@ -56,10 +56,8 @@ public class AccountsBrowser extends JPanel { outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded } - @Override - public void addNotify() { - super.addNotify(); - em = ExplorerManager.find(this); + void init(ExplorerManager acctsBrowserExplorerManager) { + em = acctsBrowserExplorerManager; em.addPropertyChangeListener(evt -> { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { SwingUtilities.invokeLater(this::setColumnWidths); @@ -67,8 +65,8 @@ public class AccountsBrowser extends JPanel { SwingUtilities.invokeLater(this::setColumnWidths); } }); - } - + + } private void setColumnWidths() { int margin = 4; int padding = 8; @@ -126,4 +124,11 @@ public class AccountsBrowser extends JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables private org.openide.explorer.view.OutlineView outlineView; // End of variables declaration//GEN-END:variables + + @Override + public ExplorerManager getExplorerManager() { + return em; + } + + } diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 6f78ddf19b..d4740c7644 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -18,3 +18,5 @@ FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh VisualizationPanel.jButton1.text=redo layout CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize VisualizationPanel.jButton2.text=pan +CVTTopComponent.browseSplitPane.TabConstraints.tabTitle=Browse +CVTTopComponent.vizSplitPane.TabConstraints.tabTitle=Visualize diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form index a24037b927..5a2d718964 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form @@ -19,9 +19,9 @@ - - - + + + @@ -29,63 +29,71 @@ - - - - + + + + +
- + - - + + + - + - - - - - - + - - + + + + + + + + + - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 9e54b5d87f..d395f9d1ab 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -21,9 +21,12 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.Subscribe; import java.util.List; import java.util.stream.Collectors; +import javax.swing.JSplitPane; import org.openide.explorer.ExplorerManager; -import org.openide.explorer.ExplorerUtils; +import static org.openide.explorer.ExplorerUtils.createLookup; +import org.openide.util.Lookup; import org.openide.util.NbBundle; +import org.openide.util.lookup.ProxyLookup; import org.openide.windows.Mode; import org.openide.windows.RetainLocation; import org.openide.windows.TopComponent; @@ -40,23 +43,25 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; public final class CVTTopComponent extends TopComponent implements ExplorerManager.Provider { private static final long serialVersionUID = 1L; - private final ExplorerManager messagesBrowserExplorerManager; private final ExplorerManager acctsBrowserExplorerManager; @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public CVTTopComponent() { initComponents(); + setName(Bundle.CVTTopComponent_name()); /* * Associate an explorer manager with the GlobalActionContext (GAC) for - * use by the messages browser so that selections in the messages + * use by the messages browsers so that selections in the messages * browser can be exposed to context-sensitive actions. */ - messagesBrowserExplorerManager = new ExplorerManager(); - associateLookup(ExplorerUtils.createLookup(messagesBrowserExplorerManager, getActionMap())); - splitPane.setRightComponent(new MessageBrowser(messagesBrowserExplorerManager)); - + ExplorerManager browserExplorerManager = new ExplorerManager(); + ExplorerManager vizExplorerManager = new ExplorerManager(); + ProxyLookupImpl proxyLookup = new ProxyLookupImpl(); + associateLookup(proxyLookup); + 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 @@ -65,9 +70,14 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag * Nodes from the accounts browser to the messages browser. */ acctsBrowserExplorerManager = new ExplorerManager(); + accountsBrowser.init(acctsBrowserExplorerManager); + vizPanel.initVisualization(acctsBrowserExplorerManager); - vizPanel.initVisualization(acctsBrowserExplorerManager, messagesBrowserExplorerManager); - + browseVisualizeTabPane.addChangeListener(changeEvent -> { + JSplitPane selectedComponent = (JSplitPane) browseVisualizeTabPane.getSelectedComponent(); + ExplorerManager.Provider leftComponent = (ExplorerManager.Provider) selectedComponent.getLeftComponent(); + proxyLookup.changeLookup(createLookup(leftComponent.getExplorerManager(), getActionMap())); + }); CVTEvents.getCVTEventBus().register(this); @@ -86,20 +96,22 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag // //GEN-BEGIN:initComponents private void initComponents() { - splitPane = new javax.swing.JSplitPane(); browseVisualizeTabPane = new javax.swing.JTabbedPane(); + browseSplitPane = new javax.swing.JSplitPane(); accountsBrowser = new org.sleuthkit.autopsy.communications.AccountsBrowser(); + vizSplitPane = new javax.swing.JSplitPane(); vizPanel = new org.sleuthkit.autopsy.communications.VisualizationPanel(); filtersPane = new org.sleuthkit.autopsy.communications.FiltersPanel(); - splitPane.setDividerLocation(400); - splitPane.setResizeWeight(0.7); - browseVisualizeTabPane.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N - browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N - browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N - splitPane.setLeftComponent(browseVisualizeTabPane); + browseSplitPane.setLeftComponent(accountsBrowser); + + browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseSplitPane.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), browseSplitPane); // NOI18N + + vizSplitPane.setLeftComponent(vizPanel); + + browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizSplitPane.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizSplitPane); // NOI18N filtersPane.setMinimumSize(new java.awt.Dimension(256, 495)); @@ -111,27 +123,29 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag .addGap(6, 6, 6) .addComponent(filtersPane, javax.swing.GroupLayout.PREFERRED_SIZE, 265, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(splitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1324, Short.MAX_VALUE) - .addGap(0, 0, 0)) + .addComponent(browseVisualizeTabPane, javax.swing.GroupLayout.DEFAULT_SIZE, 786, Short.MAX_VALUE) + .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(6, 6, 6) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(filtersPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(splitPane)) + .addComponent(filtersPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGap(5, 5, 5)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(browseVisualizeTabPane) + .addContainerGap()) ); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private org.sleuthkit.autopsy.communications.AccountsBrowser accountsBrowser; + private javax.swing.JSplitPane browseSplitPane; private javax.swing.JTabbedPane browseVisualizeTabPane; private org.sleuthkit.autopsy.communications.FiltersPanel filtersPane; - private javax.swing.JSplitPane splitPane; private org.sleuthkit.autopsy.communications.VisualizationPanel vizPanel; + private javax.swing.JSplitPane vizSplitPane; // End of variables declaration//GEN-END:variables @Override @@ -167,4 +181,14 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag return modes.stream().filter(mode -> mode.getName().equals("cvt")) .collect(Collectors.toList()); } + + private static class ProxyLookupImpl extends ProxyLookup { + + public ProxyLookupImpl() { + } + + void changeLookup(Lookup l){ + setLookups(l); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index a2ec62f8ff..15e71dc88f 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -27,8 +27,6 @@ import com.mxgraph.util.mxConstants; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxStylesheet; import java.awt.Color; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; import java.beans.PropertyVetoException; import java.util.HashMap; import java.util.List; @@ -59,7 +57,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * */ -public class VisualizationPanel extends JPanel { +public class VisualizationPanel extends JPanel implements ExplorerManager.Provider { static final private mxStylesheet mxStylesheet = new mxStylesheet(); private ExplorerManager explorerManager; @@ -92,12 +90,6 @@ public class VisualizationPanel extends JPanel { graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); this.add(graphComponent); - graphComponent.addMouseWheelListener(new MouseWheelListener() { - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - graphComponent.zoomTo(graphComponent.getZoomFactor() + e.getPreciseWheelRotation(), true); - } - }); CVTEvents.getCVTEventBus().register(this); @@ -105,7 +97,7 @@ public class VisualizationPanel extends JPanel { } - public void initVisualization(ExplorerManager em, ExplorerManager messageBrowserManager) { + public void initVisualization(ExplorerManager em) { explorerManager = em; graph.getSelectionModel().addListener(null, (sender, evt) -> { @@ -119,8 +111,8 @@ public class VisualizationPanel extends JPanel { final AccountDeviceInstanceNode accountDeviceInstanceNode = new AccountDeviceInstanceNode(((AccountDeviceInstanceKey) selectionCell.getValue()), commsManager); - messageBrowserManager.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); - messageBrowserManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); + explorerManager.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); + explorerManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); } else if (selectionCell.isEdge()) { System.out.println(selectionCell.getId()); @@ -136,6 +128,11 @@ public class VisualizationPanel extends JPanel { }); } + @Override + public ExplorerManager getExplorerManager() { + return explorerManager; + } + private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); @@ -150,6 +147,7 @@ public class VisualizationPanel extends JPanel { } } + @Deprecated private void addEdge(BlackboardArtifact artifact) throws TskCoreException { BlackboardArtifact.ARTIFACT_TYPE artfType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); if (null != artfType) { @@ -177,8 +175,8 @@ public class VisualizationPanel extends JPanel { for (String to : tos) { if (StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to)) { - mxCell fromV = getOrCreateNodeDraft(from, 10); - mxCell toV = getOrCreateNodeDraft(to, 10); + mxCell fromV = getOrCreateVertex(from, 10); + mxCell toV = getOrCreateVertex(to, 10); Object[] edgesBetween = graph.getEdgesBetween(fromV, toV); @@ -206,7 +204,7 @@ public class VisualizationPanel extends JPanel { nodeMap.clear(); graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); - mxCell pinnedAccountVertex = getOrCreateNodeDraft(adiKey); + mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); CommunicationsManager commsManager = adiNode.getCommsManager(); final CommunicationsFilter commsFilter = adiNode.getFilter(); List relatedAccountDeviceInstances = @@ -216,7 +214,7 @@ public class VisualizationPanel extends JPanel { long communicationsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter); String dataSourceName = AccountsRootChildren.getDataSourceName(relatedADI); AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount, dataSourceName); - mxCell relatedAccountVertex = getOrCreateNodeDraft(relatedADIKey); + mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); addEdge(pinnedAccountVertex, relatedAccountVertex); } @@ -231,30 +229,31 @@ public class VisualizationPanel extends JPanel { new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); } - private mxCell getOrCreateNodeDraft(AccountDeviceInstanceKey accountDeviceInstanceKey) { + private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); - mxCell nodeDraft = nodeMap.get(name); - if (nodeDraft == null) { - double size = accountDeviceInstanceKey.getMessageCount() / 10; - nodeDraft = (mxCell) graph.insertVertex( + mxCell vertex = nodeMap.get(name); + if (vertex == null) { + double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; + vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), name, accountDeviceInstanceKey, new Random().nextInt(200), new Random().nextInt(200), size, size); - graph.getView().getState(nodeDraft, true).setLabel(name); - nodeMap.put(name, nodeDraft); + graph.getView().getState(vertex, true).setLabel(name); + nodeMap.put(name, vertex); } - return nodeDraft; + return vertex; } - private mxCell getOrCreateNodeDraft(String name, long size) { - mxCell nodeDraft = nodeMap.get(name); - if (nodeDraft == null) { - nodeDraft = (mxCell) graph.insertVertex( + @Deprecated + private mxCell getOrCreateVertex(String name, long size) { + mxCell vertex = nodeMap.get(name); + if (vertex == null) { + vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), name, name, @@ -262,10 +261,10 @@ public class VisualizationPanel extends JPanel { new Random().nextInt(200), size, size); - graph.getView().getState(nodeDraft, true).setLabel(name); - nodeMap.put(name, nodeDraft); + graph.getView().getState(vertex, true).setLabel(name); + nodeMap.put(name, vertex); } - return nodeDraft; + return vertex; } /** @@ -312,7 +311,7 @@ public class VisualizationPanel extends JPanel { private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed // graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); -graphComponent.getPanningHandler().setEnabled(true); + graphComponent.getPanningHandler().setEnabled(true); }//GEN-LAST:event_jButton2ActionPerformed private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed From 461fc11c130cabe83b3d03a639d973698c2041d8 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Sat, 6 Jan 2018 13:39:13 +0100 Subject: [PATCH 005/128] WIP 3 getting graph selection to populate MessageBrowser WIP 3 --- .../communications/AccountsBrowser.form | 40 ++++----- .../communications/AccountsBrowser.java | 48 +++++------ .../autopsy/communications/Bundle.properties | 5 +- .../communications/CVTTopComponent.form | 39 +++------ .../communications/CVTTopComponent.java | 72 +++++++--------- .../autopsy/communications/FiltersPanel.java | 18 ++-- .../communications/MessageBrowser.java | 30 +++---- .../communications/VisualizationPanel.form | 86 ++++++++++++------- .../communications/VisualizationPanel.java | 76 +++++++++------- 9 files changed, 209 insertions(+), 205 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form index 83a3e75e9a..96ba675284 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form @@ -11,30 +11,28 @@ + - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index c90572bff8..25275d5e36 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -31,12 +31,13 @@ import org.openide.explorer.ExplorerManager; * A panel that goes in the Browse tab of the Communications Visualization Tool. * Hosts an OutlineView that shows information about Accounts. */ -public class AccountsBrowser extends JPanel implements ExplorerManager.Provider { +public class AccountsBrowser extends JPanel implements ExplorerManager.Provider, CVTTopComponent.ProxiedExplorerManagerProvider { private static final long serialVersionUID = 1L; private final Outline outline; - private ExplorerManager em; + private final ExplorerManager gacEM = new ExplorerManager(); + private ExplorerManager tableEM; /** * Creates new form AccountsBrowser @@ -54,19 +55,22 @@ public class AccountsBrowser extends JPanel implements ExplorerManager.Provider ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.AccountNode_accountName()); outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded + } - void init(ExplorerManager acctsBrowserExplorerManager) { - em = acctsBrowserExplorerManager; - em.addPropertyChangeListener(evt -> { + void init(ExplorerManager tableExplorerManager) { + this.tableEM = tableExplorerManager; + tableExplorerManager.addPropertyChangeListener(evt -> { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { SwingUtilities.invokeLater(this::setColumnWidths); } else if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) { SwingUtilities.invokeLater(this::setColumnWidths); } }); - - } + + jSplitPane1.setRightComponent(new MessageBrowser(tableExplorerManager, gacEM)); + } + private void setColumnWidths() { int margin = 4; int padding = 8; @@ -100,35 +104,29 @@ public class AccountsBrowser extends JPanel implements ExplorerManager.Provider // //GEN-BEGIN:initComponents private void initComponents() { + jSplitPane1 = new javax.swing.JSplitPane(); outlineView = new org.openide.explorer.view.OutlineView(); - 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(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) - .addGap(0, 0, 0)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(0, 0, 0) - .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) - .addGap(0, 0, 0)) - ); + setLayout(new java.awt.BorderLayout()); + + jSplitPane1.setLeftComponent(outlineView); + + add(jSplitPane1, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JSplitPane jSplitPane1; private org.openide.explorer.view.OutlineView outlineView; // End of variables declaration//GEN-END:variables @Override public ExplorerManager getExplorerManager() { - return em; + return tableEM; } - + @Override + public ExplorerManager getProxiedExplorerManager() { + return gacEM; + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index d4740c7644..d0fa082fd9 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -18,5 +18,6 @@ FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh VisualizationPanel.jButton1.text=redo layout CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize VisualizationPanel.jButton2.text=pan -CVTTopComponent.browseSplitPane.TabConstraints.tabTitle=Browse -CVTTopComponent.vizSplitPane.TabConstraints.tabTitle=Visualize +CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse +CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualize +CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form index 5a2d718964..9315465965 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form @@ -46,15 +46,20 @@ + + + + + - + - + @@ -62,24 +67,13 @@ - - - - - - - - - - - - - + + - + @@ -87,18 +81,7 @@ - - - - - - - - - - - - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index d395f9d1ab..fd6019c0d0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.Subscribe; import java.util.List; import java.util.stream.Collectors; -import javax.swing.JSplitPane; import org.openide.explorer.ExplorerManager; import static org.openide.explorer.ExplorerUtils.createLookup; import org.openide.util.Lookup; @@ -40,15 +39,16 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; @TopComponent.Registration(mode = "cvt", openAtStartup = false) @RetainLocation("cvt") @NbBundle.Messages("CVTTopComponent.name= Communications Visualization") -public final class CVTTopComponent extends TopComponent implements ExplorerManager.Provider { +public final class CVTTopComponent extends TopComponent { private static final long serialVersionUID = 1L; - private final ExplorerManager acctsBrowserExplorerManager; + + private final ExplorerManager filterToTableEXplorerManager = new ExplorerManager(); @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public CVTTopComponent() { initComponents(); - + filtersPane.setExplorerManager(filterToTableEXplorerManager); setName(Bundle.CVTTopComponent_name()); /* @@ -56,27 +56,25 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag * use by the messages browsers so that selections in the messages * browser can be exposed to context-sensitive actions. */ - ExplorerManager browserExplorerManager = new ExplorerManager(); - ExplorerManager vizExplorerManager = new ExplorerManager(); +// ExplorerManager browserExplorerManager = new ExplorerManager(); +// ExplorerManager vizExplorerManager = new ExplorerManager(); ProxyLookupImpl proxyLookup = new ProxyLookupImpl(); associateLookup(proxyLookup); - 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(); - accountsBrowser.init(acctsBrowserExplorerManager); - vizPanel.initVisualization(acctsBrowserExplorerManager); + 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(); browseVisualizeTabPane.addChangeListener(changeEvent -> { - JSplitPane selectedComponent = (JSplitPane) browseVisualizeTabPane.getSelectedComponent(); - ExplorerManager.Provider leftComponent = (ExplorerManager.Provider) selectedComponent.getLeftComponent(); - proxyLookup.changeLookup(createLookup(leftComponent.getExplorerManager(), getActionMap())); + ProxiedExplorerManagerProvider selectedComponent = (ProxiedExplorerManagerProvider) browseVisualizeTabPane.getSelectedComponent(); + proxyLookup.changeLookup(createLookup(selectedComponent.getProxiedExplorerManager(), getActionMap())); }); CVTEvents.getCVTEventBus().register(this); @@ -97,21 +95,13 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag private void initComponents() { browseVisualizeTabPane = new javax.swing.JTabbedPane(); - browseSplitPane = new javax.swing.JSplitPane(); accountsBrowser = new org.sleuthkit.autopsy.communications.AccountsBrowser(); - vizSplitPane = new javax.swing.JSplitPane(); vizPanel = new org.sleuthkit.autopsy.communications.VisualizationPanel(); filtersPane = new org.sleuthkit.autopsy.communications.FiltersPanel(); browseVisualizeTabPane.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N - - browseSplitPane.setLeftComponent(accountsBrowser); - - browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseSplitPane.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), browseSplitPane); // NOI18N - - vizSplitPane.setLeftComponent(vizPanel); - - browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizSplitPane.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizSplitPane); // NOI18N + browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N + browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle_1"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N filtersPane.setMinimumSize(new java.awt.Dimension(256, 495)); @@ -136,16 +126,16 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag .addComponent(browseVisualizeTabPane) .addContainerGap()) ); + + browseVisualizeTabPane.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName")); // NOI18N }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private org.sleuthkit.autopsy.communications.AccountsBrowser accountsBrowser; - private javax.swing.JSplitPane browseSplitPane; private javax.swing.JTabbedPane browseVisualizeTabPane; private org.sleuthkit.autopsy.communications.FiltersPanel filtersPane; private org.sleuthkit.autopsy.communications.VisualizationPanel vizPanel; - private javax.swing.JSplitPane vizSplitPane; // End of variables declaration//GEN-END:variables @Override @@ -154,11 +144,6 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag WindowManager.getDefault().setTopComponentFloating(this, true); } - @Override - public ExplorerManager getExplorerManager() { - return acctsBrowserExplorerManager; - } - @Override public void open() { super.open(); @@ -184,11 +169,16 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag private static class ProxyLookupImpl extends ProxyLookup { - public ProxyLookupImpl() { + ProxyLookupImpl() { } - - void changeLookup(Lookup l){ + + void changeLookup(Lookup l) { setLookups(l); } } + + static interface ProxiedExplorerManagerProvider { + + ExplorerManager getProxiedExplorerManager(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 84db631a86..9fc83ca1b5 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -30,6 +30,7 @@ import java.util.Map.Entry; import java.util.logging.Level; import java.util.stream.Collectors; import javax.swing.JCheckBox; +import javax.swing.JPanel; import org.openide.explorer.ExplorerManager; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; @@ -59,7 +60,7 @@ import org.sleuthkit.datamodel.TskCoreException; * Panel that holds the Filter control widgets and translates user filtering * changes into queries against the CommunicationsManager. */ -final public class FiltersPanel extends javax.swing.JPanel { +final public class FiltersPanel extends JPanel { private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName()); private static final long serialVersionUID = 1L; @@ -119,9 +120,9 @@ final public class FiltersPanel extends javax.swing.JPanel { if (eventType.equals(DATA_ADDED.toString())) { // Indicate that a refresh may be needed, unless the data added is Keyword or Hashset hits ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue(); - if (null != eventData && - eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() && - eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { + if (null != eventData + && eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + && eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { updateFilters(); needsRefresh = true; validateFilters(); @@ -173,11 +174,6 @@ final public class FiltersPanel extends javax.swing.JPanel { @Override public void addNotify() { super.addNotify(); - /* - * Since we get the exploreremanager from the parent JComponenet, wait - * till this FiltersPanel is actaully added to a parent. - */ - em = ExplorerManager.find(this); IngestManager.getInstance().addIngestModuleEventListener(ingestListener); Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { devicesMap.clear(); @@ -625,4 +621,8 @@ final public class FiltersPanel extends javax.swing.JPanel { private final javax.swing.JButton unCheckAllAccountTypesButton = new javax.swing.JButton(); private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton(); // End of variables declaration//GEN-END:variables + + void setExplorerManager(ExplorerManager explorerManager) { + em = explorerManager; + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index a97364de65..6cf7ebb823 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; 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; @@ -37,9 +38,10 @@ import org.sleuthkit.datamodel.CommunicationsManager; * The right hand side of the CVT. Has a DataResultPanel to show messages and * other account details, and a ContentViewer to show individual */ -final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager.Provider { - +public final class MessageBrowser extends JPanel { + private static final long serialVersionUID = 1L; + private final ExplorerManager tableEM; private final ExplorerManager gacExplorerManager; private final DataResultPanel messagesResultPanel; private DataResultViewerTable dataResultViewerTable; @@ -53,7 +55,8 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager * in the messages browser can be exposed to * context-sensitive actions. */ - MessageBrowser(ExplorerManager gacExplorerManager) { + public MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { + this.tableEM = tableEM; this.gacExplorerManager = gacExplorerManager; initComponents(); //create an uninitialized DataResultPanel so we can control the ResultViewers that get added. @@ -61,11 +64,11 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager splitPane.setTopComponent(messagesResultPanel); splitPane.setBottomComponent(messageDataContent); } - + @Override public void addNotify() { super.addNotify(); - ExplorerManager parentEm = ExplorerManager.find(this); +// ExplorerManager parentEm = ExplorerManager.find(this); // parentEm.addPropertyChangeListener(pce -> { // gacExplorerManager.setRootContext(parentEm.getRootContext()); // try { @@ -74,14 +77,14 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager // Exceptions.printStackTrace(ex); // } // }); - - parentEm.addPropertyChangeListener(pce -> { + + tableEM.addPropertyChangeListener(pce -> { if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { - final Node[] selectedNodes = parentEm.getSelectedNodes(); - + final Node[] selectedNodes = tableEM.getSelectedNodes(); + messagesResultPanel.setNumMatches(0); messagesResultPanel.setNode(null); - + if (selectedNodes.length == 0) { //reset panel when there is no selection messagesResultPanel.setPath(""); @@ -92,7 +95,7 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager CommunicationsFilter filter = adiNode.getFilter(); CommunicationsManager commsManager = adiNode.getCommsManager(); final Set accountDeviceInstances; - + if (selectedNodes.length == 1) { final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance(); accountDeviceInstances = Collections.singleton(accountDeviceInstance); @@ -121,11 +124,6 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager } messagesResultPanel.open(); } - - @Override - public ExplorerManager getExplorerManager() { - return gacExplorerManager; - } /** * This method is called from within the constructor to initialize the form. diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 3c45fd7568..3262c06010 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -16,44 +16,66 @@ - - - - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 15e71dc88f..14fa7bd5d3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,6 +26,7 @@ import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxStylesheet; +import java.awt.BorderLayout; import java.awt.Color; import java.beans.PropertyVetoException; import java.util.HashMap; @@ -42,6 +43,7 @@ import org.openide.nodes.Node; import org.openide.util.Exceptions; 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.communications.RelationshipNode.getAttributeDisplayString; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -57,23 +59,25 @@ import org.sleuthkit.datamodel.TskCoreException; /** * */ -public class VisualizationPanel extends JPanel implements ExplorerManager.Provider { +public class VisualizationPanel extends JPanel implements ExplorerManager.Provider, ProxiedExplorerManagerProvider { + + private static final long serialVersionUID = 1L; static final private mxStylesheet mxStylesheet = new mxStylesheet(); - private ExplorerManager explorerManager; - private final mxGraph graph; - private final Map nodeMap = new HashMap<>(); + private ExplorerManager vizEM = new ExplorerManager(); + + private final ExplorerManager gacEM = new ExplorerManager(); private final mxGraphComponent graphComponent; + private final mxGraph graph; + private final Map nodeMap = new HashMap<>(); static { + //initialize defaul cell properties mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); } - /** - * Creates new form VizPanel - */ public VisualizationPanel() { initComponents(); graph = new mxGraph(); @@ -89,17 +93,12 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); - this.add(graphComponent); - + jPanel1.add(graphComponent, BorderLayout.CENTER); + splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); CVTEvents.getCVTEventBus().register(this); graph.setStylesheet(mxStylesheet); - } - - public void initVisualization(ExplorerManager em) { - explorerManager = em; - graph.getSelectionModel().addListener(null, (sender, evt) -> { Object[] selectionCells = graph.getSelectionCells(); if (selectionCells.length == 1) { @@ -111,8 +110,8 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid final AccountDeviceInstanceNode accountDeviceInstanceNode = new AccountDeviceInstanceNode(((AccountDeviceInstanceKey) selectionCell.getValue()), commsManager); - explorerManager.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); - explorerManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); + vizEM.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); + vizEM.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); } else if (selectionCell.isEdge()) { System.out.println(selectionCell.getId()); @@ -130,7 +129,12 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid @Override public ExplorerManager getExplorerManager() { - return explorerManager; + return vizEM; + } + + @Override + public ExplorerManager getProxiedExplorerManager() { + return gacEM; } private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { @@ -276,24 +280,17 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid // //GEN-BEGIN:initComponents private void initComponents() { + splitPane = new javax.swing.JSplitPane(); + jPanel1 = new javax.swing.JPanel(); jToolBar1 = new javax.swing.JToolBar(); - jButton1 = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); + jButton1 = new javax.swing.JButton(); setLayout(new java.awt.BorderLayout()); - jToolBar1.setRollover(true); + jPanel1.setLayout(new java.awt.BorderLayout()); - jButton1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N - jButton1.setFocusable(false); - jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton1.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton1ActionPerformed(evt); - } - }); - jToolBar1.add(jButton1); + jToolBar1.setRollover(true); jButton2.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N jButton2.setFocusable(false); @@ -306,7 +303,22 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid }); jToolBar1.add(jButton2); - add(jToolBar1, java.awt.BorderLayout.PAGE_START); + jButton1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N + jButton1.setFocusable(false); + jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); + jToolBar1.add(jButton1); + + jPanel1.add(jToolBar1, java.awt.BorderLayout.PAGE_START); + + splitPane.setLeftComponent(jPanel1); + + add(splitPane, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed @@ -324,7 +336,9 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; + private javax.swing.JPanel jPanel1; private javax.swing.JToolBar jToolBar1; + private javax.swing.JSplitPane splitPane; // End of variables declaration//GEN-END:variables static class SimpleParentNode extends AbstractNode { From d961ea6062652daeaada7cd1104c792c30e5a304 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Sat, 6 Jan 2018 14:27:26 +0100 Subject: [PATCH 006/128] finish wiring ExplorerManagers for seperate populaiton of MessageBrowsers by browse and visualize modes --- .../communications/AccountsRootChildren.java | 27 ------------------- .../communications/CVTTopComponent.java | 2 +- .../communications/MessageBrowser.java | 15 ++++------- .../communications/VisualizationPanel.java | 12 +++------ 4 files changed, 9 insertions(+), 47 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java index b0d59bd183..e4e888158b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java @@ -22,7 +22,6 @@ import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; @@ -154,32 +153,6 @@ class AccountsRootChildren extends ChildFactory { return s; } - @Override - public int hashCode() { - int hash = 3; - hash = 73 * hash + Objects.hashCode(this.accountDeviceInstanceKey); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final AccountDeviceInstanceNode other = (AccountDeviceInstanceNode) obj; - if (!Objects.equals(this.accountDeviceInstanceKey, other.accountDeviceInstanceKey)) { - return false; - } - - return true; - } - @Override public Action[] getActions(boolean context) { ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index fd6019c0d0..3c796d6222 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -73,7 +73,7 @@ public final class CVTTopComponent extends TopComponent { // acctsBrowserExplorerManager = new ExplorerManager(); browseVisualizeTabPane.addChangeListener(changeEvent -> { - ProxiedExplorerManagerProvider selectedComponent = (ProxiedExplorerManagerProvider) browseVisualizeTabPane.getSelectedComponent(); + ProxiedExplorerManagerProvider selectedComponent = (ProxiedExplorerManagerProvider) browseVisualizeTabPane.getSelectedComponent(); proxyLookup.changeLookup(createLookup(selectedComponent.getProxiedExplorerManager(), getActionMap())); }); diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index 6cf7ebb823..7e7a5d2f2c 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -38,7 +38,7 @@ import org.sleuthkit.datamodel.CommunicationsManager; * The right hand side of the CVT. Has a DataResultPanel to show messages and * other account details, and a ContentViewer to show individual */ -public final class MessageBrowser extends JPanel { +public final class MessageBrowser extends JPanel implements ExplorerManager.Provider { private static final long serialVersionUID = 1L; private final ExplorerManager tableEM; @@ -68,15 +68,6 @@ public final class MessageBrowser extends JPanel { @Override public void addNotify() { super.addNotify(); -// ExplorerManager parentEm = ExplorerManager.find(this); -// parentEm.addPropertyChangeListener(pce -> { -// gacExplorerManager.setRootContext(parentEm.getRootContext()); -// try { -// gacExplorerManager.setSelectedNodes(parentEm.getSelectedNodes()); -// } catch (PropertyVetoException ex) { -// Exceptions.printStackTrace(ex); -// } -// }); tableEM.addPropertyChangeListener(pce -> { if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { @@ -166,4 +157,8 @@ public final class MessageBrowser extends JPanel { private javax.swing.JSplitPane splitPane; // End of variables declaration//GEN-END:variables + @Override + public ExplorerManager getExplorerManager() { + return gacExplorerManager; + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 14fa7bd5d3..557e762a2e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -59,14 +59,13 @@ import org.sleuthkit.datamodel.TskCoreException; /** * */ -public class VisualizationPanel extends JPanel implements ExplorerManager.Provider, ProxiedExplorerManagerProvider { +public class VisualizationPanel extends JPanel implements ProxiedExplorerManagerProvider { private static final long serialVersionUID = 1L; static final private mxStylesheet mxStylesheet = new mxStylesheet(); - private ExplorerManager vizEM = new ExplorerManager(); - + private final ExplorerManager vizEM = new ExplorerManager(); private final ExplorerManager gacEM = new ExplorerManager(); private final mxGraphComponent graphComponent; private final mxGraph graph; @@ -93,7 +92,7 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); - jPanel1.add(graphComponent, BorderLayout.CENTER); + jPanel1.add(graphComponent, BorderLayout.CENTER); splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); CVTEvents.getCVTEventBus().register(this); @@ -127,11 +126,6 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid }); } - @Override - public ExplorerManager getExplorerManager() { - return vizEM; - } - @Override public ExplorerManager getProxiedExplorerManager() { return gacEM; From 0630f46897f6408432cb37c9ff8ca1e053a40bc0 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 8 Jan 2018 15:16:06 +0100 Subject: [PATCH 007/128] rewire explorermanagers and views to support multiple selection on account table. --- .../AccountDeviceInstanceKey.java | 40 +++- .../AccountDeviceInstanceNode.java | 132 +++++++++++++ .../AccountDeviceInstanceNodeFactory.java | 71 +++++++ .../communications/AccountsBrowser.form | 2 +- .../communications/AccountsBrowser.java | 40 ++-- .../communications/AccountsRootChildren.java | 178 ------------------ .../autopsy/communications/CVTEvents.java | 27 ++- .../communications/CVTTopComponent.java | 49 ++--- .../communications/FilterProvider.java | 26 +++ .../autopsy/communications/FiltersPanel.java | 52 +++-- .../communications/MessageBrowser.java | 27 +-- .../communications/PinAccountEvent.java | 13 +- .../communications/VisualizationPanel.java | 117 +++++++++--- 13 files changed, 471 insertions(+), 303 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java index 343db541e2..5b3ab52fd4 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit 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(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java new file mode 100644 index 0000000000..aa34a65e8e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -0,0 +1,132 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2017-18 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.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 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 lookupAll = + Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class); + CVTEvents.getCVTEventBus().post(new PinAccountEvent(lookupAll)); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java new file mode 100644 index 0000000000..bb63d65270 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java @@ -0,0 +1,71 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2017-18 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.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 { + + 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 list) { + List accountDeviceInstanceKeys = new ArrayList<>(); + try { + final List 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); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form index 96ba675284..8642d90664 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form @@ -11,7 +11,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index 25275d5e36..55fffa9c2b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit 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; } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java deleted file mode 100644 index e4e888158b..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2017 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.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 { - - 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 list) { - List 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 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)); - } - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java index d4c76ce2d8..a8e75fffe9 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -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 sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.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; } - + } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 3c796d6222..55d1ccb0fd 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit 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(); - } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java b/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java new file mode 100644 index 0000000000..54f48f9dd1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java @@ -0,0 +1,26 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.communications; + +import org.sleuthkit.datamodel.CommunicationsFilter; + +interface FilterProvider { + + CommunicationsFilter getFilter(); +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 9fc83ca1b5..24e589290c 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit 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 { }// //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; - } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index 7e7a5d2f2c..eb81c23ce5 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit 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; - } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java index 181e160041..e77ab3f35d 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java @@ -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 accountDeviceInstances; - public AccountDeviceInstanceNode getAccountDeviceInstanceNode() { - return accountDeviceInstance; + public ImmutableSet getAccountDeviceInstances() { + return accountDeviceInstances; } - PinAccountEvent(AccountDeviceInstanceNode accountDeviceInstance) { - this.accountDeviceInstance = accountDeviceInstance; + PinAccountEvent(Collection accountDeviceInstances) { + this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 557e762a2e..d22359e0b8 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -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 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 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 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 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 From 685881462e9eaa26be7c7ef2563b5b5c28f79949 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 8 Jan 2018 15:23:34 +0100 Subject: [PATCH 008/128] cleanup --- .../autopsy/communications/PinAccountAction.java | 7 ------- .../communications/VisualizationPanel.java | 15 +++++++++------ 2 files changed, 9 insertions(+), 13 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java deleted file mode 100644 index f204507299..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java +++ /dev/null @@ -1,7 +0,0 @@ -/* - * 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. - */ -package org.sleuthkit.autopsy.communications; - diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index d22359e0b8..f75eb5a7ec 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.Subscribe; -import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.model.mxCell; import com.mxgraph.swing.mxGraphComponent; @@ -239,10 +238,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } finally { // Updates the display graph.getModel().endUpdate(); - revalidate(); + } - new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); + applyOrganicLayout(); + revalidate(); } private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { @@ -382,11 +382,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton2ActionPerformed private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - new mxOrganicLayout(graph).execute(graph.getDefaultParent()); -// new mxCompactTreeLayout(graph).execute(graph.getDefaultParent()); - graphComponent.zoomAndCenter(); + applyOrganicLayout(); }//GEN-LAST:event_jButton1ActionPerformed + private void applyOrganicLayout() { + new mxOrganicLayout(graph).execute(graph.getDefaultParent()); + graphComponent.zoomAndCenter(); + } + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButton1; From 15d031d669dec2103d17b6931c1eb218bf96b743 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 9 Jan 2018 12:27:07 +0100 Subject: [PATCH 009/128] fix Codacy issues --- .../AccountDeviceInstanceKey.java | 5 +- .../AccountDeviceInstanceNodeFactory.java | 2 +- .../communications/AccountsBrowser.java | 4 +- .../communications/CVTTopComponent.java | 4 +- .../communications/MessageBrowser.java | 11 +- .../OpenCommVisualizationToolAction.java | 6 +- .../communications/RelationshipNode.java | 4 +- .../communications/VisualizationPanel.java | 101 +++--------------- 8 files changed, 32 insertions(+), 105 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java index 5b3ab52fd4..0e89e3a1b6 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java @@ -105,10 +105,7 @@ final class AccountDeviceInstanceKey { if (!Objects.equals(this.dataSourceName, other.dataSourceName)) { return false; } - if (!Objects.equals(this.accountDeviceInstance, other.accountDeviceInstance)) { - return false; - } - return true; + return Objects.equals(this.accountDeviceInstance, other.accountDeviceInstance); } private static String getDataSourceName(AccountDeviceInstance accountDeviceInstance, SleuthkitCase db) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java index bb63d65270..5fe069b523 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java @@ -55,7 +55,7 @@ final class AccountDeviceInstanceNodeFactory extends ChildFactory { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 55d1ccb0fd..d19ad25496 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -160,13 +160,13 @@ public final class CVTTopComponent extends TopComponent { .collect(Collectors.toList()); } - private static class ProxyLookupImpl extends ProxyLookup { + final private static class ProxyLookupImpl extends ProxyLookup { ProxyLookupImpl(Lookup... l) { super(l); } - void changeLookups(Lookup... l) { + protected void changeLookups(Lookup... l) { setLookups(l); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index eb81c23ce5..8fe4873f54 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -25,6 +25,7 @@ import java.util.stream.Stream; import javax.swing.JPanel; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; @@ -43,7 +44,6 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov private final ExplorerManager tableEM; private final ExplorerManager gacExplorerManager; private final DataResultPanel messagesResultPanel; - private DataResultViewerTable dataResultViewerTable; /** * Constructs the right hand side of the Communications Visualization Tool @@ -56,6 +56,7 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov * in the messages browser can be exposed to * context-sensitive actions. */ + @NbBundle.Messages({"MessageBrowser.DataResultViewerTable.title=Messages"}) public MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { this.tableEM = tableEM; this.gacExplorerManager = gacExplorerManager; @@ -64,13 +65,13 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov messagesResultPanel = DataResultPanel.createInstanceUninitialized("Account", "", Node.EMPTY, 0, messageDataContent); splitPane.setTopComponent(messagesResultPanel); splitPane.setBottomComponent(messageDataContent); - dataResultViewerTable = new DataResultViewerTable(gacExplorerManager, "Messages"); - messagesResultPanel.addResultViewer(dataResultViewerTable); + messagesResultPanel.addResultViewer(new DataResultViewerTable(gacExplorerManager, + Bundle.MessageBrowser_DataResultViewerTable_title())); messagesResultPanel.open(); - tableEM.addPropertyChangeListener(pce -> { + this.tableEM.addPropertyChangeListener(pce -> { if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { - final Node[] selectedNodes = tableEM.getSelectedNodes(); + final Node[] selectedNodes = this.tableEM.getSelectedNodes(); messagesResultPanel.setNumMatches(0); messagesResultPanel.setNode(null); diff --git a/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java b/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java index a977ff2c09..e3dc552bab 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java +++ b/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -48,10 +48,10 @@ import org.sleuthkit.autopsy.core.RuntimeProperties; , @ActionReference(path = "Toolbars/Case", position = 102)}) @Messages("CTL_OpenCVTAction=Communications") -public final class OpenCommVisualizationToolAction extends CallableSystemAction { +public final class OpenCommVisualizationToolAction extends CallableSystemAction { private static final long serialVersionUID = 1L; - PropertyChangeListener pcl; + private final PropertyChangeListener pcl; private final JButton toolbarButton = new JButton(getName(), new ImageIcon(getClass().getResource("images/emblem-web24.png"))); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index 70d70b5325..31f5871b81 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -127,7 +127,7 @@ public class RelationshipNode extends BlackboardArtifactNode { * @return The display string, or an empty string if there is no such * attribute or an an error. */ - static String getAttributeDisplayString(final BlackboardArtifact artifact, final ATTRIBUTE_TYPE attributeType) { + 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) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index f75eb5a7ec..320e7d493b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -36,7 +36,6 @@ 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; @@ -47,14 +46,8 @@ import org.openide.util.Lookup; import org.openide.util.lookup.ProxyLookup; import org.sleuthkit.autopsy.casemodule.Case; 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; -import org.sleuthkit.datamodel.BlackboardArtifact; -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 org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.TskCoreException; @@ -93,7 +86,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private CommunicationsManager commsManager; - void setFilterProvider(FilterProvider filterProvider) { + protected void setFilterProvider(FilterProvider filterProvider) { this.filterProvider = filterProvider; } private FilterProvider filterProvider; @@ -148,66 +141,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider return proxyLookup; } - private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { - - Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); - - if (edgesBetween.length == 0) { - final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); - mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, pinnedAccountVertex, relatedAccountVertex); - } else if (edgesBetween.length == 1) { - final mxCell edge = (mxCell) edgesBetween[0]; - edge.setValue(1d + (double) edge.getValue()); - edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); - } - } - - @Deprecated - private void addEdge(BlackboardArtifact artifact) throws TskCoreException { - BlackboardArtifact.ARTIFACT_TYPE artfType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); - if (null != artfType) { - - String from = null; - String[] tos = new String[0]; - - //Consider refactoring this to reduce boilerplate - switch (artfType) { - case TSK_EMAIL_MSG: - from = StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;"); - tos = StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;").split(";"); - break; - case TSK_MESSAGE: - from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM); - tos = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO).split(";"); - break; - case TSK_CALLLOG: - from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM); - tos = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO).split(";"); - break; - default: - break; - } - for (String to : tos) { - if (StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to)) { - - mxCell fromV = getOrCreateVertex(from, 10); - mxCell toV = getOrCreateVertex(to, 10); - - Object[] edgesBetween = graph.getEdgesBetween(fromV, toV); - - if (edgesBetween.length == 0) { - final String edgeName = from + "->" + to; - mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, fromV, toV); - } else if (edgesBetween.length == 1) { - final mxCell edge = (mxCell) edgesBetween[0]; - edge.setValue(1d + (double) edge.getValue()); - edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); - } - } - } - } - } - @Subscribe public void pinAccounts(PinAccountEvent pinEvent) { @@ -265,22 +198,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider return vertex; } - @Deprecated - private mxCell getOrCreateVertex(String name, long size) { - mxCell vertex = nodeMap.get(name); - if (vertex == null) { - vertex = (mxCell) graph.insertVertex( - graph.getDefaultParent(), - name, - name, - new Random().nextInt(200), - new Random().nextInt(200), - size, - size); - graph.getView().getState(vertex, true).setLabel(name); - nodeMap.put(name, vertex); + private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { + + Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); + + if (edgesBetween.length == 0) { + final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); + graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, pinnedAccountVertex, relatedAccountVertex); + } else if (edgesBetween.length == 1) { + final mxCell edge = (mxCell) edgesBetween[0]; + edge.setValue(1d + (double) edge.getValue()); + edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); } - return vertex; } @Override @@ -399,15 +328,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private javax.swing.JSplitPane splitPane; // End of variables declaration//GEN-END:variables - static class SimpleParentNode extends AbstractNode { + private static class SimpleParentNode extends AbstractNode { - static SimpleParentNode createFromChildNodes(Node... nodes) { + private static SimpleParentNode createFromChildNodes(Node... nodes) { Children.Array array = new Children.Array(); array.add(nodes); return new SimpleParentNode(array); } - public SimpleParentNode(Children children) { + private SimpleParentNode(Children children) { super(children); } } From d3ff7f78e5df15ead33c97f172dfe5315049e2fd Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 9 Jan 2018 12:04:09 +0100 Subject: [PATCH 010/128] WIP link selection WIP --- .../RelaionshipSetNodeFactory.java | 35 +++++++ .../communications/VisualizationPanel.java | 98 ++++++++++--------- 2 files changed, 87 insertions(+), 46 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/RelaionshipSetNodeFactory.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelaionshipSetNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/RelaionshipSetNodeFactory.java new file mode 100644 index 0000000000..b147ea9537 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/RelaionshipSetNodeFactory.java @@ -0,0 +1,35 @@ +/* + * 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. + */ +package org.sleuthkit.autopsy.communications; + +import java.util.Collection; +import java.util.List; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Node; +import org.sleuthkit.datamodel.BlackboardArtifact; + +/** + * + */ +public class RelaionshipSetNodeFactory extends ChildFactory { + + private final Collection artifacts; + + public RelaionshipSetNodeFactory(Collection artifacts) { + this.artifacts = artifacts; + } + + @Override + protected boolean createKeys(List list) { + list.addAll(artifacts); + return true; + } + + @Override + protected Node createNodeForKey(BlackboardArtifact key) { + return new RelationshipNode(key); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 320e7d493b..78b2d23e71 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.communications; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.model.mxCell; @@ -28,8 +30,11 @@ import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; import java.beans.PropertyVetoException; +import java.util.Arrays; +import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; @@ -83,6 +88,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final mxGraphComponent graphComponent; private final mxGraph graph; private final Map nodeMap = new HashMap<>(); + Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); private CommunicationsManager commsManager; @@ -126,8 +132,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider vizEM.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); } else if (selectionCell.isEdge()) { - System.out.println(selectionCell.getId()); -// explorerManager.setRootContext(new CommunicationsBundleNode(adiKey, commsManager)); + vizEM.setRootContext(new AbstractNode(Children.create(new RelaionshipSetNodeFactory((Set) selectionCell.getValue()), true))); } } catch (PropertyVetoException ex) { logger.log(Level.SEVERE, "Account selection vetoed.", ex); @@ -141,43 +146,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider return proxyLookup; } - @Subscribe - public void pinAccounts(PinAccountEvent pinEvent) { - - final Set adiKeys = pinEvent.getAccountDeviceInstances(); - final CommunicationsFilter commsFilter = filterProvider.getFilter(); - - graph.getModel().beginUpdate(); - try { - nodeMap.clear(); - graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); - - for (AccountDeviceInstanceKey adiKey : adiKeys) { - mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); - - List relatedAccountDeviceInstances = - commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), commsFilter); - - 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); - } finally { - // Updates the display - graph.getModel().endUpdate(); - - } - - applyOrganicLayout(); - revalidate(); - } - private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + @@ -197,19 +165,57 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } return vertex; } - - private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { - + @SuppressWarnings("unchecked") + private void addEdge(BlackboardArtifact artifact, mxCell pinnedAccountVertex, mxCell relatedAccountVertex) throws TskCoreException { Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); - if (edgesBetween.length == 0) { final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); - graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, pinnedAccountVertex, relatedAccountVertex); + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(artifact)), pinnedAccountVertex, relatedAccountVertex); + edgeMap.put(artifact, edge); } else if (edgesBetween.length == 1) { final mxCell edge = (mxCell) edgesBetween[0]; - edge.setValue(1d + (double) edge.getValue()); - edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); + ((Collection) edge.getValue()).add(artifact); + edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); + @Subscribe + public void pinAccounts(PinAccountEvent pinEvent) { + + final Set adiKeys = pinEvent.getAccountDeviceInstances(); + final CommunicationsFilter commsFilter = filterProvider.getFilter(); + + graph.getModel().beginUpdate(); + try { + nodeMap.clear(); + graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); + + for (AccountDeviceInstanceKey adiKey : adiKeys) { + mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); + + List relatedAccountDeviceInstances = + commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), commsFilter); + + for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { + + List relationships = commsManager.getRelationships(adiKey.getAccountDeviceInstance(), relatedADI, commsFilter); + + long communicationsCount = relationships.size(); + AccountDeviceInstanceKey relatedADIKey = + new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount); + mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); + for (BlackboardArtifact relationship : relationships) { + addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); + } + } + } + } catch (TskCoreException ex) { + Exceptions.printStackTrace(ex); + } finally { + // Updates the display + graph.getModel().endUpdate(); + } + + applyOrganicLayout(); + revalidate(); } @Override From 55569a189fa924fe0ce9ff4a6e227cf6815bc119 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 9 Jan 2018 16:11:39 +0100 Subject: [PATCH 011/128] working edge selection --- .../autopsy/communications/Bundle.properties | 5 +- .../communications/MessageBrowser.java | 2 + .../communications/VisualizationPanel.form | 108 ++++++++++++- .../communications/VisualizationPanel.java | 144 ++++++++++++++++-- 4 files changed, 240 insertions(+), 19 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index d0fa082fd9..abdbd65f69 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -15,9 +15,12 @@ FiltersPanel.refreshButton.text=Refresh FiltersPanel.deviceRequiredLabel.text=Select at least one. FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. -VisualizationPanel.jButton1.text=redo layout +VisualizationPanel.jButton1.text=Organic CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize VisualizationPanel.jButton2.text=pan CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualize CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize +VisualizationPanel.jButton3.text=Orthogonal +VisualizationPanel.jButton4.text=- +VisualizationPanel.jButton5.text=+ diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index 8fe4873f54..2f76267edf 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -103,6 +103,8 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov TableFilterNode wrappedNode = new TableFilterNode(new DataResultFilterNode(accountDetailsNode, gacExplorerManager), true); messagesResultPanel.setNode(wrappedNode); + }else{ + messagesResultPanel.setNode(selectedNode); } } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 3262c06010..1084d8f2da 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -17,6 +17,10 @@ + + + + @@ -34,17 +38,63 @@ - - - - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -72,6 +122,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 78b2d23e71..f07eaa32f1 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -22,6 +22,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.mxOrganicLayout; +import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; @@ -53,12 +54,13 @@ import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; 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.TskCoreException; /** - * * A panel that goes in the Visualize tab of the Communications Visualization + * 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. * @@ -76,6 +78,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider static { //initialize defaul cell (Vertex and/or Edge) properties mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "#000000"); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); } @@ -88,11 +91,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final mxGraphComponent graphComponent; private final mxGraph graph; private final Map nodeMap = new HashMap<>(); - Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); + private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); private CommunicationsManager commsManager; - protected void setFilterProvider(FilterProvider filterProvider) { + void setFilterProvider(FilterProvider filterProvider) { this.filterProvider = filterProvider; } private FilterProvider filterProvider; @@ -100,6 +103,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public VisualizationPanel() { initComponents(); graph = new mxGraph(); + graph.setCellsCloneable(false); + graph.setDropEnabled(false); + graph.setCellsCloneable(false); graph.setCellsEditable(false); graph.setCellsResizable(false); graph.setCellsMovable(true); @@ -108,11 +114,19 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setDisconnectOnMove(false); graph.setEdgeLabelsMovable(false); graph.setVertexLabelsMovable(false); + graph.setAllowDanglingEdges(false); + graph.setCellsDeletable(false); + graph.setCellsBendable(true); + graph.setKeepEdgesInBackground(true); + graphComponent = new mxGraphComponent(graph); graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); + + graphComponent.getGraphControl().addMouseMotionListener(graphComponent.getPanningHandler()); + splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); CVTEvents.getCVTEventBus().register(this); @@ -132,7 +146,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider vizEM.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); } else if (selectionCell.isEdge()) { - vizEM.setRootContext(new AbstractNode(Children.create(new RelaionshipSetNodeFactory((Set) selectionCell.getValue()), true))); + @SuppressWarnings("unchecked") + AbstractNode abstractNode = new AbstractNode(Children.create(new RelaionshipSetNodeFactory((Set) selectionCell.getValue()), true)); + vizEM.setRootContext(abstractNode); + vizEM.setSelectedNodes(new Node[]{abstractNode}); } } catch (PropertyVetoException ex) { logger.log(Level.SEVERE, "Account selection vetoed.", ex); @@ -165,6 +182,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } return vertex; } + @SuppressWarnings("unchecked") private void addEdge(BlackboardArtifact artifact, mxCell pinnedAccountVertex, mxCell relatedAccountVertex) throws TskCoreException { Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); @@ -176,6 +194,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final mxCell edge = (mxCell) edgesBetween[0]; ((Collection) edge.getValue()).add(artifact); edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); + } + } + @Subscribe public void pinAccounts(PinAccountEvent pinEvent) { @@ -185,6 +206,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().beginUpdate(); try { nodeMap.clear(); + edgeMap.clear(); graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); for (AccountDeviceInstanceKey adiKey : adiKeys) { @@ -236,6 +258,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().beginUpdate(); try { nodeMap.clear(); + edgeMap.clear(); graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); } finally { graph.getModel().endUpdate(); @@ -255,7 +278,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Override - public void removeNotify() { super.removeNotify(); // IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); @@ -272,15 +294,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider splitPane = new javax.swing.JSplitPane(); jPanel1 = new javax.swing.JPanel(); - jToolBar1 = new javax.swing.JToolBar(); + jPanel2 = new javax.swing.JPanel(); jButton2 = new javax.swing.JButton(); jButton1 = new javax.swing.JButton(); + jButton3 = new javax.swing.JButton(); + jSeparator1 = new javax.swing.JToolBar.Separator(); + jButton4 = new javax.swing.JButton(); + jButton5 = new javax.swing.JButton(); setLayout(new java.awt.BorderLayout()); - jPanel1.setLayout(new java.awt.BorderLayout()); + splitPane.setDividerLocation(400); + splitPane.setResizeWeight(0.5); - jToolBar1.setRollover(true); + jPanel1.setLayout(new java.awt.BorderLayout()); jButton2.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N jButton2.setFocusable(false); @@ -291,7 +318,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jButton2ActionPerformed(evt); } }); - jToolBar1.add(jButton2); jButton1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N jButton1.setFocusable(false); @@ -302,9 +328,82 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jButton1ActionPerformed(evt); } }); - jToolBar1.add(jButton1); - jPanel1.add(jToolBar1, java.awt.BorderLayout.PAGE_START); + jButton3.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton3.text")); // NOI18N + jButton3.setFocusable(false); + jButton3.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton3.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton3.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton3ActionPerformed(evt); + } + }); + + jSeparator1.setPreferredSize(new java.awt.Dimension(10, 10)); + + jButton4.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton4.text")); // NOI18N + jButton4.setFocusable(false); + jButton4.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton4.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton4.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton4ActionPerformed(evt); + } + }); + + jButton5.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton5.text")); // NOI18N + jButton5.setFocusable(false); + jButton5.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton5.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton5.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton5ActionPerformed(evt); + } + }); + + javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); + jPanel2.setLayout(jPanel2Layout); + jPanel2Layout.setHorizontalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addContainerGap() + .addComponent(jButton2) + .addGap(5, 5, 5) + .addComponent(jButton1) + .addGap(5, 5, 5) + .addComponent(jButton3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 1, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jButton4) + .addGap(5, 5, 5) + .addComponent(jButton5) + .addContainerGap()) + ); + jPanel2Layout.setVerticalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(jButton2)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(jButton1)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(jButton3)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(14, 14, 14) + .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(10, 10, 10)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(jButton4)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(jButton5)) + ); + + jPanel1.add(jPanel2, java.awt.BorderLayout.NORTH); splitPane.setLeftComponent(jPanel1); @@ -320,17 +419,38 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider applyOrganicLayout(); }//GEN-LAST:event_jButton1ActionPerformed + private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed + applyOrthogonalLayout(); + }//GEN-LAST:event_jButton3ActionPerformed + + private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed + graphComponent.zoomIn(); + }//GEN-LAST:event_jButton5ActionPerformed + + private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed + graphComponent.zoomOut(); + }//GEN-LAST:event_jButton4ActionPerformed + private void applyOrganicLayout() { new mxOrganicLayout(graph).execute(graph.getDefaultParent()); graphComponent.zoomAndCenter(); } + private void applyOrthogonalLayout() { + new mxOrthogonalLayout(graph).execute(graph.getDefaultParent()); + graphComponent.zoomAndCenter(); + } + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; + private javax.swing.JButton jButton3; + private javax.swing.JButton jButton4; + private javax.swing.JButton jButton5; private javax.swing.JPanel jPanel1; - private javax.swing.JToolBar jToolBar1; + private javax.swing.JPanel jPanel2; + private javax.swing.JToolBar.Separator jSeparator1; private javax.swing.JSplitPane splitPane; // End of variables declaration//GEN-END:variables From 7fd1562427f79b8413b0975806478470c4be730a Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 10 Jan 2018 15:28:24 +0100 Subject: [PATCH 012/128] experimenting with actions. got panning to work --- .../autopsy/communications/Bundle.properties | 1 + .../communications/RelationshipNode.java | 1 - .../communications/VisualizationPanel.form | 82 ++----- .../communications/VisualizationPanel.java | 215 ++++++++++-------- 4 files changed, 138 insertions(+), 161 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index abdbd65f69..71d89548f8 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -24,3 +24,4 @@ CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize VisualizationPanel.jButton3.text=Orthogonal VisualizationPanel.jButton4.text=- VisualizationPanel.jButton5.text=+ +VisualizationPanel.jButton6.text=Hierarchy diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index 31f5871b81..0025455a51 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -143,5 +143,4 @@ public class RelationshipNode extends BlackboardArtifactNode { return ""; } } - } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 1084d8f2da..8a2130752f 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -4,14 +4,14 @@ - + - + - + @@ -38,63 +38,17 @@ - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -109,6 +63,19 @@ + + + + + + + + + + + + + @@ -135,13 +102,6 @@ - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index f07eaa32f1..8f39aecc37 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; import com.google.common.eventbus.Subscribe; +import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; @@ -30,6 +31,9 @@ import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; import java.beans.PropertyVetoException; import java.util.Arrays; import java.util.Collection; @@ -41,7 +45,11 @@ import java.util.Map; import java.util.Random; import java.util.Set; import java.util.logging.Level; +import javax.swing.JButton; import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.JToolBar; +import javax.swing.SwingConstants; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.AbstractNode; @@ -49,6 +57,7 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.Exceptions; import org.openide.util.Lookup; +import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; @@ -119,13 +128,37 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); - graphComponent = new mxGraphComponent(graph); + graphComponent = new mxGraphComponent(graph){ + @Override + public boolean isPanningEvent(MouseEvent event) { + return true; //To change body of generated methods, choose Tools | Templates. + } + + }; graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); - graphComponent.getGraphControl().addMouseMotionListener(graphComponent.getPanningHandler()); +// graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { +// @Override +// public void mouseReleased(MouseEvent e) { +// graphComponent.setPanning(false); +// } +// +// @Override +// public void mouseMoved(MouseEvent e) { +// super.mouseMoved(e); +// if (graphComponent.isPanning()) { +// graphComponent.getGraphControl().setTranslate(e.getPoint()); +// } +// } +// +// @Override +// public void mousePressed(MouseEvent e) { +// graphComponent.setPanning(true); +// } +// }); splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); CVTEvents.getCVTEventBus().register(this); @@ -292,145 +325,124 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // //GEN-BEGIN:initComponents private void initComponents() { - splitPane = new javax.swing.JSplitPane(); - jPanel1 = new javax.swing.JPanel(); - jPanel2 = new javax.swing.JPanel(); - jButton2 = new javax.swing.JButton(); - jButton1 = new javax.swing.JButton(); - jButton3 = new javax.swing.JButton(); - jSeparator1 = new javax.swing.JToolBar.Separator(); - jButton4 = new javax.swing.JButton(); - jButton5 = new javax.swing.JButton(); + splitPane = new JSplitPane(); + jPanel1 = new JPanel(); + jToolBar1 = new JToolBar(); + jButton2 = new JButton(); + jButton6 = new JButton(); + jButton1 = new JButton(); + jButton3 = new JButton(); + jButton4 = new JButton(); + jButton5 = new JButton(); - setLayout(new java.awt.BorderLayout()); + setLayout(new BorderLayout()); splitPane.setDividerLocation(400); splitPane.setResizeWeight(0.5); - jPanel1.setLayout(new java.awt.BorderLayout()); + jPanel1.setLayout(new BorderLayout()); - jButton2.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N + jToolBar1.setRollover(true); + + jButton2.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N jButton2.setFocusable(false); - jButton2.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton2.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton2.setHorizontalTextPosition(SwingConstants.CENTER); + jButton2.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { jButton2ActionPerformed(evt); } }); + jToolBar1.add(jButton2); - jButton1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N + jButton6.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton6.text")); // NOI18N + jButton6.setFocusable(false); + jButton6.setHorizontalTextPosition(SwingConstants.CENTER); + jButton6.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton6.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jButton6ActionPerformed(evt); + } + }); + jToolBar1.add(jButton6); + + jButton1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N jButton1.setFocusable(false); - jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton1.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1.setHorizontalTextPosition(SwingConstants.CENTER); + jButton1.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { jButton1ActionPerformed(evt); } }); + jToolBar1.add(jButton1); - jButton3.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton3.text")); // NOI18N + jButton3.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton3.text")); // NOI18N jButton3.setFocusable(false); - jButton3.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton3.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton3.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton3.setHorizontalTextPosition(SwingConstants.CENTER); + jButton3.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton3.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { jButton3ActionPerformed(evt); } }); + jToolBar1.add(jButton3); - jSeparator1.setPreferredSize(new java.awt.Dimension(10, 10)); - - jButton4.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton4.text")); // NOI18N + jButton4.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton4.text")); // NOI18N jButton4.setFocusable(false); - jButton4.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton4.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton4.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton4.setHorizontalTextPosition(SwingConstants.CENTER); + jButton4.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton4.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { jButton4ActionPerformed(evt); } }); + jToolBar1.add(jButton4); - jButton5.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton5.text")); // NOI18N + jButton5.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton5.text")); // NOI18N jButton5.setFocusable(false); - jButton5.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton5.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton5.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton5.setHorizontalTextPosition(SwingConstants.CENTER); + jButton5.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton5.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { jButton5ActionPerformed(evt); } }); + jToolBar1.add(jButton5); - javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); - jPanel2.setLayout(jPanel2Layout); - jPanel2Layout.setHorizontalGroup( - jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel2Layout.createSequentialGroup() - .addContainerGap() - .addComponent(jButton2) - .addGap(5, 5, 5) - .addComponent(jButton1) - .addGap(5, 5, 5) - .addComponent(jButton3) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 1, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton4) - .addGap(5, 5, 5) - .addComponent(jButton5) - .addContainerGap()) - ); - jPanel2Layout.setVerticalGroup( - jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jButton2)) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jButton1)) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jButton3)) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(14, 14, 14) - .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGap(10, 10, 10)) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jButton4)) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jButton5)) - ); - - jPanel1.add(jPanel2, java.awt.BorderLayout.NORTH); + jPanel1.add(jToolBar1, BorderLayout.NORTH); splitPane.setLeftComponent(jPanel1); - add(splitPane, java.awt.BorderLayout.CENTER); + add(splitPane, BorderLayout.CENTER); }// //GEN-END:initComponents - private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed + private void jButton2ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed // graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); - graphComponent.getPanningHandler().setEnabled(true); + graphComponent.setPanning(true); }//GEN-LAST:event_jButton2ActionPerformed - private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed + private void jButton1ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed applyOrganicLayout(); }//GEN-LAST:event_jButton1ActionPerformed - private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed + private void jButton3ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed + applyOrthogonalLayout(); }//GEN-LAST:event_jButton3ActionPerformed - private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed + private void jButton5ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed graphComponent.zoomIn(); }//GEN-LAST:event_jButton5ActionPerformed - private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed + private void jButton4ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed graphComponent.zoomOut(); }//GEN-LAST:event_jButton4ActionPerformed + private void jButton6ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton6ActionPerformed + applyHierarchicalLayout(); + }//GEN-LAST:event_jButton6ActionPerformed + private void applyOrganicLayout() { new mxOrganicLayout(graph).execute(graph.getDefaultParent()); graphComponent.zoomAndCenter(); @@ -441,17 +453,22 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.zoomAndCenter(); } + private void applyHierarchicalLayout() { + new mxHierarchicalLayout(graph).execute(graph.getDefaultParent()); + graphComponent.zoomAndCenter(); + } + // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton jButton1; - private javax.swing.JButton jButton2; - private javax.swing.JButton jButton3; - private javax.swing.JButton jButton4; - private javax.swing.JButton jButton5; - private javax.swing.JPanel jPanel1; - private javax.swing.JPanel jPanel2; - private javax.swing.JToolBar.Separator jSeparator1; - private javax.swing.JSplitPane splitPane; + private JButton jButton1; + private JButton jButton2; + private JButton jButton3; + private JButton jButton4; + private JButton jButton5; + private JButton jButton6; + private JPanel jPanel1; + private JToolBar jToolBar1; + private JSplitPane splitPane; // End of variables declaration//GEN-END:variables private static class SimpleParentNode extends AbstractNode { From c13e518d6a512abbba5c009a1a870e1d1fad42ab Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 11 Jan 2018 11:46:31 +0100 Subject: [PATCH 013/128] reduce public surface of the API --- .../autopsy/communications/AccountDetailsNode.java | 2 +- .../communications/AccountDeviceInstanceNode.java | 10 +++++----- .../autopsy/communications/AccountsBrowser.java | 2 +- .../sleuthkit/autopsy/communications/CVTEvents.java | 7 +++++-- .../autopsy/communications/MessageBrowser.java | 2 +- .../autopsy/communications/MessageDataContent.java | 2 +- .../autopsy/communications/PinAccountEvent.java | 4 ++-- .../autopsy/communications/RelationshipNode.java | 4 ++-- .../autopsy/communications/VisualizationPanel.java | 11 +++++++++-- 9 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java index 88470e87e0..7bc1a0faae 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java @@ -38,7 +38,7 @@ import org.sleuthkit.datamodel.TskCoreException; * relationships of all the accounts in this node. * */ -class AccountDetailsNode extends AbstractNode { +final class AccountDetailsNode extends AbstractNode { private final static Logger logger = Logger.getLogger(AccountDetailsNode.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index aa34a65e8e..5b88b01999 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -54,23 +54,23 @@ final class AccountDeviceInstanceNode extends AbstractNode { setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType())); } - public AccountDeviceInstance getAccountDeviceInstance() { + AccountDeviceInstance getAccountDeviceInstance() { return accountDeviceInstanceKey.getAccountDeviceInstance(); } - public AccountDeviceInstanceKey getAccountDeviceInstanceKey() { + AccountDeviceInstanceKey getAccountDeviceInstanceKey() { return accountDeviceInstanceKey; } - public CommunicationsManager getCommsManager() { + CommunicationsManager getCommsManager() { return commsManager; } - public long getMessageCount() { + long getMessageCount() { return accountDeviceInstanceKey.getMessageCount(); } - public CommunicationsFilter getFilter() { + CommunicationsFilter getFilter() { return accountDeviceInstanceKey.getCommunicationsFilter(); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index 8d6407fe02..828d654298 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -70,7 +70,7 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro } - protected void init(ExplorerManager tableExplorerManager) { + void init(ExplorerManager tableExplorerManager) { this.accountsTableEM = tableExplorerManager; tableExplorerManager.addPropertyChangeListener(evt -> { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java index a8e75fffe9..663b711c99 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -21,14 +21,17 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.EventBus; /** - * Provide he singleton EventBus. + * Provide the singleton EventBus. */ final class CVTEvents { private final static EventBus cvtEventBus = new EventBus(); - public static EventBus getCVTEventBus() { + static EventBus getCVTEventBus() { return cvtEventBus; } + private CVTEvents() { + } + } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index 8fe4873f54..7d0146715b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -57,7 +57,7 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov * context-sensitive actions. */ @NbBundle.Messages({"MessageBrowser.DataResultViewerTable.title=Messages"}) - public MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { + MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { this.tableEM = tableEM; this.gacExplorerManager = gacExplorerManager; initComponents(); diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java b/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java index 1c4ea0f7b4..ac54faa6ad 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java @@ -26,7 +26,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; * Extends MessageContentViewer so that it implements DataContent and can be set * as the only ContentViewer for a DataResultPanel */ -public class MessageDataContent extends MessageContentViewer implements DataContent { +final class MessageDataContent extends MessageContentViewer implements DataContent { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java index e77ab3f35d..c8ba063adb 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java @@ -11,11 +11,11 @@ import java.util.Collection; /** * */ -class PinAccountEvent { +final class PinAccountEvent { private final ImmutableSet accountDeviceInstances; - public ImmutableSet getAccountDeviceInstances() { + ImmutableSet getAccountDeviceInstances() { return accountDeviceInstances; } diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index 31f5871b81..fff7dfa9ad 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -43,11 +43,11 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Node for a relationship, as represented by a BlackboardArtifact. */ -public class RelationshipNode extends BlackboardArtifactNode { +final class RelationshipNode extends BlackboardArtifactNode { private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName()); - public RelationshipNode(BlackboardArtifact artifact) { + RelationshipNode(BlackboardArtifact artifact) { super(artifact); final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message"); diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 320e7d493b..678b6eabac 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -24,6 +24,7 @@ import com.mxgraph.model.mxCell; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; @@ -86,7 +87,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private CommunicationsManager commsManager; - protected void setFilterProvider(FilterProvider filterProvider) { + void setFilterProvider(FilterProvider filterProvider) { this.filterProvider = filterProvider; } private FilterProvider filterProvider; @@ -102,8 +103,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setDisconnectOnMove(false); graph.setEdgeLabelsMovable(false); graph.setVertexLabelsMovable(false); + graph.setAutoOrigin(true); graphComponent = new mxGraphComponent(graph); graphComponent.setAutoScroll(true); + graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); @@ -249,7 +252,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Override - public void removeNotify() { super.removeNotify(); // IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); @@ -316,6 +318,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void applyOrganicLayout() { new mxOrganicLayout(graph).execute(graph.getDefaultParent()); + + mxGraphView view = graphComponent.getGraph().getView(); + int compLen = graphComponent.getWidth(); + int viewLen = (int) view.getGraphBounds().getWidth(); + view.setScale((double) compLen / viewLen * view.getScale()); graphComponent.zoomAndCenter(); } From 64aa3bb929bdcb8ba4d2b25ba70065d7fa17c367 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 11 Jan 2018 17:17:37 +0100 Subject: [PATCH 014/128] first pass at pinning from the VisualizationPanel --- .../communications/VisualizationPanel.java | 87 ++++++++++++------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 7563e97a29..2dd1ecbaff 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -25,19 +25,22 @@ import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxICell; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxGraph; -import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyVetoException; import java.util.Arrays; import java.util.Collection; +import static java.util.Collections.singleton; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -46,11 +49,15 @@ import java.util.Map; import java.util.Random; import java.util.Set; import java.util.logging.Level; +import javax.swing.AbstractAction; import javax.swing.JButton; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JSplitPane; import javax.swing.JToolBar; import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.AbstractNode; @@ -128,39 +135,45 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setCellsDeletable(false); graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); - - graphComponent = new mxGraphComponent(graph){ + graphComponent = new mxGraphComponent(graph) { @Override public boolean isPanningEvent(MouseEvent event) { - return true; //To change body of generated methods, choose Tools | Templates. + return true; } - + }; + + graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); -// graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { -// @Override -// public void mouseReleased(MouseEvent e) { -// graphComponent.setPanning(false); -// } -// -// @Override -// public void mouseMoved(MouseEvent e) { -// super.mouseMoved(e); -// if (graphComponent.isPanning()) { -// graphComponent.getGraphControl().setTranslate(e.getPoint()); -// } -// } -// -// @Override -// public void mousePressed(MouseEvent e) { -// graphComponent.setPanning(true); -// } -// }); + graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + super.mouseClicked(e); + if (SwingUtilities.isRightMouseButton(e)) { + mxICell cellAt = (mxICell) graphComponent.getCellAt(e.getX(), e.getY()); + if (cellAt != null && cellAt.isVertex()) { + JPopupMenu jPopupMenu = new JPopupMenu(); + jPopupMenu.add(new JMenuItem() { + { + setAction(new AbstractAction("Pin Account " + cellAt.getId()) { + @Override + public void actionPerformed(ActionEvent e) { + pinAccounts(new PinAccountEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()))); + } + }); + } + }); + + jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY()); + } + } + } + }); splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); CVTEvents.getCVTEventBus().register(this); @@ -446,23 +459,31 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton6ActionPerformed private void applyOrganicLayout() { + graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + graphComponent.getHeight())); new mxOrganicLayout(graph).execute(graph.getDefaultParent()); - mxGraphView view = graphComponent.getGraph().getView(); - int compLen = graphComponent.getWidth(); - int viewLen = (int) view.getGraphBounds().getWidth(); - view.setScale((double) compLen / viewLen * view.getScale()); - graphComponent.zoomAndCenter(); + fitGraph(); } - private void applyOrthogonalLayout() { + private void fitGraph() { +// mxGraphView view = graphComponent.getGraph().getView(); +// int compLen = graphComponent.getWidth(); +// int viewLen = (int) view.getGraphBounds().getWidth(); +// view.setScale((double) compLen / viewLen * view.getScale()); + + } + + private void applyOrthogonalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + graphComponent.getHeight())); new mxOrthogonalLayout(graph).execute(graph.getDefaultParent()); - graphComponent.zoomAndCenter(); + fitGraph(); } - private void applyHierarchicalLayout() { + private void applyHierarchicalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + graphComponent.getHeight())); new mxHierarchicalLayout(graph).execute(graph.getDefaultParent()); - graphComponent.zoomAndCenter(); + fitGraph(); } From ed19487607b320a0f272eb9f9fc5f66dcdbfcfab Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 12 Jan 2018 13:11:08 +0100 Subject: [PATCH 015/128] distinguish pin events that should replace the pinned nodes, and those that should append to them. --- .../AccountDeviceInstanceNode.java | 7 ++- .../autopsy/communications/CVTEvents.java | 20 +++++++ .../communications/CVTTopComponent.java | 3 +- .../communications/PinAccountEvent.java | 25 -------- .../communications/VisualizationPanel.java | 54 ++++++++++-------- .../images/icons8-neural-network.png | Bin 0 -> 1046 bytes 6 files changed, 58 insertions(+), 51 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/icons8-neural-network.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index 5b88b01999..6b60d75935 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -24,6 +24,7 @@ import java.util.Arrays; import java.util.Collection; import javax.swing.AbstractAction; import javax.swing.Action; +import javax.swing.ImageIcon; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Sheet; @@ -113,20 +114,22 @@ final class AccountDeviceInstanceNode extends AbstractNode { private static final long serialVersionUID = 1L; private static PinAccountsAction instance = new PinAccountsAction(); + static final private ImageIcon imageIcon = + new ImageIcon("images/icons8-neural-network.png"); private static PinAccountsAction getInstance() { return instance; } private PinAccountsAction() { - super("Visualize Account"); + super("Visualize Account", imageIcon); } @Override public void actionPerformed(ActionEvent e) { Collection lookupAll = Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class); - CVTEvents.getCVTEventBus().post(new PinAccountEvent(lookupAll)); + CVTEvents.getCVTEventBus().post(new CVTEvents.PinAccountsEvent(lookupAll, true)); } } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java index 663b711c99..6c0001fd21 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -18,7 +18,9 @@ */ package org.sleuthkit.autopsy.communications; +import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.EventBus; +import java.util.Collection; /** * Provide the singleton EventBus. @@ -34,4 +36,22 @@ final class CVTEvents { private CVTEvents() { } + static final class PinAccountsEvent { + + private final ImmutableSet accountDeviceInstances; + private final boolean replace; + + public boolean isReplace() { + return replace; + } + + ImmutableSet getAccountDeviceInstances() { + return accountDeviceInstances; + } + + PinAccountsEvent(Collection accountDeviceInstances, boolean replace) { + this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances); + this.replace = replace; + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index d19ad25496..a7c55ab631 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -75,10 +75,11 @@ public final class CVTTopComponent extends TopComponent { } @Subscribe - public void pinAccount(PinAccountEvent pinEvent) { + public void pinAccount(CVTEvents.PinAccountsEvent pinEvent) { browseVisualizeTabPane.setSelectedIndex(1); } + /** * 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 diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java deleted file mode 100644 index c8ba063adb..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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. - */ -package org.sleuthkit.autopsy.communications; - -import com.google.common.collect.ImmutableSet; -import java.util.Collection; - -/** - * - */ -final class PinAccountEvent { - - private final ImmutableSet accountDeviceInstances; - - ImmutableSet getAccountDeviceInstances() { - return accountDeviceInstances; - } - - PinAccountEvent(Collection accountDeviceInstances) { - this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 2dd1ecbaff..39fc676f80 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -50,6 +50,7 @@ import java.util.Random; import java.util.Set; import java.util.logging.Level; import javax.swing.AbstractAction; +import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JMenuItem; import javax.swing.JPanel; @@ -88,7 +89,10 @@ import org.sleuthkit.datamodel.TskCoreException; final public class VisualizationPanel extends JPanel implements Lookup.Provider { private static final long serialVersionUID = 1L; - private Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); + private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); + + static final private ImageIcon imageIcon = + new ImageIcon("images/icons8-neural-network.png"); static final private mxStylesheet mxStylesheet = new mxStylesheet(); @@ -143,7 +147,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }; - + graphComponent.setAutoExtend(true); graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); @@ -158,12 +162,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxICell cellAt = (mxICell) graphComponent.getCellAt(e.getX(), e.getY()); if (cellAt != null && cellAt.isVertex()) { JPopupMenu jPopupMenu = new JPopupMenu(); - jPopupMenu.add(new JMenuItem() { + jPopupMenu.add(new JMenuItem(imageIcon) { { setAction(new AbstractAction("Pin Account " + cellAt.getId()) { @Override public void actionPerformed(ActionEvent e) { - pinAccounts(new PinAccountEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()))); + pinAccounts(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); } }); } @@ -215,20 +219,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); - mxCell vertex = nodeMap.get(name); - if (vertex == null) { + return nodeMap.computeIfAbsent(name, vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; - vertex = (mxCell) graph.insertVertex( + mxCell vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), - name, accountDeviceInstanceKey, + vertexName, accountDeviceInstanceKey, new Random().nextInt(200), new Random().nextInt(200), size, size); - graph.getView().getState(vertex, true).setLabel(name); - nodeMap.put(name, vertex); - } - return vertex; + graph.getView().getState(vertex, true).setLabel(vertexName); + return vertex; + }); } @SuppressWarnings("unchecked") @@ -246,17 +248,17 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Subscribe - public void pinAccounts(PinAccountEvent pinEvent) { + public void pinAccounts(CVTEvents.PinAccountsEvent pinEvent) { final Set adiKeys = pinEvent.getAccountDeviceInstances(); final CommunicationsFilter commsFilter = filterProvider.getFilter(); graph.getModel().beginUpdate(); try { - nodeMap.clear(); - edgeMap.clear(); - graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); - + if (pinEvent.isReplace()) { + clearGraph(); + } else { + } for (AccountDeviceInstanceKey adiKey : adiKeys) { mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); @@ -288,6 +290,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider revalidate(); } + private void clearGraph() { + nodeMap.clear(); + edgeMap.clear(); + graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); + } + @Override public void addNotify() { super.addNotify(); @@ -305,9 +313,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { graph.getModel().beginUpdate(); try { - nodeMap.clear(); - edgeMap.clear(); - graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); + clearGraph(); } finally { graph.getModel().endUpdate(); } @@ -459,7 +465,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton6ActionPerformed private void applyOrganicLayout() { - graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxOrganicLayout(graph).execute(graph.getDefaultParent()); @@ -474,13 +480,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } - private void applyOrthogonalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + private void applyOrthogonalLayout() { + graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxOrthogonalLayout(graph).execute(graph.getDefaultParent()); fitGraph(); } - private void applyHierarchicalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + private void applyHierarchicalLayout() { + graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxHierarchicalLayout(graph).execute(graph.getDefaultParent()); fitGraph(); diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/icons8-neural-network.png b/Core/src/org/sleuthkit/autopsy/communications/images/icons8-neural-network.png new file mode 100644 index 0000000000000000000000000000000000000000..331be052314b9451e92d0378ce954e9a4ea56e4c GIT binary patch literal 1046 zcmV+x1nK*UP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1FK0yK~z{r#g|P? z6HyR=F?uk1(3?L|6Au_K8gC|g;6i^ev<*K6^dLs?00%+68I>4*T1va6g##*r33?$) zqQMv~2}av)yKf8SPeBv^BD9qf3Q3JbrM_8aYpHE1hJ`PgWZ%y0%-c7!@6Bec1-Ui+ zwwwm{p{$zzEqH7F&^8nEnbv{nOAp=zv{pV)z>rc@NlzZ*F2B+9e(uBUV&r3JF)|+f z6)C$j6jU16qi7;6B~}kNIQ2saF3RJfnMk3PG@*&KZ1@E7n@L%G3eMm526J@IBWNN$ zS_?6jv<`%;?~jFO5&H8?p-@Q0Sdry!?N%%>6s%jgDN9Y(2jN zZ?8ypqkXSj=j_E{Qd0WY7`YS0R*#Q){%IZruHhc4Q=-thGzvX?6Nd>&?LQLPRA}M9 z3{L+2oZ!01LOz3T$tXDx943Loe_6@DtjTq-x@*iYDn+^2H5vdNx><(2mfNpp_T#nSXfnFaRKnkusnwU!~1eV5BhAVx3iGXyZ&U;42A1LX5BaV1i QRR91007*qoM6N<$g5AQ^PXGV_ literal 0 HcmV?d00001 From 330d9ad4f6f95b7da1400b84b3a9ef0bbacd6c63 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 12 Jan 2018 15:49:05 +0100 Subject: [PATCH 016/128] simplify panning (use default ctrl+shift + drag) reinstate fiting in pinning, sore pinned nodes. --- .../communications/VisualizationPanel.java | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 39fc676f80..bc686fcd47 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -22,7 +22,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; -import com.mxgraph.layout.mxOrganicLayout; +import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; @@ -30,6 +30,7 @@ import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; @@ -46,7 +47,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.Set; import java.util.logging.Level; import javax.swing.AbstractAction; @@ -115,6 +115,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); private CommunicationsManager commsManager; + private final HashSet pinnedAccountDevices = new HashSet<>(); void setFilterProvider(FilterProvider filterProvider) { this.filterProvider = filterProvider; @@ -139,17 +140,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setCellsDeletable(false); graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); - graphComponent = new mxGraphComponent(graph) { - @Override - public boolean isPanningEvent(MouseEvent event) { - return true; - } - - }; - - graphComponent.setAutoExtend(true); - graphComponent.setAutoScroll(true); - + graphComponent = new mxGraphComponent(graph); + graphComponent.setConnectable(false); + graphComponent.setKeepSelectionVisibleOnZoom(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); @@ -164,7 +157,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider JPopupMenu jPopupMenu = new JPopupMenu(); jPopupMenu.add(new JMenuItem(imageIcon) { { - setAction(new AbstractAction("Pin Account " + cellAt.getId()) { + setAction(new AbstractAction("Pin Account " + graph.getLabel(cellAt)) { @Override public void actionPerformed(ActionEvent e) { pinAccounts(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); @@ -219,18 +212,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); - return nodeMap.computeIfAbsent(name, vertexName -> { + final mxCell computeIfAbsent = nodeMap.computeIfAbsent(name, vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; + mxCell vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), vertexName, accountDeviceInstanceKey, - new Random().nextInt(200), - new Random().nextInt(200), + 0, + 0, size, size); graph.getView().getState(vertex, true).setLabel(vertexName); return vertex; }); + return computeIfAbsent; } @SuppressWarnings("unchecked") @@ -250,28 +245,27 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Subscribe public void pinAccounts(CVTEvents.PinAccountsEvent pinEvent) { - final Set adiKeys = pinEvent.getAccountDeviceInstances(); final CommunicationsFilter commsFilter = filterProvider.getFilter(); graph.getModel().beginUpdate(); try { if (pinEvent.isReplace()) { + pinnedAccountDevices.clear(); clearGraph(); - } else { } - for (AccountDeviceInstanceKey adiKey : adiKeys) { + pinnedAccountDevices.addAll(pinEvent.getAccountDeviceInstances()); + for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), commsFilter); for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { - + long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter); List relationships = commsManager.getRelationships(adiKey.getAccountDeviceInstance(), relatedADI, commsFilter); - long communicationsCount = relationships.size(); AccountDeviceInstanceKey relatedADIKey = - new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount); + new AccountDeviceInstanceKey(relatedADI, commsFilter, adiRelationshipsCount); mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); for (BlackboardArtifact relationship : relationships) { addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); @@ -467,17 +461,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void applyOrganicLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); - new mxOrganicLayout(graph).execute(graph.getDefaultParent()); + new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); fitGraph(); } private void fitGraph() { -// mxGraphView view = graphComponent.getGraph().getView(); -// int compLen = graphComponent.getWidth(); -// int viewLen = (int) view.getGraphBounds().getWidth(); -// view.setScale((double) compLen / viewLen * view.getScale()); - + mxGraphView view = graphComponent.getGraph().getView(); + int compLen = graphComponent.getWidth(); + int viewLen = (int) view.getGraphBounds().getWidth(); + view.setScale((double) compLen / viewLen * view.getScale()); } private void applyOrthogonalLayout() { From 79194a2758a796901e2582d78ea63a9475da5534 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 12 Jan 2018 16:25:11 +0100 Subject: [PATCH 017/128] wire filters via the Eventbus instead of by passing a reference to the ExplorerManager --- .../communications/AccountsBrowser.java | 31 ++++++-- .../autopsy/communications/CVTEvents.java | 15 ++++ .../communications/CVTTopComponent.java | 22 ++---- .../communications/FilterProvider.java | 26 ------- .../autopsy/communications/FiltersPanel.java | 37 ++------- .../communications/VisualizationPanel.java | 75 +++++++++++-------- 6 files changed, 96 insertions(+), 110 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index 828d654298..b1d705f6e7 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -18,7 +18,9 @@ */ package org.sleuthkit.autopsy.communications; +import com.google.common.eventbus.Subscribe; import java.awt.Component; +import java.util.logging.Level; import javax.swing.JPanel; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; @@ -27,8 +29,14 @@ 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.nodes.AbstractNode; +import org.openide.nodes.Children; import org.openide.util.Lookup; import org.openide.util.lookup.ProxyLookup; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.TskCoreException; /** * A panel that goes in the Browse tab of the Communications Visualization Tool. @@ -42,17 +50,18 @@ import org.openide.util.lookup.ProxyLookup; public final class AccountsBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider { private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(AccountsBrowser.class.getName()); private final Outline outline; private final ExplorerManager messageBrowserEM = new ExplorerManager(); - private ExplorerManager accountsTableEM; + private final ExplorerManager accountsTableEM = new ExplorerManager(); /* * This lookup proxies the selection lookup of both he accounts table and * the messages table. */ - private ProxyLookup proxyLookup; + private final ProxyLookup proxyLookup; public AccountsBrowser() { initComponents(); @@ -68,11 +77,7 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded - } - - void init(ExplorerManager tableExplorerManager) { - this.accountsTableEM = tableExplorerManager; - tableExplorerManager.addPropertyChangeListener(evt -> { + accountsTableEM.addPropertyChangeListener(evt -> { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { SwingUtilities.invokeLater(this::setColumnWidths); } else if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) { @@ -80,7 +85,7 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro } }); - jSplitPane1.setRightComponent(new MessageBrowser(tableExplorerManager, messageBrowserEM)); + jSplitPane1.setRightComponent(new MessageBrowser(accountsTableEM, messageBrowserEM)); proxyLookup = new ProxyLookup( ExplorerUtils.createLookup(messageBrowserEM, getActionMap()), @@ -111,6 +116,16 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro } } + @Subscribe + public void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { + try { + final CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); + accountsTableEM.setRootContext(new AbstractNode(Children.create(new AccountDeviceInstanceNodeFactory(commsManager, filterChangeEvent.getNewFilter()), true))); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "There was an error getting the CommunicationsManager for the current case.", ex); + } + } + /** * 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 diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java index 6c0001fd21..f13128f5c3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.EventBus; import java.util.Collection; +import org.sleuthkit.datamodel.CommunicationsFilter; /** * Provide the singleton EventBus. @@ -36,6 +37,20 @@ final class CVTEvents { private CVTEvents() { } + static final class FilterChangeEvent { + + private final CommunicationsFilter newFilter; + + CommunicationsFilter getNewFilter() { + return newFilter; + } + + FilterChangeEvent(CommunicationsFilter newFilter) { + this.newFilter = newFilter; + } + + } + static final class PinAccountsEvent { private final ImmutableSet accountDeviceInstances; diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index a7c55ab631..39361de47e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.Subscribe; import java.util.List; import java.util.stream.Collectors; -import org.openide.explorer.ExplorerManager; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; @@ -42,22 +41,13 @@ public final class CVTTopComponent extends TopComponent { private static final long serialVersionUID = 1L; - private final ExplorerManager filterToTableEXplorerManager = new ExplorerManager(); - @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public CVTTopComponent() { initComponents(); setName(Bundle.CVTTopComponent_name()); - /* - * Connect the filtersPane and the accountsBrowser via a shared - * ExplorerMmanager - */ - filtersPane.setExplorerManager(filterToTableEXplorerManager); - accountsBrowser.init(filterToTableEXplorerManager); - /* - * Associate an Lookup with the GlobalActionContext (GAC) so that + * Associate a Lookup with the GlobalActionContext (GAC) so that * selections in the sub views can be exposed to context-sensitive * actions. */ @@ -69,9 +59,14 @@ public final class CVTTopComponent extends TopComponent { proxyLookup.changeLookups(selectedComponent.getLookup()); }); - vizPanel.setFilterProvider(filtersPane); - CVTEvents.getCVTEventBus().register(this); + /* + * Connect the filtersPane to the accountsBrowser and visualizaionPanel + * via an Eventbus + */ + CVTEvents.getCVTEventBus().register(this); + CVTEvents.getCVTEventBus().register(vizPanel); + CVTEvents.getCVTEventBus().register(accountsBrowser); } @Subscribe @@ -79,7 +74,6 @@ public final class CVTTopComponent extends TopComponent { browseVisualizeTabPane.setSelectedIndex(1); } - /** * 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 diff --git a/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java b/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java deleted file mode 100644 index 54f48f9dd1..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.communications; - -import org.sleuthkit.datamodel.CommunicationsFilter; - -interface FilterProvider { - - CommunicationsFilter getFilter(); -} diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 24e589290c..36232eaf15 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -31,9 +31,6 @@ import java.util.logging.Level; import java.util.stream.Collectors; import javax.swing.JCheckBox; import javax.swing.JPanel; -import org.openide.explorer.ExplorerManager; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.Children; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; @@ -49,7 +46,6 @@ import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter; -import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.DataSource; import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG; import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE; @@ -60,13 +56,11 @@ import org.sleuthkit.datamodel.TskCoreException; * Panel that holds the Filter control widgets and triggers queries against the * CommunicationsManager on user filtering changes. */ -final public class FiltersPanel extends JPanel implements FilterProvider { +final public class FiltersPanel extends JPanel { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName()); - private ExplorerManager em; - @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private final Map accountTypeMap = new HashMap<>(); @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @@ -134,10 +128,6 @@ final public class FiltersPanel extends JPanel implements FilterProvider { 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 @@ -161,9 +151,7 @@ final public class FiltersPanel extends JPanel implements FilterProvider { */ void updateAndApplyFilters() { updateFilters(); - if (em != null) { - applyFilters(); - } + applyFilters(); } private void updateTimeZone() { @@ -224,8 +212,7 @@ final public class FiltersPanel extends JPanel implements FilterProvider { return jCheckBox; }); } - } - ); + }); } /** @@ -490,27 +477,15 @@ final public class FiltersPanel extends JPanel implements FilterProvider { }// //GEN-END:initComponents /** - * Push a new root AccountDeviceInstanceNodeFactory with he current filters - * into the explorer manager. The factory will do he actual queries. - * - * + * Post an event with the new filters. */ private void applyFilters() { - CommunicationsFilter commsFilter = getFilter(); - - try { - final CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); - 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); - } - + CVTEvents.getCVTEventBus().post(new CVTEvents.FilterChangeEvent(getFilter())); needsRefresh = false; validateFilters(); } - @Override - public CommunicationsFilter getFilter() { + private CommunicationsFilter getFilter() { CommunicationsFilter commsFilter = new CommunicationsFilter(); commsFilter.addAndFilter(getDeviceFilter()); commsFilter.addAndFilter(getAccountTypeFilter()); diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index bc686fcd47..d202b54ee8 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -64,7 +64,6 @@ 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.NbBundle; import org.openide.util.lookup.ProxyLookup; @@ -116,11 +115,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private CommunicationsManager commsManager; private final HashSet pinnedAccountDevices = new HashSet<>(); - - void setFilterProvider(FilterProvider filterProvider) { - this.filterProvider = filterProvider; - } - private FilterProvider filterProvider; + private CommunicationsFilter currentFilter; public VisualizationPanel() { initComponents(); @@ -160,7 +155,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider setAction(new AbstractAction("Pin Account " + graph.getLabel(cellAt)) { @Override public void actionPerformed(ActionEvent e) { - pinAccounts(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); + handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); } }); } @@ -173,7 +168,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); - CVTEvents.getCVTEventBus().register(this); graph.setStylesheet(mxStylesheet); @@ -243,9 +237,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Subscribe - public void pinAccounts(CVTEvents.PinAccountsEvent pinEvent) { - - final CommunicationsFilter commsFilter = filterProvider.getFilter(); + public void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); try { @@ -254,36 +246,57 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider clearGraph(); } pinnedAccountDevices.addAll(pinEvent.getAccountDeviceInstances()); - for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { - mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); - - List relatedAccountDeviceInstances = - commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), commsFilter); - - for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { - long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter); - List relationships = commsManager.getRelationships(adiKey.getAccountDeviceInstance(), relatedADI, commsFilter); - - AccountDeviceInstanceKey relatedADIKey = - new AccountDeviceInstanceKey(relatedADI, commsFilter, adiRelationshipsCount); - mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); - for (BlackboardArtifact relationship : relationships) { - addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); - } - } - } + rebuildGraph(); } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); + logger.log(Level.SEVERE, "Error pinning accounts", ex); } finally { // Updates the display graph.getModel().endUpdate(); - } applyOrganicLayout(); revalidate(); } + @Subscribe + public void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { + graph.getModel().beginUpdate(); + try { + clearGraph(); + currentFilter = filterChangeEvent.getNewFilter(); + rebuildGraph(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error filtering accounts", ex); + } finally { + // Updates the display + graph.getModel().endUpdate(); + } + + applyOrganicLayout(); + revalidate(); + } + + private void rebuildGraph() throws TskCoreException { + for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { + mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); + + List relatedAccountDeviceInstances = + commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); + + for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { + long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); + List relationships = commsManager.getRelationships(adiKey.getAccountDeviceInstance(), relatedADI, currentFilter); + + AccountDeviceInstanceKey relatedADIKey = + new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); + mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); + for (BlackboardArtifact relationship : relationships) { + addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); + } + } + } + } + private void clearGraph() { nodeMap.clear(); edgeMap.clear(); From b3faf438af043385aca74088a97724adab790cea Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 15 Jan 2018 17:06:46 +0100 Subject: [PATCH 018/128] fix filtering begin adding rubberband and multiselect --- .../communications/VisualizationPanel.java | 94 ++++++++++++------- 1 file changed, 62 insertions(+), 32 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index d202b54ee8..ff25a753a0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -26,8 +26,11 @@ import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; +import com.mxgraph.swing.handler.mxRubberband; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraphView; @@ -49,6 +52,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; +import java.util.stream.Stream; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -116,6 +120,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private CommunicationsManager commsManager; private final HashSet pinnedAccountDevices = new HashSet<>(); private CommunicationsFilter currentFilter; + private final mxRubberband rubberband; public VisualizationPanel() { initComponents(); @@ -132,16 +137,23 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setEdgeLabelsMovable(false); graph.setVertexLabelsMovable(false); graph.setAllowDanglingEdges(false); - graph.setCellsDeletable(false); graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); + graph.setStylesheet(mxStylesheet); graphComponent = new mxGraphComponent(graph); + graphComponent.setAutoExtend(true); + graphComponent.setAutoScroll(true); + graphComponent.setAutoscrolls(true); graphComponent.setConnectable(false); graphComponent.setKeepSelectionVisibleOnZoom(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); + //install rubber band selection handler + rubberband = new mxRubberband(graphComponent); + + //right click handler graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { @@ -169,32 +181,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); - graph.setStylesheet(mxStylesheet); - - graph.getSelectionModel().addListener(null, (sender, evt) -> { - Object[] selectionCells = graph.getSelectionCells(); - if (selectionCells.length == 1) { - mxCell selectionCell = (mxCell) selectionCells[0]; - try { - - if (selectionCell.isVertex()) { - final AccountDeviceInstanceNode accountDeviceInstanceNode = - new AccountDeviceInstanceNode(((AccountDeviceInstanceKey) selectionCell.getValue()), - commsManager); - vizEM.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); - vizEM.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); - - } else if (selectionCell.isEdge()) { - @SuppressWarnings("unchecked") - AbstractNode abstractNode = new AbstractNode(Children.create(new RelaionshipSetNodeFactory((Set) selectionCell.getValue()), true)); - vizEM.setRootContext(abstractNode); - vizEM.setSelectedNodes(new Node[]{abstractNode}); - } - } catch (PropertyVetoException ex) { - logger.log(Level.SEVERE, "Account selection vetoed.", ex); - } - } - }); + //feed selection to explorermanager + graph.getSelectionModel().addListener(null, new SelectionListener()); } @Override @@ -238,7 +226,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Subscribe public void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { - graph.getModel().beginUpdate(); try { if (pinEvent.isReplace()) { @@ -255,11 +242,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } applyOrganicLayout(); - revalidate(); } @Subscribe public void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { + graph.getModel().beginUpdate(); try { clearGraph(); @@ -273,7 +260,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } applyOrganicLayout(); - revalidate(); } private void rebuildGraph() throws TskCoreException { @@ -300,7 +286,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void clearGraph() { nodeMap.clear(); edgeMap.clear(); - graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); + graph.removeCells(graph.getChildVertices(graph.getDefaultParent())); } @Override @@ -483,7 +469,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxGraphView view = graphComponent.getGraph().getView(); int compLen = graphComponent.getWidth(); int viewLen = (int) view.getGraphBounds().getWidth(); - view.setScale((double) compLen / viewLen * view.getScale()); +// view.setScale((double) compLen / viewLen * view.getScale()); + } private void applyOrthogonalLayout() { @@ -525,4 +512,47 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider super(children); } } + + private class SelectionListener implements mxEventSource.mxIEventListener { + + @Override + + public void invoke(Object sender, mxEventObject evt) { + Object[] selectionCells = graph.getSelectionCells(); + if (selectionCells.length == 0) { + vizEM.setRootContext(Node.EMPTY); + } else { + Node rootNode; + Node[] selectedNodes; + mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]); + + if (Stream.of(selectedCells).allMatch(mxICell::isVertex)) { + HashSet adis = new HashSet<>(); + for (mxICell vertex : selectedCells) { + adis.add(((AccountDeviceInstanceKey) vertex.getValue()).getAccountDeviceInstance()); + } + + final AccountDetailsNode accountDeviceInstanceNode = + new AccountDetailsNode(adis, currentFilter, commsManager); + rootNode = SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode); + selectedNodes = new Node[]{accountDeviceInstanceNode}; + + } else { + HashSet relationshipArtifacts = new HashSet<>(); + for (mxICell edge : selectedCells) { + relationshipArtifacts.addAll((Set) edge.getValue()); + } + + rootNode = new AbstractNode(Children.create(new RelaionshipSetNodeFactory(relationshipArtifacts), true)); + selectedNodes = new Node[]{rootNode}; + } + vizEM.setRootContext(rootNode); + try { + vizEM.setSelectedNodes(selectedNodes); + } catch (PropertyVetoException ex) { + logger.log(Level.SEVERE, "Account selection vetoed.", ex); + } + } + } + } } From fcd170a393bd88517785b8f5c0f969c429726367 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 16 Jan 2018 12:57:09 +0100 Subject: [PATCH 019/128] refactoring multi select in MessageBrowser --- .../communications/AccountDetailsNode.java | 5 ++ .../AccountDeviceInstanceNode.java | 1 + .../autopsy/communications/Bundle.properties | 1 + .../communications/MessageBrowser.java | 66 ++++++++----------- .../communications/VisualizationPanel.form | 13 ++++ .../communications/VisualizationPanel.java | 50 ++++++++++---- 6 files changed, 85 insertions(+), 51 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java index 7bc1a0faae..39eae2b9f3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java @@ -25,6 +25,7 @@ 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; @@ -44,6 +45,10 @@ final class AccountDetailsNode extends AbstractNode { AccountDetailsNode(Set 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); } /** diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index 6b60d75935..c0208b1bce 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -52,6 +52,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { this.commsManager = commsManager; this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount(); setName(account.getTypeSpecificID()); + setDisplayName(getName()); setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType())); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 71d89548f8..63f58253a0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -25,3 +25,4 @@ VisualizationPanel.jButton3.text=Orthogonal VisualizationPanel.jButton4.text=- VisualizationPanel.jButton5.text=+ VisualizationPanel.jButton6.text=Hierarchy +VisualizationPanel.jButton7.text=Compact Tree diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index d0cbdc6e64..b04f232a29 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -18,7 +18,8 @@ */ package org.sleuthkit.autopsy.communications; -import java.util.Collections; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -31,8 +32,6 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.datamodel.AccountDeviceInstance; -import org.sleuthkit.datamodel.CommunicationsFilter; -import org.sleuthkit.datamodel.CommunicationsManager; /** * The right hand side of the CVT. Has a DataResultPanel to show messages and @@ -57,7 +56,7 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov * context-sensitive actions. */ @NbBundle.Messages({"MessageBrowser.DataResultViewerTable.title=Messages"}) - MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { + MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { this.tableEM = tableEM; this.gacExplorerManager = gacExplorerManager; initComponents(); @@ -69,45 +68,38 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov Bundle.MessageBrowser_DataResultViewerTable_title())); messagesResultPanel.open(); - this.tableEM.addPropertyChangeListener(pce -> { - if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { - final Node[] selectedNodes = this.tableEM.getSelectedNodes(); - - messagesResultPanel.setNumMatches(0); - messagesResultPanel.setNode(null); - - if (selectedNodes.length == 0) { - //reset panel when there is no selection + this.tableEM.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent pce) { + if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { + final Node[] selectedNodes = MessageBrowser.this.tableEM.getSelectedNodes(); + messagesResultPanel.setNumMatches(0); + messagesResultPanel.setNode(null); messagesResultPanel.setPath(""); - } else { - final Node selectedNode = selectedNodes[0]; - if (selectedNode instanceof AccountDeviceInstanceNode) { - AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNode; - CommunicationsFilter filter = adiNode.getFilter(); - CommunicationsManager commsManager = adiNode.getCommsManager(); - final Set accountDeviceInstances; - - if (selectedNodes.length == 1) { - final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance(); - accountDeviceInstances = Collections.singleton(accountDeviceInstance); - messagesResultPanel.setPath(accountDeviceInstance.getAccount().getTypeSpecificID()); + if (selectedNodes.length > 0) { + Node rootNode; + final Node selectedNode = selectedNodes[0]; + String path = selectedNode.getDisplayName(); + if (selectedNode instanceof AccountDeviceInstanceNode) { + rootNode = makeRootNodeFromAccountDeviceInstanceNodes(selectedNodes); } else { - accountDeviceInstances = Stream.of(selectedNodes) - .map(node -> (AccountDeviceInstanceNode) node) - .map(AccountDeviceInstanceNode::getAccountDeviceInstance) - .collect(Collectors.toSet()); - messagesResultPanel.setPath(selectedNodes.length + " accounts"); + rootNode = selectedNode; } - AccountDetailsNode accountDetailsNode = - new AccountDetailsNode(accountDeviceInstances, filter, commsManager); - TableFilterNode wrappedNode = - new TableFilterNode(new DataResultFilterNode(accountDetailsNode, gacExplorerManager), true); - messagesResultPanel.setNode(wrappedNode); - }else{ - messagesResultPanel.setNode(selectedNode); + messagesResultPanel.setPath(path); + messagesResultPanel.setNode(new TableFilterNode(new DataResultFilterNode(rootNode, gacExplorerManager), true)); } } } + + private Node makeRootNodeFromAccountDeviceInstanceNodes(final Node[] selectedNodes) { + AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0]; + final Set accountDeviceInstances; + accountDeviceInstances = Stream.of(selectedNodes) + .map(AccountDeviceInstanceNode.class::cast) + .map(AccountDeviceInstanceNode::getAccountDeviceInstance) + .collect(Collectors.toSet()); + return new AccountDetailsNode(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager()); + } }); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 8a2130752f..da37189ec5 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -102,6 +102,19 @@ + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index ff25a753a0..1f9fcf66ee 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -22,6 +22,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; +import com.mxgraph.layout.mxCompactTreeLayout; import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; @@ -346,6 +347,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jButton6 = new JButton(); jButton1 = new JButton(); jButton3 = new JButton(); + jButton7 = new JButton(); jButton4 = new JButton(); jButton5 = new JButton(); @@ -402,6 +404,17 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); jToolBar1.add(jButton3); + jButton7.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton7.text")); // NOI18N + jButton7.setFocusable(false); + jButton7.setHorizontalTextPosition(SwingConstants.CENTER); + jButton7.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton7.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jButton7ActionPerformed(evt); + } + }); + jToolBar1.add(jButton7); + jButton4.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton4.text")); // NOI18N jButton4.setFocusable(false); jButton4.setHorizontalTextPosition(SwingConstants.CENTER); @@ -457,6 +470,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider applyHierarchicalLayout(); }//GEN-LAST:event_jButton6ActionPerformed + private void jButton7ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton7ActionPerformed + applyCompactTreeLayout(); + }//GEN-LAST:event_jButton7ActionPerformed + private void applyOrganicLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); @@ -487,6 +504,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider fitGraph(); } + private void applyCompactTreeLayout() { + graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + graphComponent.getHeight())); + new mxCompactTreeLayout(graph).execute(graph.getDefaultParent()); + fitGraph(); + } + // Variables declaration - do not modify//GEN-BEGIN:variables private JButton jButton1; @@ -495,6 +519,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JButton jButton4; private JButton jButton5; private JButton jButton6; + private JButton jButton7; private JPanel jPanel1; private JToolBar jToolBar1; private JSplitPane splitPane; @@ -516,14 +541,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private class SelectionListener implements mxEventSource.mxIEventListener { @Override - + public void invoke(Object sender, mxEventObject evt) { Object[] selectionCells = graph.getSelectionCells(); - if (selectionCells.length == 0) { - vizEM.setRootContext(Node.EMPTY); - } else { - Node rootNode; - Node[] selectedNodes; + Node rootNode = Node.EMPTY; + Node[] selectedNodes = new Node[0]; + if (selectionCells.length > 0) { mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]); if (Stream.of(selectedCells).allMatch(mxICell::isVertex)) { @@ -537,21 +560,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider rootNode = SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode); selectedNodes = new Node[]{accountDeviceInstanceNode}; - } else { + } else if (Stream.of(selectedCells).allMatch(mxICell::isEdge)) { HashSet relationshipArtifacts = new HashSet<>(); for (mxICell edge : selectedCells) { relationshipArtifacts.addAll((Set) edge.getValue()); } - rootNode = new AbstractNode(Children.create(new RelaionshipSetNodeFactory(relationshipArtifacts), true)); selectedNodes = new Node[]{rootNode}; } - vizEM.setRootContext(rootNode); - try { - vizEM.setSelectedNodes(selectedNodes); - } catch (PropertyVetoException ex) { - logger.log(Level.SEVERE, "Account selection vetoed.", ex); - } + } + vizEM.setRootContext(rootNode); + try { + vizEM.setSelectedNodes(selectedNodes); + } catch (PropertyVetoException ex) { + logger.log(Level.SEVERE, "Selection vetoed.", ex); } } } From 85fb7ee11e06c08fb61cebcde0104b0a03837ee7 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 16 Jan 2018 13:46:28 +0100 Subject: [PATCH 020/128] fix Type column of RelationshipNode --- .../autopsy/communications/RelationshipNode.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index 27c39d80ec..e952573f50 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -47,7 +47,7 @@ final class RelationshipNode extends BlackboardArtifactNode { private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName()); - RelationshipNode(BlackboardArtifact artifact) { + RelationshipNode(BlackboardArtifact artifact) { super(artifact); final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message"); @@ -143,4 +143,15 @@ final class RelationshipNode extends BlackboardArtifactNode { 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(); + } } From f63d76a9673238d39c6efd74e6a1247cb4c2655b Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 16 Jan 2018 17:15:55 +0100 Subject: [PATCH 021/128] uniform multiselect using SelectionNode --- .../communications/CVTTopComponent.java | 2 +- .../communications/MessageBrowser.java | 21 +-- .../autopsy/communications/SelectionNode.java | 133 ++++++++++++++++++ .../communications/VisualizationPanel.java | 64 +++------ 4 files changed, 165 insertions(+), 55 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 39361de47e..6585f6324a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -70,7 +70,7 @@ public final class CVTTopComponent extends TopComponent { } @Subscribe - public void pinAccount(CVTEvents.PinAccountsEvent pinEvent) { + void pinAccount(CVTEvents.PinAccountsEvent pinEvent) { browseVisualizeTabPane.setSelectedIndex(1); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index b04f232a29..226c0cf508 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -20,9 +20,8 @@ package org.sleuthkit.autopsy.communications; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.HashSet; import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.swing.JPanel; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; @@ -79,26 +78,28 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov if (selectedNodes.length > 0) { Node rootNode; final Node selectedNode = selectedNodes[0]; - String path = selectedNode.getDisplayName(); + if (selectedNode instanceof AccountDeviceInstanceNode) { rootNode = makeRootNodeFromAccountDeviceInstanceNodes(selectedNodes); } else { rootNode = selectedNode; } - messagesResultPanel.setPath(path); + messagesResultPanel.setPath(rootNode.getDisplayName()); messagesResultPanel.setNode(new TableFilterNode(new DataResultFilterNode(rootNode, gacExplorerManager), true)); } } } private Node makeRootNodeFromAccountDeviceInstanceNodes(final Node[] selectedNodes) { + //Use lookup here? AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0]; - final Set accountDeviceInstances; - accountDeviceInstances = Stream.of(selectedNodes) - .map(AccountDeviceInstanceNode.class::cast) - .map(AccountDeviceInstanceNode::getAccountDeviceInstance) - .collect(Collectors.toSet()); - return new AccountDetailsNode(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager()); + + final Set accountDeviceInstances = new HashSet<>(); + for (Node n : selectedNodes) { + //Use lookup here? + accountDeviceInstances.add(((AccountDeviceInstanceNode) n).getAccountDeviceInstance()); + } + return SelectionNode.createFromAccounts(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager()); } }); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java b/Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java new file mode 100644 index 0000000000..40739b49cc --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java @@ -0,0 +1,133 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.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 org.openide.nodes.AbstractNode; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +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) { + super(children); + } + + static SelectionNode createFromAccountsAndRelationships( + Set edgeRelationshipArtifacts, + Set accountDeviceInstances, + CommunicationsFilter filter, + CommunicationsManager commsManager) { + + SelectionNode node = new SelectionNode(Children.create( + new RelationshipChildren( + edgeRelationshipArtifacts, + accountDeviceInstances, + commsManager, + filter), + true)); + + //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 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 { + + static final private Logger logger = Logger.getLogger(RelationshipChildren.class.getName()); + + private final Set edgeRelationshipArtifacts; + + private final Set accountDeviceInstances; + + private final CommunicationsManager commsManager; + private final CommunicationsFilter filter; + + private RelationshipChildren(Set selectedEdgeRelationshipSources, Set selecedAccountDeviceInstances, CommunicationsManager commsManager, CommunicationsFilter filter) { + this.edgeRelationshipArtifacts = selectedEdgeRelationshipSources; + this.accountDeviceInstances = selecedAccountDeviceInstances; + this.commsManager = commsManager; + this.filter = filter; + } + + @Override + protected boolean createKeys(List list) { + try { + final Set 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 t) { + if (t instanceof BlackboardArtifact) { + return new RelationshipNode((BlackboardArtifact) t); + } else { + throw new UnsupportedOperationException("Cannot create a RelationshipNode for non BlackboardArtifact content."); + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 1f9fcf66ee..5db792c344 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -53,7 +53,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; -import java.util.stream.Stream; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -66,8 +65,6 @@ import javax.swing.SwingConstants; import javax.swing.SwingUtilities; 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.Lookup; import org.openide.util.NbBundle; @@ -76,9 +73,9 @@ import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; 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; /** @@ -116,7 +113,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final mxGraphComponent graphComponent; private final mxGraph graph; private final Map nodeMap = new HashMap<>(); - private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); + private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); private CommunicationsManager commsManager; private final HashSet pinnedAccountDevices = new HashSet<>(); @@ -212,21 +209,21 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @SuppressWarnings("unchecked") - private void addEdge(BlackboardArtifact artifact, mxCell pinnedAccountVertex, mxCell relatedAccountVertex) throws TskCoreException { + private void addEdge(Content relSource, mxCell pinnedAccountVertex, mxCell relatedAccountVertex) throws TskCoreException { Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); if (edgesBetween.length == 0) { final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); - mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(artifact)), pinnedAccountVertex, relatedAccountVertex); - edgeMap.put(artifact, edge); + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(relSource)), pinnedAccountVertex, relatedAccountVertex); + edgeMap.put(relSource, edge); } else if (edgesBetween.length == 1) { final mxCell edge = (mxCell) edgesBetween[0]; - ((Collection) edge.getValue()).add(artifact); + ((Collection) edge.getValue()).add(relSource); edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); } } @Subscribe - public void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { + void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); try { if (pinEvent.isReplace()) { @@ -246,7 +243,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Subscribe - public void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { + void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { graph.getModel().beginUpdate(); try { @@ -272,12 +269,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); - List relationships = commsManager.getRelationships(adiKey.getAccountDeviceInstance(), relatedADI, currentFilter); + List relationships = commsManager.getRelationshipSources(adiKey.getAccountDeviceInstance(), relatedADI, currentFilter); AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); - for (BlackboardArtifact relationship : relationships) { + for (Content relationship : relationships) { addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); } } @@ -525,49 +522,28 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JSplitPane splitPane; // End of variables declaration//GEN-END:variables - private static class SimpleParentNode extends AbstractNode { - - private static SimpleParentNode createFromChildNodes(Node... nodes) { - Children.Array array = new Children.Array(); - array.add(nodes); - return new SimpleParentNode(array); - } - - private SimpleParentNode(Children children) { - super(children); - } - } - private class SelectionListener implements mxEventSource.mxIEventListener { @Override + @SuppressWarnings("unchecked") 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]); - - if (Stream.of(selectedCells).allMatch(mxICell::isVertex)) { - HashSet adis = new HashSet<>(); - for (mxICell vertex : selectedCells) { - adis.add(((AccountDeviceInstanceKey) vertex.getValue()).getAccountDeviceInstance()); + HashSet relationshipSources = new HashSet<>(); + HashSet adis = new HashSet<>(); + for (mxICell cell : selectedCells) { + if (cell.isEdge()) { + relationshipSources.addAll((Set) cell.getValue()); + } else if (cell.isVertex()) { + adis.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance()); } - - final AccountDetailsNode accountDeviceInstanceNode = - new AccountDetailsNode(adis, currentFilter, commsManager); - rootNode = SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode); - selectedNodes = new Node[]{accountDeviceInstanceNode}; - - } else if (Stream.of(selectedCells).allMatch(mxICell::isEdge)) { - HashSet relationshipArtifacts = new HashSet<>(); - for (mxICell edge : selectedCells) { - relationshipArtifacts.addAll((Set) edge.getValue()); - } - rootNode = new AbstractNode(Children.create(new RelaionshipSetNodeFactory(relationshipArtifacts), true)); - selectedNodes = new Node[]{rootNode}; } + rootNode = SelectionNode.createFromAccountsAndRelationships(relationshipSources, adis, currentFilter, commsManager); + selectedNodes = new Node[]{rootNode}; } vizEM.setRootContext(rootNode); try { From 055931b81d20143cd15465f99f1f743584306949 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 16 Jan 2018 22:56:40 +0100 Subject: [PATCH 022/128] Cartesian product WIP --- .../communications/VisualizationPanel.java | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 5db792c344..68c2a1efce 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; +import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.layout.mxCompactTreeLayout; @@ -34,7 +35,6 @@ import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxGraph; -import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; @@ -102,6 +102,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "#000000"); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ENDARROW, mxConstants.NONE); } private final ExplorerManager vizEM = new ExplorerManager(); @@ -209,11 +210,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @SuppressWarnings("unchecked") - private void addEdge(Content relSource, mxCell pinnedAccountVertex, mxCell relatedAccountVertex) throws TskCoreException { - Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); + private void addEdge(Content relSource, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { + mxCell vertex1 = getOrCreateVertex(account1); + mxCell vertex2 = getOrCreateVertex(account2); + Object[] edgesBetween = graph.getEdgesBetween(vertex1, vertex2); if (edgesBetween.length == 0) { - final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); - mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(relSource)), pinnedAccountVertex, relatedAccountVertex); + final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(relSource)), vertex1, vertex2); edgeMap.put(relSource, edge); } else if (edgesBetween.length == 1) { final mxCell edge = (mxCell) edgesBetween[0]; @@ -223,7 +226,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Subscribe - void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { + void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); try { if (pinEvent.isReplace()) { @@ -239,11 +242,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyOrganicLayout(); + applyHierarchicalLayout(); } @Subscribe - void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { + void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { graph.getModel().beginUpdate(); try { @@ -257,26 +260,34 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyOrganicLayout(); + applyHierarchicalLayout(); } private void rebuildGraph() throws TskCoreException { + + Set allAccounts = new HashSet<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { - mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); + allAccounts.add(adiKey); List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); - List relationships = commsManager.getRelationshipSources(adiKey.getAccountDeviceInstance(), relatedADI, currentFilter); + allAccounts.add(new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount)); + } + } - AccountDeviceInstanceKey relatedADIKey = - new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); - mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); - for (Content relationship : relationships) { - addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); - } + Set> cartesianProduct = Sets.cartesianProduct(Arrays.asList(allAccounts, allAccounts)); + for (List pair : cartesianProduct) { + AccountDeviceInstanceKey adiKey1 = pair.get(0); + AccountDeviceInstanceKey adiKey2 = pair.get(1); + List relationships = commsManager.getRelationshipSources( + adiKey1.getAccountDeviceInstance(), + adiKey2.getAccountDeviceInstance(), + currentFilter); + for (Content relationship : relationships) { + addEdge(relationship, adiKey1, adiKey2); } } } @@ -476,36 +487,25 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.getHeight())); new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); - fitGraph(); } - private void fitGraph() { - mxGraphView view = graphComponent.getGraph().getView(); - int compLen = graphComponent.getWidth(); - int viewLen = (int) view.getGraphBounds().getWidth(); -// view.setScale((double) compLen / viewLen * view.getScale()); - - } private void applyOrthogonalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxOrthogonalLayout(graph).execute(graph.getDefaultParent()); - fitGraph(); } private void applyHierarchicalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxHierarchicalLayout(graph).execute(graph.getDefaultParent()); - fitGraph(); } private void applyCompactTreeLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxCompactTreeLayout(graph).execute(graph.getDefaultParent()); - fitGraph(); } From 19f13a0b30dd113abdb6c90845e9588eacd5b682 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 17 Jan 2018 12:04:57 +0100 Subject: [PATCH 023/128] simplify getting relationships to improve graph loading speed. It is still to slow! --- .../autopsy/communications/Bundle.properties | 2 +- .../communications/VisualizationPanel.java | 108 +++++++++++------- 2 files changed, 68 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 63f58253a0..61ee014750 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -25,4 +25,4 @@ VisualizationPanel.jButton3.text=Orthogonal VisualizationPanel.jButton4.text=- VisualizationPanel.jButton5.text=+ VisualizationPanel.jButton6.text=Hierarchy -VisualizationPanel.jButton7.text=Compact Tree +VisualizationPanel.jButton7.text=Circle diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 68c2a1efce..69129f8e4a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -20,20 +20,22 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; -import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; -import com.mxgraph.layout.mxCompactTreeLayout; +import com.mxgraph.layout.mxCircleLayout; import com.mxgraph.layout.mxFastOrganicLayout; +import com.mxgraph.layout.mxIGraphLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; import com.mxgraph.swing.handler.mxRubberband; import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.util.mxMorphing; import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxEvent; import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; -import com.mxgraph.util.mxRectangle; +import com.mxgraph.util.mxEventSource.mxIEventListener; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; @@ -43,6 +45,7 @@ import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyVetoException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import static java.util.Collections.singleton; @@ -199,8 +202,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxCell vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), vertexName, accountDeviceInstanceKey, - 0, - 0, + Math.random() * graphComponent.getWidth(), + Math.random() * graphComponent.getHeight(), size, size); graph.getView().getState(vertex, true).setLabel(vertexName); @@ -210,17 +213,19 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @SuppressWarnings("unchecked") - private void addEdge(Content relSource, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { + private void addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { mxCell vertex1 = getOrCreateVertex(account1); mxCell vertex2 = getOrCreateVertex(account2); Object[] edgesBetween = graph.getEdgesBetween(vertex1, vertex2); if (edgesBetween.length == 0) { final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); - mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(relSource)), vertex1, vertex2); - edgeMap.put(relSource, edge); + final HashSet hashSet = new HashSet<>(relSources); + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, hashSet, vertex1, vertex2, + "strokeWidth=" + Math.sqrt(hashSet.size())); +// edgeMap.put(relSource, edge); } else if (edgesBetween.length == 1) { final mxCell edge = (mxCell) edgesBetween[0]; - ((Collection) edge.getValue()).add(relSource); + ((Collection) edge.getValue()).addAll(relSources); edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); } } @@ -242,7 +247,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyHierarchicalLayout(); +// applyOrganicLayout(); } @Subscribe @@ -260,34 +265,46 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyHierarchicalLayout(); +// applyOrganicLayout(); } private void rebuildGraph() throws TskCoreException { - Set allAccounts = new HashSet<>(); + /** + * set to keep track of accounts related to pinned accounts + */ + Set relatedAccounts = new HashSet<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { - allAccounts.add(adiKey); - List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); + //get accounts related to pinned account for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); - allAccounts.add(new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount)); + final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); + + //add and edge between pinned and related accounts + List relationships = commsManager.getRelationshipSources( + adiKey.getAccountDeviceInstance(), + relatedADIKey.getAccountDeviceInstance(), + currentFilter); + addEdge(relationships, adiKey, relatedADIKey); + relatedAccounts.add(relatedADIKey); //store related accounts } } - Set> cartesianProduct = Sets.cartesianProduct(Arrays.asList(allAccounts, allAccounts)); - for (List pair : cartesianProduct) { - AccountDeviceInstanceKey adiKey1 = pair.get(0); - AccountDeviceInstanceKey adiKey2 = pair.get(1); - List relationships = commsManager.getRelationshipSources( - adiKey1.getAccountDeviceInstance(), - adiKey2.getAccountDeviceInstance(), - currentFilter); - for (Content relationship : relationships) { - addEdge(relationship, adiKey1, adiKey2); + //for each pair of related accounts add edges if they are related o each other. + // this is O(n^2) in the number of related accounts!!! + List relatedAccountsList = new ArrayList<>(relatedAccounts); + for (int i = 0; i < relatedAccountsList.size(); i++) { + for (int j = i; j < relatedAccountsList.size(); j++) { + AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); + AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); + List relationships = commsManager.getRelationshipSources( + adiKey1.getAccountDeviceInstance(), + adiKey2.getAccountDeviceInstance(), + currentFilter); + addEdge(relationships, adiKey1, adiKey2); } } } @@ -479,35 +496,44 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton6ActionPerformed private void jButton7ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton7ActionPerformed - applyCompactTreeLayout(); + morph(new mxCircleLayout(graph)); }//GEN-LAST:event_jButton7ActionPerformed private void applyOrganicLayout() { - graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), - graphComponent.getHeight())); - new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); + + morph(new mxFastOrganicLayout(graph)); } - private void applyOrthogonalLayout() { - graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), - graphComponent.getHeight())); - new mxOrthogonalLayout(graph).execute(graph.getDefaultParent()); + + morph(new mxOrthogonalLayout(graph)); } private void applyHierarchicalLayout() { - graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), - graphComponent.getHeight())); - new mxHierarchicalLayout(graph).execute(graph.getDefaultParent()); + + morph(new mxHierarchicalLayout(graph));//.execute(graph.getDefaultParent()); } - private void applyCompactTreeLayout() { - graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), - graphComponent.getHeight())); - new mxCompactTreeLayout(graph).execute(graph.getDefaultParent()); - } + private void morph(mxIGraphLayout layout) { + // layout using morphing + graph.getModel().beginUpdate(); + try { + layout.execute(graph.getDefaultParent()); + } finally { + mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); + morph.addListener(mxEvent.DONE, new mxIEventListener() { + @Override + public void invoke(Object sender, mxEventObject event) { + graph.getModel().endUpdate(); + // fitViewport(); + } + }); + + morph.startAnimation(); + } + } // Variables declaration - do not modify//GEN-BEGIN:variables private JButton jButton1; From f49bf59f9189c4ef69e40bc79835f4b46aa83b4b Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 17 Jan 2018 13:06:31 +0100 Subject: [PATCH 024/128] don't add edges with no relationships --- .../autopsy/communications/Bundle.properties | 3 +- .../communications/VisualizationPanel.form | 13 +++ .../communications/VisualizationPanel.java | 102 +++++++++++------- 3 files changed, 76 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 61ee014750..4935475284 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -15,7 +15,7 @@ FiltersPanel.refreshButton.text=Refresh FiltersPanel.deviceRequiredLabel.text=Select at least one. FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. -VisualizationPanel.jButton1.text=Organic +VisualizationPanel.jButton1.text=Fast Organic CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize VisualizationPanel.jButton2.text=pan CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse @@ -26,3 +26,4 @@ VisualizationPanel.jButton4.text=- VisualizationPanel.jButton5.text=+ VisualizationPanel.jButton6.text=Hierarchy VisualizationPanel.jButton7.text=Circle +VisualizationPanel.jButton8.text=Organic diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index da37189ec5..2facc5f66e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -89,6 +89,19 @@ + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 69129f8e4a..6accd8965c 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -25,6 +25,7 @@ import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.layout.mxCircleLayout; import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.mxIGraphLayout; +import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; @@ -91,15 +92,15 @@ import org.sleuthkit.datamodel.TskCoreException; * actions to work correctly. */ final public class VisualizationPanel extends JPanel implements Lookup.Provider { - + private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); - + static final private ImageIcon imageIcon = new ImageIcon("images/icons8-neural-network.png"); - + 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); @@ -107,23 +108,23 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ENDARROW, mxConstants.NONE); } - + 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 nodeMap = new HashMap<>(); private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); - + private CommunicationsManager commsManager; private final HashSet pinnedAccountDevices = new HashSet<>(); private CommunicationsFilter currentFilter; private final mxRubberband rubberband; - + public VisualizationPanel() { initComponents(); graph = new mxGraph(); @@ -174,31 +175,31 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); } }); - + jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY()); } } } }); - + splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); //feed selection to explorermanager graph.getSelectionModel().addListener(null, new SelectionListener()); } - + @Override public Lookup getLookup() { return proxyLookup; } - + private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); final mxCell computeIfAbsent = nodeMap.computeIfAbsent(name, vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; - + mxCell vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), vertexName, accountDeviceInstanceKey, @@ -211,7 +212,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); return computeIfAbsent; } - + @SuppressWarnings("unchecked") private void addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { mxCell vertex1 = getOrCreateVertex(account1); @@ -229,7 +230,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); } } - + @Subscribe void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); @@ -249,10 +250,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // applyOrganicLayout(); } - + @Subscribe void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { - + graph.getModel().beginUpdate(); try { clearGraph(); @@ -267,7 +268,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // applyOrganicLayout(); } - + private void rebuildGraph() throws TskCoreException { /** @@ -304,31 +305,33 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider adiKey1.getAccountDeviceInstance(), adiKey2.getAccountDeviceInstance(), currentFilter); - addEdge(relationships, adiKey1, adiKey2); + if (relationships.size() > 0) { + addEdge(relationships, adiKey1, adiKey2); + } } } } - + private void clearGraph() { nodeMap.clear(); edgeMap.clear(); graph.removeCells(graph.getChildVertices(graph.getDefaultParent())); } - + @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 { @@ -346,10 +349,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } else { commsManager = null; } - + }); } - + @Override public void removeNotify() { super.removeNotify(); @@ -371,6 +374,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jButton2 = new JButton(); jButton6 = new JButton(); jButton1 = new JButton(); + jButton8 = new JButton(); jButton3 = new JButton(); jButton7 = new JButton(); jButton4 = new JButton(); @@ -418,6 +422,17 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); jToolBar1.add(jButton1); + jButton8.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton8.text")); // NOI18N + jButton8.setFocusable(false); + jButton8.setHorizontalTextPosition(SwingConstants.CENTER); + jButton8.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton8.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jButton8ActionPerformed(evt); + } + }); + jToolBar1.add(jButton8); + jButton3.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton3.text")); // NOI18N jButton3.setFocusable(false); jButton3.setHorizontalTextPosition(SwingConstants.CENTER); @@ -475,11 +490,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton2ActionPerformed private void jButton1ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - applyOrganicLayout(); + applyFastOrganicLayout(); }//GEN-LAST:event_jButton1ActionPerformed private void jButton3ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed - + applyOrthogonalLayout(); }//GEN-LAST:event_jButton3ActionPerformed @@ -499,22 +514,26 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider morph(new mxCircleLayout(graph)); }//GEN-LAST:event_jButton7ActionPerformed - private void applyOrganicLayout() { - + private void jButton8ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton8ActionPerformed + mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph); + mxOrganicLayout.setMaxIterations(10); + morph(mxOrganicLayout); + }//GEN-LAST:event_jButton8ActionPerformed + + private void applyFastOrganicLayout() { morph(new mxFastOrganicLayout(graph)); - } - + private void applyOrthogonalLayout() { - + morph(new mxOrthogonalLayout(graph)); } - + private void applyHierarchicalLayout() { - - morph(new mxHierarchicalLayout(graph));//.execute(graph.getDefaultParent()); + + morph(new mxHierarchicalLayout(graph)); } - + private void morph(mxIGraphLayout layout) { // layout using morphing graph.getModel().beginUpdate(); @@ -523,14 +542,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } finally { mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); morph.addListener(mxEvent.DONE, new mxIEventListener() { - + @Override public void invoke(Object sender, mxEventObject event) { graph.getModel().endUpdate(); // fitViewport(); } }); - + morph.startAnimation(); } } @@ -543,15 +562,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JButton jButton5; private JButton jButton6; private JButton jButton7; + private JButton jButton8; private JPanel jPanel1; private JToolBar jToolBar1; private JSplitPane splitPane; // End of variables declaration//GEN-END:variables private class SelectionListener implements mxEventSource.mxIEventListener { - + @Override - + @SuppressWarnings("unchecked") public void invoke(Object sender, mxEventObject evt) { Object[] selectionCells = graph.getSelectionCells(); From 6260c8d842ddf53126237a1d147a0b482a1a86f6 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 18 Jan 2018 10:26:01 +0100 Subject: [PATCH 025/128] add pinned status icon; actions for adding to pinned accounts, unpinning; refined zooming buttons; refined styling and polish of visualization; improve layout animation --- .../AccountDeviceInstanceNode.java | 33 +- .../autopsy/communications/Bundle.properties | 7 +- .../autopsy/communications/CVTEvents.java | 14 + .../communications/VisualizationPanel.form | 62 ++-- .../communications/VisualizationPanel.java | 304 ++++++++++-------- .../images/icons8-mind-map-16.png | Bin 0 -> 663 bytes .../communications/images/magnifier-left.png | Bin 0 -> 1106 bytes .../images/magnifier-zoom-actual-equal.png | Bin 0 -> 1147 bytes .../images/magnifier-zoom-actual.png | Bin 0 -> 1189 bytes .../images/magnifier-zoom-fit.png | Bin 0 -> 1215 bytes .../images/magnifier-zoom-in-green.png | Bin 0 -> 1271 bytes .../images/magnifier-zoom-in.png | Bin 0 -> 1225 bytes .../images/magnifier-zoom-out-red.png | Bin 0 -> 1053 bytes .../images/magnifier-zoom-out.png | Bin 0 -> 1150 bytes .../communications/images/magnifier-zoom.png | Bin 0 -> 1079 bytes .../communications/images/magnifier.png | Bin 0 -> 615 bytes .../images/magnifier_zoom_in.png | Bin 0 -> 680 bytes .../images/magnifier_zoom_out.png | Bin 0 -> 657 bytes .../communications/images/marker--arrow.png | Bin 0 -> 672 bytes .../images/marker--exclamation.png | Bin 0 -> 695 bytes .../communications/images/marker--minus.png | Bin 0 -> 567 bytes .../communications/images/marker--pencil.png | Bin 0 -> 660 bytes .../communications/images/marker--pin.png | Bin 0 -> 497 bytes .../communications/images/marker--plus.png | Bin 0 -> 656 bytes .../autopsy/communications/images/marker.png | Bin 0 -> 622 bytes 25 files changed, 250 insertions(+), 170 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/icons8-mind-map-16.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-left.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual-equal.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier_zoom_in.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier_zoom_out.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--arrow.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--exclamation.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--minus.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--pencil.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--pin.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--plus.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index c0208b1bce..eec27f30e5 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -24,7 +24,6 @@ import java.util.Arrays; import java.util.Collection; import javax.swing.AbstractAction; import javax.swing.Action; -import javax.swing.ImageIcon; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Sheet; @@ -104,6 +103,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { public Action[] getActions(boolean context) { ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); actions.add(PinAccountsAction.getInstance()); + actions.add(ResetAndPinAccountsAction.getInstance()); return actions.toArray(new Action[actions.size()]); } @@ -114,16 +114,39 @@ final class AccountDeviceInstanceNode extends AbstractNode { static private class PinAccountsAction extends AbstractAction { private static final long serialVersionUID = 1L; - private static PinAccountsAction instance = new PinAccountsAction(); - static final private ImageIcon imageIcon = - new ImageIcon("images/icons8-neural-network.png"); + private final static PinAccountsAction instance = new PinAccountsAction(); private static PinAccountsAction getInstance() { return instance; } private PinAccountsAction() { - super("Visualize Account", imageIcon); + super("Add Account(s) to Visualization"); + } + + @Override + public void actionPerformed(ActionEvent e) { + Collection lookupAll = + Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class); + CVTEvents.getCVTEventBus().post(new CVTEvents.PinAccountsEvent(lookupAll, false)); + } + } + + /** + * Action that pins the selected AccountDeviceInstances to the + * visualization. + */ + static private class ResetAndPinAccountsAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + private final static ResetAndPinAccountsAction instance = new ResetAndPinAccountsAction(); + + private static ResetAndPinAccountsAction getInstance() { + return instance; + } + + private ResetAndPinAccountsAction() { + super("Visualize Account(s)"); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 4935475284..0161fd855c 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -17,13 +17,12 @@ FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. VisualizationPanel.jButton1.text=Fast Organic CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize -VisualizationPanel.jButton2.text=pan CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualize CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize -VisualizationPanel.jButton3.text=Orthogonal -VisualizationPanel.jButton4.text=- -VisualizationPanel.jButton5.text=+ VisualizationPanel.jButton6.text=Hierarchy VisualizationPanel.jButton7.text=Circle VisualizationPanel.jButton8.text=Organic +VisualizationPanel.zoomOutButton.text= +VisualizationPanel.zoomInButton.text= +VisualizationPanel.fitGraphButton.text= diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java index f13128f5c3..e0f945bb69 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.EventBus; import java.util.Collection; +import java.util.Set; import org.sleuthkit.datamodel.CommunicationsFilter; /** @@ -69,4 +70,17 @@ final class CVTEvents { this.replace = replace; } } + + static final class UnpinAccountsEvent { + + private final ImmutableSet accountDeviceInstances; + + public ImmutableSet getAccountDeviceInstances() { + return accountDeviceInstances; + } + + public UnpinAccountsEvent(Set accountDeviceInstances) { + this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 2facc5f66e..0e1d7ff407 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -50,19 +50,6 @@ - - - - - - - - - - - - - @@ -102,19 +89,6 @@ - - - - - - - - - - - - - @@ -128,30 +102,54 @@ - + + + + + + - + - + - + + + + - + - + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 6accd8965c..af8d915f65 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -26,18 +26,19 @@ import com.mxgraph.layout.mxCircleLayout; import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.mxIGraphLayout; import com.mxgraph.layout.mxOrganicLayout; -import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; import com.mxgraph.swing.handler.mxRubberband; import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.util.mxCellOverlay; import com.mxgraph.swing.util.mxMorphing; import com.mxgraph.util.mxConstants; import com.mxgraph.util.mxEvent; import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; -import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.util.mxPoint; import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; @@ -45,6 +46,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; import java.beans.PropertyVetoException; import java.util.ArrayList; import java.util.Arrays; @@ -92,39 +94,48 @@ import org.sleuthkit.datamodel.TskCoreException; * actions to work correctly. */ final public class VisualizationPanel extends JPanel implements Lookup.Provider { - + private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); - - static final private ImageIcon imageIcon = - new ImageIcon("images/icons8-neural-network.png"); - + + static final private ImageIcon pinIcon = + new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png")); + static final private ImageIcon addPinIcon = + new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--plus.png")); + static final private ImageIcon unpinIcon = + new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--minus.png")); + 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.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "#000000"); + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE); + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "000000"); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); +// mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ROUNDED, true); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_PERIMETER_SPACING, 0); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ENDARROW, mxConstants.NONE); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE); } - + 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 nodeMap = new HashMap<>(); private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); - + private CommunicationsManager commsManager; private final HashSet pinnedAccountDevices = new HashSet<>(); private CommunicationsFilter currentFilter; private final mxRubberband rubberband; - + public VisualizationPanel() { initComponents(); graph = new mxGraph(); @@ -142,7 +153,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setAllowDanglingEdges(false); graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); + graph.setResetEdgesOnMove(true); graph.setStylesheet(mxStylesheet); + graphComponent = new mxGraphComponent(graph); graphComponent.setAutoExtend(true); graphComponent.setAutoScroll(true); @@ -158,61 +171,92 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider //right click handler graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + super.mouseWheelMoved(e); + if (e.getPreciseWheelRotation() > 0) { + graphComponent.zoomIn(); + } else if (e.getPreciseWheelRotation() < 0) { + graphComponent.zoomOut(); + } + + } + @Override public void mouseClicked(MouseEvent e) { super.mouseClicked(e); if (SwingUtilities.isRightMouseButton(e)) { mxICell cellAt = (mxICell) graphComponent.getCellAt(e.getX(), e.getY()); if (cellAt != null && cellAt.isVertex()) { + JPopupMenu jPopupMenu = new JPopupMenu(); - jPopupMenu.add(new JMenuItem(imageIcon) { - { - setAction(new AbstractAction("Pin Account " + graph.getLabel(cellAt)) { - @Override - public void actionPerformed(ActionEvent e) { - handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); - } - }); - } - }); - + + if (pinnedAccountDevices.contains(cellAt.getValue())) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin Account " + graph.getLabel(cellAt), unpinIcon) { + @Override + public void actionPerformed(ActionEvent e) { + handleUnPinEvent(new CVTEvents.UnpinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()))); + } + })); + } else { + + jPopupMenu.add(new JMenuItem(new AbstractAction("Pin Account " + graph.getLabel(cellAt), addPinIcon) { + @Override + public void actionPerformed(ActionEvent e) { + handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); + } + })); + jPopupMenu.add(new JMenuItem(new AbstractAction("Reset and Pin Account " + graph.getLabel(cellAt), pinIcon) { + @Override + public void actionPerformed(ActionEvent e) { + handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), true)); + } + })); + } + jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY()); } } } }); - + splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); //feed selection to explorermanager graph.getSelectionModel().addListener(null, new SelectionListener()); } - + @Override public Lookup getLookup() { return proxyLookup; } - + private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); - final mxCell computeIfAbsent = nodeMap.computeIfAbsent(name, vertexName -> { + final mxCell vertex = nodeMap.computeIfAbsent(name, vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; - - mxCell vertex = (mxCell) graph.insertVertex( + + mxCell newVertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), vertexName, accountDeviceInstanceKey, Math.random() * graphComponent.getWidth(), Math.random() * graphComponent.getHeight(), size, size); - graph.getView().getState(vertex, true).setLabel(vertexName); - return vertex; + graph.getView().getState(newVertex, true).setLabel(vertexName); + + return newVertex; }); - return computeIfAbsent; + if (pinnedAccountDevices.contains(accountDeviceInstanceKey)) { + graphComponent.addCellOverlay(vertex, new mxCellOverlay(pinIcon, "pinned")); + } else { + graphComponent.removeCellOverlays(vertex); + } + return vertex; } - + @SuppressWarnings("unchecked") private void addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { mxCell vertex1 = getOrCreateVertex(account1); @@ -230,7 +274,25 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); } } - + + @Subscribe + void handleUnPinEvent(CVTEvents.UnpinAccountsEvent pinEvent) { + graph.getModel().beginUpdate(); + try { + + pinnedAccountDevices.removeAll(pinEvent.getAccountDeviceInstances()); + clearGraph(); + rebuildGraph(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error pinning accounts", ex); + } finally { + // Updates the display + graph.getModel().endUpdate(); + } + +// applyOrganicLayout(); + } + @Subscribe void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); @@ -250,10 +312,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // applyOrganicLayout(); } - + @Subscribe void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { - + graph.getModel().beginUpdate(); try { clearGraph(); @@ -268,7 +330,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // applyOrganicLayout(); } - + private void rebuildGraph() throws TskCoreException { /** @@ -311,27 +373,27 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } } - + private void clearGraph() { nodeMap.clear(); edgeMap.clear(); graph.removeCells(graph.getChildVertices(graph.getDefaultParent())); } - + @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 { @@ -349,10 +411,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } else { commsManager = null; } - + }); } - + @Override public void removeNotify() { super.removeNotify(); @@ -371,14 +433,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider splitPane = new JSplitPane(); jPanel1 = new JPanel(); jToolBar1 = new JToolBar(); - jButton2 = new JButton(); jButton6 = new JButton(); jButton1 = new JButton(); jButton8 = new JButton(); - jButton3 = new JButton(); jButton7 = new JButton(); - jButton4 = new JButton(); - jButton5 = new JButton(); + jSeparator1 = new JToolBar.Separator(); + zoomOutButton = new JButton(); + fitGraphButton = new JButton(); + zoomInButton = new JButton(); setLayout(new BorderLayout()); @@ -389,17 +451,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jToolBar1.setRollover(true); - jButton2.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N - jButton2.setFocusable(false); - jButton2.setHorizontalTextPosition(SwingConstants.CENTER); - jButton2.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton2.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - jButton2ActionPerformed(evt); - } - }); - jToolBar1.add(jButton2); - jButton6.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton6.text")); // NOI18N jButton6.setFocusable(false); jButton6.setHorizontalTextPosition(SwingConstants.CENTER); @@ -433,17 +484,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); jToolBar1.add(jButton8); - jButton3.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton3.text")); // NOI18N - jButton3.setFocusable(false); - jButton3.setHorizontalTextPosition(SwingConstants.CENTER); - jButton3.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton3.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - jButton3ActionPerformed(evt); - } - }); - jToolBar1.add(jButton3); - jButton7.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton7.text")); // NOI18N jButton7.setFocusable(false); jButton7.setHorizontalTextPosition(SwingConstants.CENTER); @@ -454,28 +494,43 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } }); jToolBar1.add(jButton7); + jToolBar1.add(jSeparator1); - jButton4.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton4.text")); // NOI18N - jButton4.setFocusable(false); - jButton4.setHorizontalTextPosition(SwingConstants.CENTER); - jButton4.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton4.addActionListener(new ActionListener() { + zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N + zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N + zoomOutButton.setFocusable(false); + zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER); + zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM); + zoomOutButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - jButton4ActionPerformed(evt); + zoomOutButtonActionPerformed(evt); } }); - jToolBar1.add(jButton4); + jToolBar1.add(zoomOutButton); - jButton5.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton5.text")); // NOI18N - jButton5.setFocusable(false); - jButton5.setHorizontalTextPosition(SwingConstants.CENTER); - jButton5.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton5.addActionListener(new ActionListener() { + fitGraphButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N + fitGraphButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitGraphButton.text")); // NOI18N + fitGraphButton.setFocusable(false); + fitGraphButton.setHorizontalTextPosition(SwingConstants.CENTER); + fitGraphButton.setVerticalTextPosition(SwingConstants.BOTTOM); + fitGraphButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - jButton5ActionPerformed(evt); + fitGraphButtonActionPerformed(evt); } }); - jToolBar1.add(jButton5); + jToolBar1.add(fitGraphButton); + + zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N + zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N + zoomInButton.setFocusable(false); + zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER); + zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM); + zoomInButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + zoomInButtonActionPerformed(evt); + } + }); + jToolBar1.add(zoomInButton); jPanel1.add(jToolBar1, BorderLayout.NORTH); @@ -484,30 +539,25 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider add(splitPane, BorderLayout.CENTER); }// //GEN-END:initComponents - private void jButton2ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed -// graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); - graphComponent.setPanning(true); - }//GEN-LAST:event_jButton2ActionPerformed - private void jButton1ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - applyFastOrganicLayout(); + morph(new mxFastOrganicLayout(graph) { + @Override + public boolean isVertexMovable(Object vertex) { + return super.isVertexMovable(vertex) && false == pinnedAccountDevices.contains((AccountDeviceInstanceKey) ((mxICell) vertex).getValue()); + } + }); }//GEN-LAST:event_jButton1ActionPerformed - private void jButton3ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed - - applyOrthogonalLayout(); - }//GEN-LAST:event_jButton3ActionPerformed - - private void jButton5ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed + private void zoomInButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed graphComponent.zoomIn(); - }//GEN-LAST:event_jButton5ActionPerformed + }//GEN-LAST:event_zoomInButtonActionPerformed - private void jButton4ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed + private void zoomOutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed graphComponent.zoomOut(); - }//GEN-LAST:event_jButton4ActionPerformed + }//GEN-LAST:event_zoomOutButtonActionPerformed private void jButton6ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton6ActionPerformed - applyHierarchicalLayout(); + morph(new mxHierarchicalLayout(graph)); }//GEN-LAST:event_jButton6ActionPerformed private void jButton7ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton7ActionPerformed @@ -519,21 +569,21 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxOrganicLayout.setMaxIterations(10); morph(mxOrganicLayout); }//GEN-LAST:event_jButton8ActionPerformed - - private void applyFastOrganicLayout() { - morph(new mxFastOrganicLayout(graph)); + + private void fitGraphButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitGraphButtonActionPerformed + fitGraph(); + }//GEN-LAST:event_fitGraphButtonActionPerformed + + private void fitGraph() { + mxGraphView view = graphComponent.getGraph().getView(); + view.setTranslate(new mxPoint(-view.getGraphBounds().getX(), -view.getGraphBounds().getY())); + +// final double widthFactor = (double) graphComponent.getWidth() / (int) view.getGraphBounds().getWidth(); +// final double heightFactor = (double) graphComponent.getHeight() / (int) view.getGraphBounds().getHeight(); +// +// view.setScale(Math.min(widthFactor, heightFactor) * view.getScale()); } - - private void applyOrthogonalLayout() { - - morph(new mxOrthogonalLayout(graph)); - } - - private void applyHierarchicalLayout() { - - morph(new mxHierarchicalLayout(graph)); - } - + private void morph(mxIGraphLayout layout) { // layout using morphing graph.getModel().beginUpdate(); @@ -541,37 +591,33 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider layout.execute(graph.getDefaultParent()); } finally { mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); - morph.addListener(mxEvent.DONE, new mxIEventListener() { - - @Override - public void invoke(Object sender, mxEventObject event) { - graph.getModel().endUpdate(); - // fitViewport(); - } + morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { + graph.getModel().endUpdate(); +// fitGraph(); }); - + morph.startAnimation(); } } // Variables declaration - do not modify//GEN-BEGIN:variables + private JButton fitGraphButton; private JButton jButton1; - private JButton jButton2; - private JButton jButton3; - private JButton jButton4; - private JButton jButton5; private JButton jButton6; private JButton jButton7; private JButton jButton8; private JPanel jPanel1; + private JToolBar.Separator jSeparator1; private JToolBar jToolBar1; private JSplitPane splitPane; + private JButton zoomInButton; + private JButton zoomOutButton; // End of variables declaration//GEN-END:variables private class SelectionListener implements mxEventSource.mxIEventListener { - + @Override - + @SuppressWarnings("unchecked") public void invoke(Object sender, mxEventObject evt) { Object[] selectionCells = graph.getSelectionCells(); diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/icons8-mind-map-16.png b/Core/src/org/sleuthkit/autopsy/communications/images/icons8-mind-map-16.png new file mode 100644 index 0000000000000000000000000000000000000000..4d9188c24518a204fa39ef11bd7c3734bd319f4a GIT binary patch literal 663 zcmV;I0%-k-P)E~JSA^7~fRx!6Lk{2l z^5gFNzyBN7-C2iC4HG^C7@3%u{xdK#FfcGM{A2i!RV@oX17;`duKGLcyf+so2kXZ# zzY1_FbZ$K%?%aAp9Gjd+$FX#L>RGbpU5T{R6zyU7_n$#y&XwY+DLN~V761Rw$cW5G zSI;EC%jROEDJo^7Ehfdr!JOvSa!Qtgfq@}<=Joi3Rgcc9DN0nv&A1xFz`(%Z)^bXg zgPl1Qrk))iAtY3|wo|1&V$eEKQ<#mBE4fB!SesL4sR>8Q%dF)%PNT)y?_ z;oV0sYZ#gSJ(dvQ{jDL*GYzUf3B`HHAaB)!i&{#udJGH<4A<{IxiB$Y-VmEQoU!oc z?dQ3d|2`^aVqpC7>dl88xYXfH2X8-pW;*fdt;*y`1@#Z#zCHEpJi<$~0q*U`R2cp- zJwBUm@|%Hyfq{wh!|b07O!3&%qJ%-@gcIUo!u;*;zy1_ml&^R@{eS50m!AoUZP-E!df9C%TPzU{isAFJ2i-kLn-=RTaHVVaSii z<3SWfA&jy11c(49nAQP|OJ~D?X-Fstm?eqHvW#*i==b}d5~K+a*lI4c)xNrQr^8#*LEpTE7?<5pFL8tS`1+SZqgfF?iC%XI}}k=y;&9v2BtbUL2F45sMkG6J1m;FxWV zd#a(8ZsIFR63T>B1#oEdmih(zb> zu&poKSXGp6V4+8A*YK>K$1KOtINcUcDAL`TeA;F{;Ol27$vy2GcwV2#b$` z3uOcr>s2Xg_}q6q@S3yy{mRP9O|V`XAcT1}yC@ou$Gf45@YUX)x&0q}=2`I9M>~_0$ zDdBJ!EiNuBn3z#r zF4rd{cD5;6CWWumBWvk)RNeTo_F<5e(cZ5#q)91Uve#fRD55A{9v>g?qh9uB%YOn4 Y0Q}V0z?sHwE&u=k07*qoM6N<$f>jC|UjP6A literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual-equal.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual-equal.png new file mode 100644 index 0000000000000000000000000000000000000000..52972052c5e9f68880c12353b5008eab9df665d8 GIT binary patch literal 1147 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 literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in.png new file mode 100644 index 0000000000000000000000000000000000000000..281af0cde172dac2a38734350dfd35d840881fde GIT binary patch literal 1225 zcmV;)1UCDLP)`wXRCe@1VJ{T#-aUC3T1YRl!9dpw!uD(Q9lLNIfl5+IR)vQ z)R49j9F$om&iZHT|1C+Iyd>}Cy|;6pP1Z`nKBxzN?;h?w=Xc&c@7#MHq3b$&nL^`7 zU0oeoTwFwvNCer6-auOqoX;3g6omxAj98Y%`WBKTJ7BsA;9fDA0{Ase8&p*_D9bXE zq!*fur&uTK9SlvJ}*3C07*rLP5Uf+_jzi-7Fx+Ca~6GyGg){)h?2& zcG0M=I)4AV;pMH^LecfL{Orxq1zZe4BS1VZ&2DCGcrqXKv`?vn&o2-; z4(m&CB{cch(tJ&oC4BA1qo&Lbl1U|30Jp~_F2JZ`^425Q%AYlOt?d#{k&&jci~BSf z<}1rZ9yr~Z0IHh%u+by|--*!w&u+z*Nc*I1jQ|8eHX+$_j?z2mQflp(JTK&r-%)C@D?FBg+Z~ zR!d6NG?XH^8>10B_RZ|T9y*h22lo^CUAG5^obC_aFM$jzL_$$DoQl9VJ7!m>1r|w~ zAWefq^ALyskzv$eW(PUM0ryXHg>XHjCSwhsoc~*AbXqX6lvUMqIhsh#Ehkd5z!o<# zlsOScLjMeZE(17s+fRRn0#~YAk n9U7HlG=1Zqd)dD&{|YbwP~{7JH}}k800000NkvXXu0mjf$H7Yd literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png new file mode 100644 index 0000000000000000000000000000000000000000..04446b048ca454541a9bc74b5f34200a35afe041 GIT binary patch literal 1053 zcmV+&1mgRNP)FL;JH_4ypkk+S{tCg zj#ee@U`X;XneOu1*dK8>mhzcPqnlN?R;)GCG+e_sa?#JbtysFmgu@x4Y7m0097C3MG`F2Y8eq z>|#{vZg*!)BiD2A_|+Bw09sBUpU)3I9ri6(xri(T2mpZ1yjh%CGB-+boK8ZU_uE&# zolFe^K%#;`u5I5Q$07jOy-wNVb;uq70GVL0$%H5r|B1hmH}#pNjn&8ZIx}C6-E6D4 zL7`xVb_ZQ401#e>>{(PJ4dw97$s`AW)jbU^d!b;4D*i#SD1c&w0nj7anc{mmiV-dr z1ytMsAu>0&m^JtAcFX#fk*S~j5qvI1MkVZBuy_^#V16NMB1GmY3Sg{^C#N?e&HKGv z&l(v$Yh)yXT^K!FfC~_cMC9bmrW0djyy6o{l07z_$og|RF6=T<002yj7fa!-BIjTs zWo&ZYD@pbk0NnF-scQ1w$Xjx_WnsnPi?(_d0U#(r!X}Xico-oJ0@>e+0$|j}07sVf6F&FWyY~3lrCC8n<##aN3Evvqtg|jM?mbvGB!r zdHUYR>ue0_s9nxAuiI2dd=aPs7-7l((>ew0Z3@J)5&(aTM(!y$wo zey)Y!v9Sw*L|Gqenif!1^+a!P-^i6K6QZNzP@FL~NGbLI>*UciZI`O5r~3N(MB1bY-DO`R&;iD#TjF-P)cjJB{x-7eR*Ku#K`pYucE8# z**IhDbxLX7wgqz}5_xTQc1?75_rw`vZ&6Bv+Zy`eaQK;YIvv$@of?L5-mF00000NkvXXu0mjfQxfOs literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out.png new file mode 100644 index 0000000000000000000000000000000000000000..c11249f5286caa237c1128a6e500fc0e6e9619fb GIT binary patch literal 1150 zcmV-^1cCdBP)4vp`NX3u&k1!B~2{j#~f7C*mog$^6jKMb8A7jM70_%h!8{0%d zs*@VhHiLbXttf8sW45*JmX9VcXR&jj{^S$SP^H>g6&Q~LgJEmug7hw2}l^`_vSZZHKyuAqlke;4(t}D7a+f7NcNU@lvP^FDG zJ-rG~waK=>Lr7KFi!S0re9Hl{*{rTglC-^0koN6qxBkUogo&-lGnl~?{mfy)m{nxh z&URZ;E=c={Z+VOI`MePcx>~omX4Y9T2V^VJZ80Y+QxD~|k%Ps8ndT;8_Qu#eAw#Sb zAeWPj?Ataqm>)nZz=7HwX3kCqIZPuHhcVC4t4Na0$%(0l>A6OqC3)rgy|!`&OG~m* z0FT#gorhK<;GIXW)cqUh$1fgKp_yx_vkHLeDF;v#<6>iIvfq^UUv|s3)QZoFZ7Bdn zVI!Y-{6R+cd2L)GTS#90Y%g+Ic@9u0J>#XKnj!7o`xzN9@Y05ArU1eDz6p3Z3BuUi-4wqIT7gfb*HAO{L zQIX=WkEI;Mx150^aoWh|PQIe|`_Qn<^Uhl}u){Kymlc&~pD678h(J#pgK3@sQXo*-jETkOd=802pFC1;-$gSR za|57{4_QZC0N$~2V{OOLBM0C7@ZC zUA~zWa(Q}$fGdi;`m%dUssMbC+gA&U))5d-YDwNL1?@iAqpJ zQq{6OVa^RrV?(foG2Qpz38@C~z4i6pm%F=rf`fza67ZXF$6toI1){JkF};+H-mNDC zYyj{BUazmUr>8%7>QrBNe0*#Iz`r5YxuzElqz1qb`FxFA`ua`>`}^MxN28G+0sKpt zn_csh3E+qQ{>@D%PYwlpdyj`BkiDVrn|j`VTmBYc0P$hN7tzCR Q_y7O^07*qoM6N<$f*p@6M*si- literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom.png new file mode 100644 index 0000000000000000000000000000000000000000..a705d33d9231f2ca64f90a4505422f45bec80935 GIT binary patch literal 1079 zcmV-71jze|P)V!R}{W;?>u&9vuie4 z(^$42Td)HBFtBjc)}A7 zzw|Ld=TUfJdxLk9YU(Z)YnLdO%Q=;&scwURbeV^U&}>$_&19)Q^q5XN1r!w#{=K#G zSa1IXYlftiAe&X4{Oea&yC1?`o>#CMm3kz>EfGQFF9@wB?8rfBlgmHYMf z3T9`iQ-WY9;F*BCy;&;!3RGz^@xn5(dv&Ep2F{?JK-Zl&8;@BK2M%*P(uv&KGk7im zi<#%N6dXsKFg*M)P3Hq&Rqj1VQx7qd|nXrq`d+MDS`8+p-TB9rO#$+zy0Z%DVV(C z5{Z{5SYm`62z(#jB@lNmiguG1wCyD}i(76?>+bm-9G zNLN>9ESVg*TvFw^P+Yz_*po1udJ-yZBDL&=e;UwhnnDyxm@GIC7Dzde@2IK@eR$+Z zbEKo=D@J||&iIR9d&&S+NfuhN)7)B^UEFSM00`gzL zH1bAfGNn{Jb?TeQ(W3`qeSKH|0rGB8KYC+B9|#j$w$yf{QYjvuW1xDlsI4kDp-=X8 xSw8>_g#>221x+(yP*H2>Tj$){{%v_KzyRdFwPn-lWzqlu002ovPDHLkV1h5({M`Tm literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier.png new file mode 100644 index 0000000000000000000000000000000000000000..cf3d97f75e9cde9c143980d89272fe61fc2d64ee GIT binary patch literal 615 zcmV-t0+{`YP)gNuvOO$0ks zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+ z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8 z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@! zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0 B3FJNMp+(Bt!=q9U!ZZOlw$c zuAy5i+nTd|<_>NivLu&tYWf+obh7aHN%Hi45`pBR)x`tA#^U98gM4FFC6h~&)aWQw>e5Y84Gj%C?Fa5wL3#v12nvm3<6OafjJt}U((Qj zn8!nMmXr-qoCO7XcZRS8(x9RlIA>F^1(GoPldw}sc)rpQ>IL9yYf!7MN);5mno3dL zFr9-f3^@5I0h2d@QBNW#I`RB4IwvonO1T#W1?;?jrZNjp_!1ar;E|a)8g&BH^;Scq zt%uAgf}pb+yKn5ouFDnCJb}hGpY=s(m>77B`PIn4hUqw48S;@<+#YViwZYT4_>vEC z?=frJc<3Fn+HA3jXTwUklhgJ-dYkmNL^YBTW!uzZM O00008VI0S0!CoiyA}k0DS`b78y(oGU6a>AkUK9~n*^7|=kr7cwRFWv6%Z}9bXJxHq zv!bQ7t!|lawm;5>vpILy?iOC&HfG!Hc8BBJ-HKqfMb?WCJa0aq=l6SfssL2|4?ho1 zYF_hZS|)5^i5(2(3eSVtLjTwt+viCU4@P*+9|NQ!zmCYHkC8wnWH2cSOj5$smpmL3 zY1}4f$SympsTgoXWWvkj!KP${W<>az96Yfc0&EV^Sqav$1oSUxqGI6Xq{vddRFu7n z`2LdsHzi;=Dtr$y0$QI$*opyl-1%;k%_tFkSWDaJm;MHp-}I<6Z; z`=uSavTxAsh-+>P#z@O32VYD;@Uw_<1$Q)qo>w&5O)gAWuE2V33*Ucwqps{ny7nxp zX|aTf%a1~AQ*W?v_D-PC*yD@0=#7-ucnX}S5B~d&FdbXR-#fBe_gkP615I1CPtR;N z4-*FwcN*ZjSr5aZ75rZZR34vLukCGEW45>LLezQ{#QvpNP(bBYeXX{uZkgF|xEA`o r)y)eIsC4dIvZ!Ov;+nFL_^*5eZM*&99sh^s^i~2rXbB>z#ZtkF!9$9ORD+0!@uy{_ zwuz9nWs~h@V|Jf2yKcxXwgVsY=9@PkGxHwD7z4+)d7fMsB7r!GXh&2L9}x2h89e6q z#65^xfzi>(p;)ZX*VYy^biJ%*v&SkcD-q01BMJuz#DfzP{lS5OD3fKN-ms@&$MEne zuBF9aUS5tPb+@^IZw?G>TNoS+o8>a3QQ(RsS)O9zDR0>K8gL2JaAuC@1!frVq*#Q4 zqFA0{;wfLV?=|2O&;csCEbmgm89@ME(=1Oh@mAix*MMsWxb8Q_)zuS+W3f=8yBi2( zthE)2*{nqQ1786zEEaOPv5+W++c~bo;k8OdLH;Km7k?Z*4au92LC5a)a5vs1mNdqe zzGpJu!0!jMUI+3NTYTS0iHi4q1qDAf)FUD^Ge!L{&;(rA+ z8!C>nkY8Kd1dappUylqe%w4;$e(5<5wQ5yFOgSQ4^H)w`!9NQk`~3XmM4X literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/marker--exclamation.png b/Core/src/org/sleuthkit/autopsy/communications/images/marker--exclamation.png new file mode 100644 index 0000000000000000000000000000000000000000..b225a567b382355128ef59bf63c2ebc2ea84624f GIT binary patch literal 695 zcmV;o0!aOdP)X( zGJS~B8ef#OwY9C{Vk*`}Z7FVCltK~x3@*gAxX_PK5L_k{r7B%ym8>L?g`iN;ltA;c zI#n_gl1VbTo;$H26KF5|IGo4*&$(yrC6rP)T1AdyCX8$#lZc4ak+hJAZxL5P#PwefJ}g_zpY| z?N$qz&88ArZqa34Q8(uZ;TQ1gi0t|>90C=L)G3V>v)O&EP&i~~XZCPlkA~*%xJ1#f z1eJxWRvYfN@M{c*K&?X+j}qTar}u#n_?AiM-7BOqaPIbnwTCeX58b-dw#a>}1w7&u zsQ(A2`@WFRS1mk(7B29?sRTN)k5udB`D1P~oXToeU_8*nB#933tG zNyEnvPvBR57lhDY1XB;1O}rkx0!=4eMLq!O;%SNUg|M5`V0q;wNP-SN$qWmkZxTgr zIs|ARMI{R((V*uPR|20k@7DNO=`~yNA930$SogD;!f{Pg(Dx%b(gHxSE=pZgWq0lE{iZ-pW)%5e+)RcU& zUN}A6N6vS?n??~rkP|soW$Fhazy#0%d;&Ls2I$W0;{s7?Ns!_Z7zFKhFE5XEg~Ox1 z_V$ofts0d?;=UY@-=enzc}t_ew?hC3^&8WfgAr-cILOC_5*=Q{UmjkzWy zfhtkCs_Sq3z+x~+s%hHHIoIueW3CBl2Uz!2;qr2PC>-vK3=9y1Lj(d;NF-9+_v}n| zfPs^IGMV`(mrH!A1J1e5edlBYc)%O7{9My z5-6egDMU2){G8Ua*&?6L%sJO}^ou~v_1}>M%mym3dVF_%ohC&gX3n|(MSsJ)L?_O( znaq}jD}XB)T2Y9$y${1j@;?FIkxxJX3-jULBkPgKj91`L(_{x8iz@E`HMNM7_B5@P zec6HVTCCTd?_twyasSR1ACo%{j-pYUd-3!B&(Mzm0|0im4AhcuK!X4P002ovPDHLk FV1mN&0+Ij# literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/marker--pencil.png b/Core/src/org/sleuthkit/autopsy/communications/images/marker--pencil.png new file mode 100644 index 0000000000000000000000000000000000000000..c18784e7cf00714b60bb72d00af053214a58fa13 GIT binary patch literal 660 zcmV;F0&D$=P)xv&t%*aJlNe+5RBp`nXPclQOM>p;C>Ps-80 zz8=Zvla2ZLQFJ}47qEJRW7~n=-VVnwAc_TUCK4`_O@4~=YMzTdZ-Fv?v&2|Ha1OCt z4zl^Y%Vd+E;^k@{I0(aApafu+bbXfuR{{ZG#iGk(li!VFo>V7nD1hVsl3QL*2)`f3 z+S?%(4!cY?`6*7nTWcVj{1ktyX_R`Y0uaMF^m~W6T@>Qo z&&9xcW?9y?$$0$9z|_?99U;aW3KXj-V5_t1D=S+dNkCjVC(X+DpvNk}ow@0*7pkg0 z{A$~;vEvWk8MwenzfY%91&*IU>>q(W5)TaDy*~45GOnJVPKj8lO}&M-^u0x7r=H6H3;V}zqVeKeb05CkA5 zO}+yF+jbBDxBaU3dLs{?Kumhsif`0R;rh6g00000NkvXXu0mjfyRpv7 literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/marker--plus.png b/Core/src/org/sleuthkit/autopsy/communications/images/marker--plus.png new file mode 100644 index 0000000000000000000000000000000000000000..cb98edc78ae1bd4c79064cf09091b9864d2feacf GIT binary patch literal 656 zcmV;B0&o3^P)1RCwBaQ(bG*U=%)Ynyj_j73Red z1#gNvwz;`%?6L}au`2b>o52ti2E%_Kx+`zQKTz;$s9?raCNiPo=&ocoN<-tN@(;1SrznVBGjxoyMCN(IXGy36EK zobvmfx*j|N7C^(&wBPjLaySfvX}U~4#oc_Tt_RN!aNQp&TU+N(#p6Q@!^1!z;{yXw z$z<}BKlBP%uvS*p#E>k1a{lU9I{gzw z5uA1#h{>n;o#kkC>3$UcdxbixHaS;HJ3G5tu~?(oiOK&sA}XIpq!ivZi>0Z53YZ-j zILhkx?d@G)42VCSlUAQxKVm?O%eSxGl!?LO2L+mcf!lpuEEEhIM<9Nl3|;#g6SH7B zuxIZ9F|>IWyGi+%*fkvy;3$A%fp4b+XD%f!%9N}iyT8r57G?)(5>>3ceK8HpB&(v>^pDBvViL?c8 q#(*BFrH4Q8v`j?+oD9Mr0R{lL<06*LsCZ%k0000&kijW3b2_y{?RmcE%+Miz+;LyA5TDSD9_t1+Mq z&5*VXn7taX2b@B8R~0!4?IHnk;A9Fjo4A5+z#RAstbtOgI^=-7CAb7gfs=;(XXQ&^ zqjep6NZLUKGoWb5odXl)GVSHH0opeEF;Qi6Q*Dk>LG~P;>HSHY1KI+tpBelL*v8CV z*DbCE#*c;ATWL?-rbTU7JkY#dh`Aq>p8>ytJ4wyI7@%1P09_~CyGmebG;nFa1xMb& z{GnOfsHJXf)_S&$5$G<38X%p*FV9K7dw`E3)I1~9XTZn^^-);ttZvYd`v{Dci`K)g zoTRBjV>wCIJ4x>(y_57#(mP4-B)yaLPV#=8B<}#I6X_-JptY;u(i7|8v+Q4g4AT-r zgvz)HT32RI7SZN97sxbH4(#pLt>+|{h9qgk?N;po4y0Fw0UJWH92hp_zUcW7SP5Xr z3~qrkX)6OS9Vu@--aNnr_$m!MlYbTgWN|(`oWiw7lAro$oofcG1INkD=>}Nj(yLSt zthtAfWYmzmmo7d?4@*xE4{@h;!tN$Cl*QIb#{9qX{REsUKL`E@=)wqgFpG;w9dvsO z;B*Q%UWA}$h($07*qo IM6N<$f*xZGIRF3v literal 0 HcmV?d00001 From b318327727c45e26fad537d159c2ee7322022e14 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 19 Jan 2018 15:42:08 +0100 Subject: [PATCH 026/128] add type and pin icons to vertex labels as html --- .../communications/VisualizationPanel.form | 4 +- .../communications/VisualizationPanel.java | 51 +++++++++++++------ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 0e1d7ff407..5085589359 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -11,14 +11,14 @@ - + - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index af8d915f65..be4ae4c388 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -30,13 +30,13 @@ import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; import com.mxgraph.swing.handler.mxRubberband; import com.mxgraph.swing.mxGraphComponent; -import com.mxgraph.swing.util.mxCellOverlay; import com.mxgraph.swing.util.mxMorphing; import com.mxgraph.util.mxConstants; import com.mxgraph.util.mxEvent; import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxPoint; +import com.mxgraph.view.mxCellState; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; @@ -48,6 +48,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.beans.PropertyVetoException; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -97,9 +98,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); - - static final private ImageIcon pinIcon = - new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png")); + private static final URL MARKER_PIN_URL = VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png"); + static final private ImageIcon pinIcon = new ImageIcon(MARKER_PIN_URL); static final private ImageIcon addPinIcon = new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--plus.png")); static final private ImageIcon unpinIcon = @@ -112,6 +112,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE); mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "000000"); +// mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_WHITE_SPACE, "wrap"); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); // mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ROUNDED, true); @@ -138,7 +139,27 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public VisualizationPanel() { initComponents(); - graph = new mxGraph(); + graph = new mxGraph() { + @Override + public String convertValueToString(Object cell) { + Object value = getModel().getValue(cell); + if (value instanceof AccountDeviceInstanceKey) { + final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value; + final String accountName = adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID(); + String iconFileName = Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()); + String label = "" + accountName; + if (pinnedAccountDevices.contains(adiKey)) { + label += ""; + } + return "" + label + ""; + } else { + return ""; + } + } + + }; graph.setCellsCloneable(false); graph.setDropEnabled(false); graph.setCellsCloneable(false); @@ -154,6 +175,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); graph.setResetEdgesOnMove(true); + graph.setHtmlLabels(true); graph.setStylesheet(mxStylesheet); graphComponent = new mxGraphComponent(graph); @@ -192,7 +214,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider JPopupMenu jPopupMenu = new JPopupMenu(); if (pinnedAccountDevices.contains(cellAt.getValue())) { - jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin Account " + graph.getLabel(cellAt), unpinIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin Account " + cellAt.getId(), unpinIcon) { @Override public void actionPerformed(ActionEvent e) { handleUnPinEvent(new CVTEvents.UnpinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()))); @@ -200,13 +222,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider })); } else { - jPopupMenu.add(new JMenuItem(new AbstractAction("Pin Account " + graph.getLabel(cellAt), addPinIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Pin Account " + cellAt.getId(), addPinIcon) { @Override public void actionPerformed(ActionEvent e) { handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); } })); - jPopupMenu.add(new JMenuItem(new AbstractAction("Reset and Pin Account " + graph.getLabel(cellAt), pinIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Reset and Pin Account " + cellAt.getId(), pinIcon) { @Override public void actionPerformed(ActionEvent e) { handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), true)); @@ -245,15 +267,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider Math.random() * graphComponent.getHeight(), size, size); - graph.getView().getState(newVertex, true).setLabel(vertexName); - return newVertex; }); - if (pinnedAccountDevices.contains(accountDeviceInstanceKey)) { - graphComponent.addCellOverlay(vertex, new mxCellOverlay(pinIcon, "pinned")); - } else { - graphComponent.removeCellOverlays(vertex); - } + final mxCellState state = graph.getView().getState(vertex, true); + + graph.getView().updateLabel(state); + graph.getView().updateLabelBounds(state); return vertex; } @@ -444,7 +463,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider setLayout(new BorderLayout()); - splitPane.setDividerLocation(400); + splitPane.setDividerLocation(800); splitPane.setResizeWeight(0.5); jPanel1.setLayout(new BorderLayout()); From 70ed9b48551800857a56da59908c218f0f0bc863 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Sat, 20 Jan 2018 12:40:49 +0100 Subject: [PATCH 027/128] tooltips in VisualizationPanel --- .../autopsy/communications/VisualizationPanel.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index be4ae4c388..f268cee083 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -159,6 +159,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } + @Override + public String getToolTipForCell(Object cell) { + return ((mxCell) cell).getId(); + } + }; graph.setCellsCloneable(false); graph.setDropEnabled(false); @@ -185,6 +190,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.setConnectable(false); graphComponent.setKeepSelectionVisibleOnZoom(true); graphComponent.setOpaque(true); + graphComponent.setToolTips(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); From ac358accd84bd59712e1f6f251275282da3daf19 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 24 Jan 2018 09:53:00 +0100 Subject: [PATCH 028/128] initial pass at putting graph updates on bg thread --- Core/nbproject/project.xml | 10 +- .../communications/VisualizationPanel.form | 65 ++++++- .../communications/VisualizationPanel.java | 161 ++++++++++++++---- 3 files changed, 200 insertions(+), 36 deletions(-) diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 9f75c01eeb..914fd29a94 100644 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -6,6 +6,15 @@ org.sleuthkit.autopsy.core + + org.jdesktop.layout + + + + 1 + 1.33.1 + + org.netbeans.api.progress @@ -286,7 +295,6 @@ - net.sf.sevenzipjbinding net.sf.sevenzipjbinding.impl diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 5085589359..c8ae39ae44 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -1,6 +1,6 @@ -
+ @@ -154,6 +154,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index f268cee083..72ef80e7d0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -52,6 +52,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import static java.util.Collections.singleton; import java.util.EnumSet; import java.util.HashMap; @@ -59,20 +60,29 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JButton; +import javax.swing.JLayeredPane; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import javax.swing.JProgressBar; import javax.swing.JSplitPane; import javax.swing.JToolBar; +import javax.swing.OverlayLayout; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; +import org.jdesktop.layout.GroupLayout; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressHandleFactory; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.Node; +import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; @@ -115,6 +125,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_WHITE_SPACE, "wrap"); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); +// mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_OPACITY, 50 ); // mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ROUNDED, true); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_PERIMETER_SPACING, 0); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ENDARROW, mxConstants.NONE); @@ -192,7 +203,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.setOpaque(true); graphComponent.setToolTips(true); graphComponent.setBackground(Color.WHITE); - jPanel1.add(graphComponent, BorderLayout.CENTER); + layeredPane.add(graphComponent, new Integer(0)); + layeredPane.remove(progressOverlay); //install rubber band selection handler rubberband = new mxRubberband(graphComponent); @@ -358,45 +370,73 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void rebuildGraph() throws TskCoreException { - /** - * set to keep track of accounts related to pinned accounts - */ - Set relatedAccounts = new HashSet<>(); - for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { - List relatedAccountDeviceInstances = - commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); + ProgressHandle handle = ProgressHandleFactory.createHandle("Rebuiling graph"); + layeredPane.add(progressOverlay, new Integer(1)); + new SwingWorker, Void>() { + @Override + protected Set doInBackground() throws Exception { + handle.start(); + Set relationshipModels = new HashSet<>(); + try { - //get accounts related to pinned account - for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { - long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); - final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); + /** + * set to keep track of accounts related to pinned accounts + */ + Set relatedAccounts = new HashSet<>(); + for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { + List relatedAccountDeviceInstances = + commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); - //add and edge between pinned and related accounts - List relationships = commsManager.getRelationshipSources( - adiKey.getAccountDeviceInstance(), - relatedADIKey.getAccountDeviceInstance(), - currentFilter); - addEdge(relationships, adiKey, relatedADIKey); - relatedAccounts.add(relatedADIKey); //store related accounts + relatedAccounts.add(adiKey); + //get accounts related to pinned account + for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { +// handle.progress(1); + long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); + final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); + relatedAccounts.add(relatedADIKey); //store related accounts + } + } + + //for each pair of related accounts add edges if they are related o each other. + // this is O(n^2) in the number of related accounts!!! + List relatedAccountsList = new ArrayList<>(relatedAccounts); + for (int i = 0; i < relatedAccountsList.size(); i++) { + for (int j = i; j < relatedAccountsList.size(); j++) { + AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); + AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); + List relationships = commsManager.getRelationshipSources( + adiKey1.getAccountDeviceInstance(), + adiKey2.getAccountDeviceInstance(), + currentFilter); + if (relationships.size() > 0) { + relationshipModels.add(new RelationshipModel(relationships, adiKey1, adiKey2)); + } + } + } + } catch (TskCoreException tskCoreException) { + logger.log(Level.SEVERE, "Error", tskCoreException); + } finally { + } + return relationshipModels; } - } - //for each pair of related accounts add edges if they are related o each other. - // this is O(n^2) in the number of related accounts!!! - List relatedAccountsList = new ArrayList<>(relatedAccounts); - for (int i = 0; i < relatedAccountsList.size(); i++) { - for (int j = i; j < relatedAccountsList.size(); j++) { - AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); - AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); - List relationships = commsManager.getRelationshipSources( - adiKey1.getAccountDeviceInstance(), - adiKey2.getAccountDeviceInstance(), - currentFilter); - if (relationships.size() > 0) { - addEdge(relationships, adiKey1, adiKey2); + @Override + protected void done() { + super.done(); + try { + Set get = get(); + for (RelationshipModel r : get) { + addEdge(r.getSources(), r.getAccount1(), r.getAccount2()); + } + } catch (InterruptedException | ExecutionException | TskCoreException ex) { + Exceptions.printStackTrace(ex); + } finally { + handle.finish(); + layeredPane.remove(progressOverlay); } } - } + }.execute(); + } private void clearGraph() { @@ -466,6 +506,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomOutButton = new JButton(); fitGraphButton = new JButton(); zoomInButton = new JButton(); + layeredPane = new JLayeredPane(); + progressOverlay = new JPanel(); + jProgressBar1 = new JProgressBar(); setLayout(new BorderLayout()); @@ -559,6 +602,27 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jPanel1.add(jToolBar1, BorderLayout.NORTH); + layeredPane.setLayout(new OverlayLayout(layeredPane)); + + GroupLayout progressOverlayLayout = new GroupLayout(progressOverlay); + progressOverlay.setLayout(progressOverlayLayout); + progressOverlayLayout.setHorizontalGroup(progressOverlayLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, progressOverlayLayout.createSequentialGroup() + .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(jProgressBar1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + progressOverlayLayout.setVerticalGroup(progressOverlayLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, progressOverlayLayout.createSequentialGroup() + .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(jProgressBar1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + ); + + layeredPane.add(progressOverlay); + + jPanel1.add(layeredPane, BorderLayout.CENTER); + splitPane.setLeftComponent(jPanel1); add(splitPane, BorderLayout.CENTER); @@ -632,8 +696,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JButton jButton7; private JButton jButton8; private JPanel jPanel1; + private JProgressBar jProgressBar1; private JToolBar.Separator jSeparator1; private JToolBar jToolBar1; + private JLayeredPane layeredPane; + private JPanel progressOverlay; private JSplitPane splitPane; private JButton zoomInButton; private JButton zoomOutButton; @@ -670,4 +737,30 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } } + + private static class RelationshipModel { + + private final List relationshipSources; + private final AccountDeviceInstanceKey adiKey1; + private final AccountDeviceInstanceKey adiKey2; + + private RelationshipModel(List relationships, AccountDeviceInstanceKey adiKey1, AccountDeviceInstanceKey adiKey2) { + this.relationshipSources = relationships; + this.adiKey1 = adiKey1; + this.adiKey2 = adiKey2; + } + + public List getSources() { + return Collections.unmodifiableList(relationshipSources); + } + + public AccountDeviceInstanceKey getAccount1() { + return adiKey1; + } + + public AccountDeviceInstanceKey getAccount2() { + return adiKey2; + } + + } } From 096a85b8802013bf82c0de5e1bc04c15509578f6 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 24 Jan 2018 15:26:03 +0100 Subject: [PATCH 029/128] show progress ass graph is built --- .../communications/VisualizationPanel.form | 86 ++++-------- .../communications/VisualizationPanel.java | 130 ++++++++++-------- 2 files changed, 104 insertions(+), 112 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index c8ae39ae44..76522a6e68 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -11,7 +11,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -154,68 +154,42 @@ - + - + - - - - - - - - - - + + + + + + + - - - - - - - - + + + + + + + + - - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 72ef80e7d0..f0d75d81b7 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -36,12 +36,13 @@ import com.mxgraph.util.mxEvent; import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxPoint; +import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxCellState; import com.mxgraph.view.mxGraph; -import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; @@ -65,20 +66,16 @@ import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JButton; -import javax.swing.JLayeredPane; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JProgressBar; import javax.swing.JSplitPane; import javax.swing.JToolBar; -import javax.swing.OverlayLayout; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.jdesktop.layout.GroupLayout; -import org.netbeans.api.progress.ProgressHandle; -import org.netbeans.api.progress.ProgressHandleFactory; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.Node; @@ -203,8 +200,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.setOpaque(true); graphComponent.setToolTips(true); graphComponent.setBackground(Color.WHITE); - layeredPane.add(graphComponent, new Integer(0)); - layeredPane.remove(progressOverlay); + borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); + progressBar.setVisible(false); //install rubber band selection handler rubberband = new mxRubberband(graphComponent); @@ -219,7 +216,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } else if (e.getPreciseWheelRotation() < 0) { graphComponent.zoomOut(); } - } @Override @@ -291,6 +287,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getView().updateLabel(state); graph.getView().updateLabelBounds(state); + graph.getView().updateBoundingBox(state); + return vertex; } @@ -314,7 +312,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Subscribe void handleUnPinEvent(CVTEvents.UnpinAccountsEvent pinEvent) { - graph.getModel().beginUpdate(); +// graph.getModel().beginUpdate(); try { pinnedAccountDevices.removeAll(pinEvent.getAccountDeviceInstances()); @@ -324,7 +322,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider logger.log(Level.SEVERE, "Error pinning accounts", ex); } finally { // Updates the display - graph.getModel().endUpdate(); +// graph.getModel().endUpdate(); } // applyOrganicLayout(); @@ -332,7 +330,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Subscribe void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { - graph.getModel().beginUpdate(); +// graph.getModel().beginUpdate(); try { if (pinEvent.isReplace()) { pinnedAccountDevices.clear(); @@ -344,7 +342,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider logger.log(Level.SEVERE, "Error pinning accounts", ex); } finally { // Updates the display - graph.getModel().endUpdate(); +// graph.getModel().endUpdate(); } // applyOrganicLayout(); @@ -370,12 +368,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void rebuildGraph() throws TskCoreException { - ProgressHandle handle = ProgressHandleFactory.createHandle("Rebuiling graph"); - layeredPane.add(progressOverlay, new Integer(1)); - new SwingWorker, Void>() { + progressBar.setVisible(true); + new SwingWorker, RelationshipModel>() { @Override protected Set doInBackground() throws Exception { - handle.start(); + progressBar.setIndeterminate(true); + progressBar.setStringPainted(false); Set relationshipModels = new HashSet<>(); try { @@ -400,18 +398,29 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider //for each pair of related accounts add edges if they are related o each other. // this is O(n^2) in the number of related accounts!!! List relatedAccountsList = new ArrayList<>(relatedAccounts); + + progressBar.setString(""); + progressBar.setStringPainted(true); + progressBar.setValue(0); + progressBar.setMaximum(relatedAccountsList.size()); + progressBar.setIndeterminate(false); + for (int i = 0; i < relatedAccountsList.size(); i++) { + AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); + progressBar.setString(adiKey1.getAccountDeviceInstance().getAccount().getTypeSpecificID()); for (int j = i; j < relatedAccountsList.size(); j++) { - AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); + AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); List relationships = commsManager.getRelationshipSources( adiKey1.getAccountDeviceInstance(), adiKey2.getAccountDeviceInstance(), currentFilter); if (relationships.size() > 0) { - relationshipModels.add(new RelationshipModel(relationships, adiKey1, adiKey2)); + RelationshipModel relationshipModel = new RelationshipModel(relationships, adiKey1, adiKey2); + publish(relationshipModel); } } + progressBar.setValue(i); } } catch (TskCoreException tskCoreException) { logger.log(Level.SEVERE, "Error", tskCoreException); @@ -420,19 +429,31 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider return relationshipModels; } + @Override + protected void process(List chunks) { + super.process(chunks); + for (RelationshipModel relationShipModel : chunks) { + try { + addEdge(relationShipModel.getSources(), relationShipModel.adiKey1, relationShipModel.adiKey2); + + } catch (TskCoreException ex) { + Exceptions.printStackTrace(ex); + } + } + mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph); + mxOrganicLayout.setMaxIterations(1); + morph(mxOrganicLayout); + } + @Override protected void done() { super.done(); try { - Set get = get(); - for (RelationshipModel r : get) { - addEdge(r.getSources(), r.getAccount1(), r.getAccount2()); - } - } catch (InterruptedException | ExecutionException | TskCoreException ex) { + get(); + } catch (InterruptedException | ExecutionException ex) { Exceptions.printStackTrace(ex); } finally { - handle.finish(); - layeredPane.remove(progressOverlay); + progressBar.setVisible(false); } } }.execute(); @@ -496,7 +517,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void initComponents() { splitPane = new JSplitPane(); - jPanel1 = new JPanel(); + borderLayoutPanel = new JPanel(); jToolBar1 = new JToolBar(); jButton6 = new JButton(); jButton1 = new JButton(); @@ -506,16 +527,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomOutButton = new JButton(); fitGraphButton = new JButton(); zoomInButton = new JButton(); - layeredPane = new JLayeredPane(); - progressOverlay = new JPanel(); - jProgressBar1 = new JProgressBar(); + statusPanel = new JPanel(); + progressBar = new JProgressBar(); setLayout(new BorderLayout()); splitPane.setDividerLocation(800); splitPane.setResizeWeight(0.5); - jPanel1.setLayout(new BorderLayout()); + borderLayoutPanel.setLayout(new BorderLayout()); jToolBar1.setRollover(true); @@ -600,30 +620,28 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); jToolBar1.add(zoomInButton); - jPanel1.add(jToolBar1, BorderLayout.NORTH); + borderLayoutPanel.add(jToolBar1, BorderLayout.NORTH); - layeredPane.setLayout(new OverlayLayout(layeredPane)); + progressBar.setMaximumSize(new Dimension(200, 14)); - GroupLayout progressOverlayLayout = new GroupLayout(progressOverlay); - progressOverlay.setLayout(progressOverlayLayout); - progressOverlayLayout.setHorizontalGroup(progressOverlayLayout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, progressOverlayLayout.createSequentialGroup() - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(jProgressBar1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + GroupLayout statusPanelLayout = new GroupLayout(statusPanel); + statusPanel.setLayout(statusPanelLayout); + statusPanelLayout.setHorizontalGroup(statusPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, statusPanelLayout.createSequentialGroup() + .addContainerGap(516, Short.MAX_VALUE) + .add(progressBar, GroupLayout.PREFERRED_SIZE, 200, GroupLayout.PREFERRED_SIZE) + .addContainerGap()) ); - progressOverlayLayout.setVerticalGroup(progressOverlayLayout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, progressOverlayLayout.createSequentialGroup() - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(jProgressBar1, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) - .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + statusPanelLayout.setVerticalGroup(statusPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, statusPanelLayout.createSequentialGroup() + .add(3, 3, 3) + .add(progressBar, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(3, 3, 3)) ); - layeredPane.add(progressOverlay); + borderLayoutPanel.add(statusPanel, BorderLayout.SOUTH); - jPanel1.add(layeredPane, BorderLayout.CENTER); - - splitPane.setLeftComponent(jPanel1); + splitPane.setLeftComponent(borderLayoutPanel); add(splitPane, BorderLayout.CENTER); }// //GEN-END:initComponents @@ -664,9 +682,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_fitGraphButtonActionPerformed private void fitGraph() { - mxGraphView view = graphComponent.getGraph().getView(); - view.setTranslate(new mxPoint(-view.getGraphBounds().getX(), -view.getGraphBounds().getY())); - + final Object[] childVertices = graph.getChildVertices(graph.getDefaultParent()); + final mxRectangle boundsForCells = graph.getBoundsForCells(childVertices,true, true,true); + graph.getView().setTranslate(new mxPoint(-boundsForCells.getX(), -boundsForCells.getY())); + // final double widthFactor = (double) graphComponent.getWidth() / (int) view.getGraphBounds().getWidth(); // final double heightFactor = (double) graphComponent.getHeight() / (int) view.getGraphBounds().getHeight(); // @@ -682,7 +701,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { graph.getModel().endUpdate(); -// fitGraph(); + fitGraph(); }); morph.startAnimation(); @@ -690,18 +709,17 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } // Variables declaration - do not modify//GEN-BEGIN:variables + private JPanel borderLayoutPanel; private JButton fitGraphButton; private JButton jButton1; private JButton jButton6; private JButton jButton7; private JButton jButton8; - private JPanel jPanel1; - private JProgressBar jProgressBar1; private JToolBar.Separator jSeparator1; private JToolBar jToolBar1; - private JLayeredPane layeredPane; - private JPanel progressOverlay; + private JProgressBar progressBar; private JSplitPane splitPane; + private JPanel statusPanel; private JButton zoomInButton; private JButton zoomOutButton; // End of variables declaration//GEN-END:variables From 0d187049f11cadd6d1d92dc9ece3da7a4e29e6a7 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 26 Jan 2018 11:49:08 +0100 Subject: [PATCH 030/128] do layout at each graph update --- .../communications/VisualizationPanel.java | 88 ++++++++++++------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index f0d75d81b7..4654fc24af 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -271,6 +271,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); + final mxCell vertex = nodeMap.computeIfAbsent(name, vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; @@ -288,31 +289,34 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getView().updateLabel(state); graph.getView().updateLabelBounds(state); graph.getView().updateBoundingBox(state); - + return vertex; } @SuppressWarnings("unchecked") - private void addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { + private mxCell addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { mxCell vertex1 = getOrCreateVertex(account1); mxCell vertex2 = getOrCreateVertex(account2); Object[] edgesBetween = graph.getEdgesBetween(vertex1, vertex2); + mxCell edge; + if (edgesBetween.length == 0) { final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); final HashSet hashSet = new HashSet<>(relSources); - mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, hashSet, vertex1, vertex2, + // edgeMap.put(relSource, edge); + edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, hashSet, vertex1, vertex2, "strokeWidth=" + Math.sqrt(hashSet.size())); -// edgeMap.put(relSource, edge); - } else if (edgesBetween.length == 1) { - final mxCell edge = (mxCell) edgesBetween[0]; + } else { + edge = (mxCell) edgesBetween[0]; ((Collection) edge.getValue()).addAll(relSources); edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); } + return edge; } @Subscribe void handleUnPinEvent(CVTEvents.UnpinAccountsEvent pinEvent) { -// graph.getModel().beginUpdate(); + graph.getModel().beginUpdate(); try { pinnedAccountDevices.removeAll(pinEvent.getAccountDeviceInstances()); @@ -322,15 +326,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider logger.log(Level.SEVERE, "Error pinning accounts", ex); } finally { // Updates the display -// graph.getModel().endUpdate(); + graph.getModel().endUpdate(); } -// applyOrganicLayout(); + applyOrganicLayout(); } @Subscribe void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { -// graph.getModel().beginUpdate(); + graph.getModel().beginUpdate(); try { if (pinEvent.isReplace()) { pinnedAccountDevices.clear(); @@ -342,10 +346,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider logger.log(Level.SEVERE, "Error pinning accounts", ex); } finally { // Updates the display -// graph.getModel().endUpdate(); + graph.getModel().endUpdate(); } -// applyOrganicLayout(); + applyOrganicLayout(); } @Subscribe @@ -363,17 +367,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } -// applyOrganicLayout(); + applyOrganicLayout(); } private void rebuildGraph() throws TskCoreException { progressBar.setVisible(true); + progressBar.setIndeterminate(true); + progressBar.setStringPainted(false); + new SwingWorker, RelationshipModel>() { @Override protected Set doInBackground() throws Exception { - progressBar.setIndeterminate(true); - progressBar.setStringPainted(false); Set relationshipModels = new HashSet<>(); try { @@ -398,16 +403,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider //for each pair of related accounts add edges if they are related o each other. // this is O(n^2) in the number of related accounts!!! List relatedAccountsList = new ArrayList<>(relatedAccounts); - - progressBar.setString(""); - progressBar.setStringPainted(true); - progressBar.setValue(0); - progressBar.setMaximum(relatedAccountsList.size()); - progressBar.setIndeterminate(false); - + SwingUtilities.invokeLater(() -> { + progressBar.setString(""); + progressBar.setStringPainted(true); + progressBar.setValue(0); + progressBar.setMaximum(relatedAccountsList.size()); + progressBar.setIndeterminate(false); + }); for (int i = 0; i < relatedAccountsList.size(); i++) { AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); - progressBar.setString(adiKey1.getAccountDeviceInstance().getAccount().getTypeSpecificID()); for (int j = i; j < relatedAccountsList.size(); j++) { AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); @@ -420,7 +424,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider publish(relationshipModel); } } - progressBar.setValue(i); + final int p = i; + SwingUtilities.invokeLater(() -> { + progressBar.setValue(p); + }); } } catch (TskCoreException tskCoreException) { logger.log(Level.SEVERE, "Error", tskCoreException); @@ -434,15 +441,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider super.process(chunks); for (RelationshipModel relationShipModel : chunks) { try { - addEdge(relationShipModel.getSources(), relationShipModel.adiKey1, relationShipModel.adiKey2); + mxCell addEdge = addEdge(relationShipModel.getSources(), + relationShipModel.getAccount1(), + relationShipModel.getAccount2()); + progressBar.setString(addEdge.getId()); } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); } } - mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph); - mxOrganicLayout.setMaxIterations(1); - morph(mxOrganicLayout); + applyOrganicLayout(); } @Override @@ -672,10 +680,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton7ActionPerformed private void jButton8ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton8ActionPerformed + applyOrganicLayout(); + }//GEN-LAST:event_jButton8ActionPerformed + + private void applyOrganicLayout() { mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph); mxOrganicLayout.setMaxIterations(10); morph(mxOrganicLayout); - }//GEN-LAST:event_jButton8ActionPerformed + } private void fitGraphButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitGraphButtonActionPerformed fitGraph(); @@ -683,9 +695,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void fitGraph() { final Object[] childVertices = graph.getChildVertices(graph.getDefaultParent()); - final mxRectangle boundsForCells = graph.getBoundsForCells(childVertices,true, true,true); - graph.getView().setTranslate(new mxPoint(-boundsForCells.getX(), -boundsForCells.getY())); - + mxRectangle boundsForCells = graph.getBoundsForCells(childVertices, true, true, true); + if (boundsForCells == null){ + boundsForCells = new mxRectangle(); + } + mxPoint translate = graph.getView().getTranslate(); + if(translate == null){ + translate = new mxPoint(); + } + + graph.getView().setTranslate(new mxPoint(translate.getX()-boundsForCells.getX(),translate.getY() -boundsForCells.getY())); +// graph.moveCells(childVertices, -boundsForCells.getX(), -boundsForCells.getY()); + // final double widthFactor = (double) graphComponent.getWidth() / (int) view.getGraphBounds().getWidth(); // final double heightFactor = (double) graphComponent.getHeight() / (int) view.getGraphBounds().getHeight(); // @@ -703,6 +724,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); fitGraph(); }); + morph.addListener(mxEvent.EXECUTE, (Object sender, mxEventObject event) -> { + fitGraph(); + }); morph.startAnimation(); } From 61a35d8e1f13bd6a8622ae6ce1b5f904c2eb4742 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 26 Jan 2018 12:54:35 +0100 Subject: [PATCH 031/128] use layout manager to apply layout instead of calling it manually --- .../communications/VisualizationPanel.java | 62 +++++++++++-------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 4654fc24af..d8cbf37155 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -39,6 +39,7 @@ import com.mxgraph.util.mxPoint; import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxCellState; import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxLayoutManager; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; @@ -205,7 +206,26 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider //install rubber band selection handler rubberband = new mxRubberband(graphComponent); + new mxLayoutManager(graph) { + final private mxOrganicLayout layout; + { + this.layout = new mxOrganicLayout(graph); + } + @Override + protected void executeLayout(mxIGraphLayout layout, Object parent) { + super.executeLayout(layout, parent); + fitGraph(); + } + + @Override + public mxIGraphLayout getLayout(Object parent) { + if (graph.getModel().getChildCount(parent) > 0) { + return layout; + } + return null; + } + }; //right click handler graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { @Override @@ -271,7 +291,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); - + final mxCell vertex = nodeMap.computeIfAbsent(name, vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; @@ -294,19 +314,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @SuppressWarnings("unchecked") - private mxCell addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { + private mxCell addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) { mxCell vertex1 = getOrCreateVertex(account1); mxCell vertex2 = getOrCreateVertex(account2); Object[] edgesBetween = graph.getEdgesBetween(vertex1, vertex2); mxCell edge; - if (edgesBetween.length == 0) { final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); final HashSet hashSet = new HashSet<>(relSources); // edgeMap.put(relSource, edge); edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, hashSet, vertex1, vertex2, "strokeWidth=" + Math.sqrt(hashSet.size())); - } else { + } else { edge = (mxCell) edgesBetween[0]; ((Collection) edge.getValue()).addAll(relSources); edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); @@ -329,7 +348,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyOrganicLayout(); + applyOrganicLayout(10); } @Subscribe @@ -349,7 +368,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyOrganicLayout(); + applyOrganicLayout(10); } @Subscribe @@ -367,7 +386,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyOrganicLayout(); + applyOrganicLayout(10); } private void rebuildGraph() throws TskCoreException { @@ -440,17 +459,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider protected void process(List chunks) { super.process(chunks); for (RelationshipModel relationShipModel : chunks) { - try { - mxCell addEdge = addEdge(relationShipModel.getSources(), - relationShipModel.getAccount1(), - relationShipModel.getAccount2()); - progressBar.setString(addEdge.getId()); - - } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); - } + mxCell addEdge = addEdge(relationShipModel.getSources(), + relationShipModel.getAccount1(), + relationShipModel.getAccount2()); + progressBar.setString(addEdge.getId()); } - applyOrganicLayout(); } @Override @@ -680,12 +693,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton7ActionPerformed private void jButton8ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton8ActionPerformed - applyOrganicLayout(); + applyOrganicLayout(10); }//GEN-LAST:event_jButton8ActionPerformed - private void applyOrganicLayout() { + private void applyOrganicLayout(int iterations) { mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph); - mxOrganicLayout.setMaxIterations(10); + mxOrganicLayout.setMaxIterations(iterations); morph(mxOrganicLayout); } @@ -695,16 +708,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void fitGraph() { final Object[] childVertices = graph.getChildVertices(graph.getDefaultParent()); - mxRectangle boundsForCells = graph.getBoundsForCells(childVertices, true, true, true); - if (boundsForCells == null){ + mxRectangle boundsForCells = graph.getBoundsForCells(childVertices, true, true, true); + if (boundsForCells == null) { boundsForCells = new mxRectangle(); } mxPoint translate = graph.getView().getTranslate(); - if(translate == null){ + if (translate == null) { translate = new mxPoint(); } - graph.getView().setTranslate(new mxPoint(translate.getX()-boundsForCells.getX(),translate.getY() -boundsForCells.getY())); + graph.getView().setTranslate(new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY())); // graph.moveCells(childVertices, -boundsForCells.getX(), -boundsForCells.getY()); // final double widthFactor = (double) graphComponent.getWidth() / (int) view.getGraphBounds().getWidth(); @@ -803,6 +816,5 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public AccountDeviceInstanceKey getAccount2() { return adiKey2; } - } } From 2f03445ce93a64a589642119bf43e40954b1d317 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 1 Feb 2018 16:46:56 +0100 Subject: [PATCH 032/128] Revert "Merge remote-tracking branch 'upstream/develop' into cvt-bg-threads" This reverts commit 1c40c1071a48f693d5d60148b648b0c230314b66, reversing changes made to 61a35d8e1f13bd6a8622ae6ce1b5f904c2eb4742. --- .../autopsy/actions/Bundle.properties | 8 ++-- .../actions/OpenOutputFolderAction.java | 8 ++-- .../casemodule/CaseInformationPanel.java | 7 +--- .../datamodel/AbstractSqlEamDb.java | 2 +- Core/src/org/sleuthkit/autopsy/core/layer.xml | 6 +-- .../AutoIngestDashboardOpenAction.java | 5 +-- build.xml | 24 ++---------- .../images/case-properties-history-tab.PNG | Bin 28533 -> 29219 bytes docs/doxygen-user/images/live_triage_case.png | Bin 26594 -> 0 bytes .../images/live_triage_dialog.png | Bin 17794 -> 0 bytes docs/doxygen-user/images/live_triage_ds.png | Bin 39635 -> 0 bytes .../images/live_triage_script.png | Bin 12839 -> 0 bytes .../images/multi_user_case_select.png | Bin 15919 -> 44244 bytes docs/doxygen-user/live_triage.dox | 33 ----------------- docs/doxygen-user/main.dox | 1 - ruleset.xml | 35 +++--------------- 16 files changed, 23 insertions(+), 106 deletions(-) delete mode 100644 docs/doxygen-user/images/live_triage_case.png delete mode 100644 docs/doxygen-user/images/live_triage_dialog.png delete mode 100644 docs/doxygen-user/images/live_triage_ds.png delete mode 100644 docs/doxygen-user/images/live_triage_script.png delete mode 100644 docs/doxygen-user/live_triage.dox diff --git a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties index 84f64f3190..1cbc4b70b6 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/actions/Bundle.properties @@ -36,10 +36,10 @@ GetTagNameDialog.tagNameExistsTskCore.msg=The {0} tag name already exists in the OpenLogFolder.error1=Log File Not Found: {0} OpenLogFolder.CouldNotOpenLogFolder=Could not open log folder CTL_OpenLogFolder=Open Log Folder -CTL_OpenOutputFolder=Open Case Folder -OpenOutputFolder.error1=Case Output Folder Not Found\: {0} -OpenOutputFolder.noCaseOpen=No open case, therefore no current case output folder available. -OpenOutputFolder.CouldNotOpenOutputFolder=Could not open case output folder +CTL_OpenOutputFolder=Open Output Folder +OpenOutputFolder.error1=Output Folder Not Found\: {0} +OpenOutputFolder.noCaseOpen=No open case, therefore no current output folder available. +OpenOutputFolder.CouldNotOpenOutputFolder=Could not open output folder ShowIngestProgressSnapshotAction.actionName.text=Get Ingest Progress Snapshot OpenPythonModulesFolderAction.actionName.text=Python Plugins OpenPythonModulesFolderAction.errorMsg.folderNotFound=Python plugins folder not found: {0} diff --git a/Core/src/org/sleuthkit/autopsy/actions/OpenOutputFolderAction.java b/Core/src/org/sleuthkit/autopsy/actions/OpenOutputFolderAction.java index 24370b5a73..c3477859e6 100644 --- a/Core/src/org/sleuthkit/autopsy/actions/OpenOutputFolderAction.java +++ b/Core/src/org/sleuthkit/autopsy/actions/OpenOutputFolderAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,7 +35,7 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; /** - * The action associated with the Tools/Open Case Folder menu item. It opens a + * The action associated with the Tools/Open Output Folder menu item. It opens a * file explorer window for the root output directory for the currently open * case. If the case is a single-user case, this is the case directory. If the * case is a multi-user case, this is a subdirectory of the case directory @@ -44,7 +44,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; * This action should only be invoked in the event dispatch thread (EDT). */ @ActionRegistration(displayName = "#CTL_OpenOutputFolder", iconInMenu = true, lazy = false) -@ActionReference(path = "Menu/Case", position = 302) +@ActionReference(path = "Menu/Tools", position = 1850, separatorBefore = 1849) @ActionID(id = "org.sleuthkit.autopsy.actions.OpenOutputFolderAction", category = "Help") public final class OpenOutputFolderAction extends CallableSystemAction { @@ -61,7 +61,7 @@ public final class OpenOutputFolderAction extends CallableSystemAction { try { Desktop.getDesktop().open(outputDir); } catch (IOException ex) { - logger.log(Level.SEVERE, String.format("Failed to open case output folder %s", outputDir), ex); //NON-NLS + logger.log(Level.SEVERE, String.format("Failed to open output folder %s", outputDir), ex); //NON-NLS NotifyDescriptor descriptor = new NotifyDescriptor.Message( NbBundle.getMessage(this.getClass(), "OpenOutputFolder.CouldNotOpenOutputFolder", outputDir.getAbsolutePath()), NotifyDescriptor.ERROR_MESSAGE); DialogDisplayer.getDefault().notify(descriptor); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java index 2a460d5c46..b03a17920e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2018 Basis Technology Corp. + * Copyright 2011-2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -61,11 +61,6 @@ class CaseInformationPanel extends javax.swing.JPanel { @Override public void stateChanged(ChangeEvent e) { tabbedPane.getSelectedComponent().setSize(tabbedPane.getSelectedComponent().getPreferredSize()); - if (tabbedPane.getSelectedComponent() instanceof CasePropertiesPanel) { - editDetailsButton.setVisible(true); - } else { - editDetailsButton.setVisible(false); - } } }); } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java index 87478529f7..2cac9b4927 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/AbstractSqlEamDb.java @@ -2172,7 +2172,7 @@ public abstract class AbstractSqlEamDb implements EamDb { } CorrelationAttributeInstance eamArtifactInstance = new CorrelationAttributeInstance( new CorrelationCase(resultSet.getInt("case_id"), resultSet.getString("case_uid"), resultSet.getString("case_name")), - new CorrelationDataSource(resultSet.getInt("case_id"), -1, resultSet.getString("device_id"), resultSet.getString("name")), + new CorrelationDataSource(-1, resultSet.getInt("case_id"), resultSet.getString("device_id"), resultSet.getString("name")), resultSet.getString("file_path"), resultSet.getString("comment"), TskData.FileKnown.valueOf(resultSet.getByte("known_status")) diff --git a/Core/src/org/sleuthkit/autopsy/core/layer.xml b/Core/src/org/sleuthkit/autopsy/core/layer.xml index fa78fe5485..465529fa9f 100644 --- a/Core/src/org/sleuthkit/autopsy/core/layer.xml +++ b/Core/src/org/sleuthkit/autopsy/core/layer.xml @@ -198,10 +198,10 @@ - + - + @@ -213,7 +213,7 @@ --> - + diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java index f299fbc5a3..45563a4f4b 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestDashboardOpenAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017-2018 Basis Technology Corp. + * Copyright 2017 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,14 +28,13 @@ import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.experimental.autoingest.AutoIngestDashboardOpenAction") -@ActionReference(path = "Menu/Tools", position = 201) +@ActionReference(path = "Menu/Tools", position = 104) @ActionRegistration(displayName = "#CTL_AutoIngestDashboardOpenAction", lazy = false) @Messages({"CTL_AutoIngestDashboardOpenAction=Auto Ingest Dashboard"}) public final class AutoIngestDashboardOpenAction extends CallableSystemAction { private static final Logger LOGGER = Logger.getLogger(AutoIngestDashboardOpenAction.class.getName()); private static final String DISPLAY_NAME = Bundle.CTL_AutoIngestDashboardOpenAction(); - private static final long serialVersionUID = 1L; @Override public boolean isEnabled() { diff --git a/build.xml b/build.xml index d044f68f06..4eaa7df1c8 100644 --- a/build.xml +++ b/build.xml @@ -32,10 +32,7 @@ - - - - + @@ -85,13 +82,7 @@ - - - - - - - + @@ -100,17 +91,8 @@ - - - - - - - - - - + diff --git a/docs/doxygen-user/images/case-properties-history-tab.PNG b/docs/doxygen-user/images/case-properties-history-tab.PNG index 0a5d8f25b75fbc102bb21cbe921c518f4417e6de..fc0a4da441bf5cfce71ddfab700066e8b2196994 100644 GIT binary patch delta 9079 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 S3b;TDxB%GI&t;ucLK6TvSzk^7 diff --git a/docs/doxygen-user/images/live_triage_ds.png b/docs/doxygen-user/images/live_triage_ds.png deleted file mode 100644 index f21a50aa82317547361a10f34ee02e5b34512221..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 39635 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 diff --git a/docs/doxygen-user/live_triage.dox b/docs/doxygen-user/live_triage.dox deleted file mode 100644 index d85b54a20a..0000000000 --- a/docs/doxygen-user/live_triage.dox +++ /dev/null @@ -1,33 +0,0 @@ -/*! \page live_triage_page Live Triage - -\section live_triage_overview Overview - -The Live Triage feature allows you to load Autopsy onto a removable drive to run on target systems while making minimal changes to that target system. This will currently only work on Windows systems. - -\section live_triage_create_drive Creating a live triage drive - -To create a live triage drive, go to Tools->Make Live Triage Drive to bring up the main dialog. - -\image html live_triage_dialog.png - -Select the drive you want to use - any type of USB storage device will work. For best results use the fastest drive available. Once the process is complete the root folder will contain an Autopsy folder and a RunFromUSB.bat file. - -\section live_triage_usage Running Autopsy from the live triage drive - -Insert the drive into the target machine and browse to it in Windows Explorer. Right click on RunFromUSB.bat and select "Run as administrator". This is necessary to analyze the local drives. - -\image html live_triage_script.png - -Running the script will generate a few more directories on the USB drive. The configData directory stores all the data used by Autopsy - primarily configuration files and temporary files. You can make changes to the Autopsy settings and they will persist between runs. The cases directory is created as a recommended place to save your case data. You will need to browse to it when creating a case in Autopsy. - -Once Autopsy is running, proceed to create a case as normal, making sure to save it on the USB drive. - -\image html live_triage_case.png - -Then choose the Local Disk data source and select the desired drive. - -\image html live_triage_ds.png - -See the \ref ds_local page for more information on local disk data sources. - -*/ \ No newline at end of file diff --git a/docs/doxygen-user/main.dox b/docs/doxygen-user/main.dox index a72256c7f6..e4a1e2b3a9 100644 --- a/docs/doxygen-user/main.dox +++ b/docs/doxygen-user/main.dox @@ -60,7 +60,6 @@ The following topics are available here: - \subpage windows_authentication - \subpage multiuser_sec_page - \subpage multiuser_page -- \subpage live_triage_page - \subpage advanced_page If the topic you need is not listed, refer to the Autopsy Wiki or join the SleuthKit User List at SourceForge. diff --git a/ruleset.xml b/ruleset.xml index 2b4e66b821..d4f2f075fc 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -1,38 +1,13 @@ - + xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" + xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"> - Ruleset used by Autopsy + Ruleset used by Autopsy - - - - - - - - - - - - - - - - - - - - - - - - - - From db89695d5d936e8a7b9f0d3e19f9386e1aacce3c Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 6 Feb 2018 12:25:47 +0100 Subject: [PATCH 033/128] refactor mxGraphImpl out of VisualizationPanel --- .../communications/VisualizationPanel.form | 5 +- .../communications/VisualizationPanel.java | 413 ++++++------------ .../autopsy/communications/mxGraphImpl.java | 335 ++++++++++++++ 3 files changed, 475 insertions(+), 278 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 76522a6e68..13517bf10b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -11,7 +11,7 @@ - + @@ -175,7 +175,7 @@ - + @@ -187,6 +187,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index d8cbf37155..9147552bcd 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -18,8 +18,6 @@ */ package org.sleuthkit.autopsy.communications; -import com.google.common.collect.Multimap; -import com.google.common.collect.MultimapBuilder; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.layout.mxCircleLayout; @@ -31,16 +29,11 @@ import com.mxgraph.model.mxICell; import com.mxgraph.swing.handler.mxRubberband; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.swing.util.mxMorphing; -import com.mxgraph.util.mxConstants; import com.mxgraph.util.mxEvent; import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxPoint; import com.mxgraph.util.mxRectangle; -import com.mxgraph.view.mxCellState; -import com.mxgraph.view.mxGraph; -import com.mxgraph.view.mxLayoutManager; -import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; @@ -51,18 +44,11 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.beans.PropertyVetoException; import java.net.URL; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; import static java.util.Collections.singleton; 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; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.ImageIcon; @@ -80,13 +66,13 @@ import org.jdesktop.layout.GroupLayout; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.Node; -import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; @@ -106,6 +92,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); + private static final URL MARKER_PIN_URL = VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png"); static final private ImageIcon pinIcon = new ImageIcon(MARKER_PIN_URL); static final private ImageIcon addPinIcon = @@ -113,23 +100,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider static final private ImageIcon unpinIcon = new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--minus.png")); - 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.getDefaultVertexStyle().put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE); - mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "000000"); -// mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_WHITE_SPACE, "wrap"); - - mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); -// mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_OPACITY, 50 ); -// mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ROUNDED, true); - mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_PERIMETER_SPACING, 0); - mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ENDARROW, mxConstants.NONE); - mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE); - } - private final ExplorerManager vizEM = new ExplorerManager(); private final ExplorerManager gacEM = new ExplorerManager(); private final ProxyLookup proxyLookup = new ProxyLookup( @@ -137,60 +107,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider ExplorerUtils.createLookup(vizEM, getActionMap())); private final mxGraphComponent graphComponent; - private final mxGraph graph; - private final Map nodeMap = new HashMap<>(); - private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); + private final mxGraphImpl graph; private CommunicationsManager commsManager; - private final HashSet pinnedAccountDevices = new HashSet<>(); private CommunicationsFilter currentFilter; private final mxRubberband rubberband; public VisualizationPanel() { initComponents(); - graph = new mxGraph() { - @Override - public String convertValueToString(Object cell) { - Object value = getModel().getValue(cell); - if (value instanceof AccountDeviceInstanceKey) { - final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value; - final String accountName = adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID(); - String iconFileName = Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()); - String label = "" + accountName; - if (pinnedAccountDevices.contains(adiKey)) { - label += ""; - } - return "" + label + ""; - } else { - return ""; - } - } - - @Override - public String getToolTipForCell(Object cell) { - return ((mxCell) cell).getId(); - } - - }; - graph.setCellsCloneable(false); - graph.setDropEnabled(false); - graph.setCellsCloneable(false); - graph.setCellsEditable(false); - graph.setCellsResizable(false); - graph.setCellsMovable(true); - graph.setCellsDisconnectable(false); - graph.setConnectableEdges(false); - graph.setDisconnectOnMove(false); - graph.setEdgeLabelsMovable(false); - graph.setVertexLabelsMovable(false); - graph.setAllowDanglingEdges(false); - graph.setCellsBendable(true); - graph.setKeepEdgesInBackground(true); - graph.setResetEdgesOnMove(true); - graph.setHtmlLabels(true); - graph.setStylesheet(mxStylesheet); + graph = new mxGraphImpl(); graphComponent = new mxGraphComponent(graph); graphComponent.setAutoExtend(true); @@ -206,26 +131,32 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider //install rubber band selection handler rubberband = new mxRubberband(graphComponent); - new mxLayoutManager(graph) { - final private mxOrganicLayout layout; - { - this.layout = new mxOrganicLayout(graph); - } - - @Override - protected void executeLayout(mxIGraphLayout layout, Object parent) { - super.executeLayout(layout, parent); - fitGraph(); - } - - @Override - public mxIGraphLayout getLayout(Object parent) { - if (graph.getModel().getChildCount(parent) > 0) { - return layout; - } - return null; - } - }; +// new mxLayoutManager(graph) { +// final private mxOrganicLayout layout; +// private int counter; +// { +// this.layout = new mxOrganicLayout(graph); +// this.layout.setMaxIterations(1); +// } +// +// @Override +// protected void executeLayout(mxIGraphLayout layout, Object parent) { +// if (counter % 10 == 0) +// { +// super.executeLayout(layout, parent); +//// fitGraph(); +// } +// counter++; +// } +// +// @Override +// public mxIGraphLayout getLayout(Object parent) { +// if (graph.getModel().getChildCount(parent) > 0) { +// return layout; +// } +// return null; +// } +// }; //right click handler graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { @Override @@ -244,10 +175,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider if (SwingUtilities.isRightMouseButton(e)) { mxICell cellAt = (mxICell) graphComponent.getCellAt(e.getX(), e.getY()); if (cellAt != null && cellAt.isVertex()) { - JPopupMenu jPopupMenu = new JPopupMenu(); - - if (pinnedAccountDevices.contains(cellAt.getValue())) { + if (graph.isAccountPinned((AccountDeviceInstanceKey) cellAt.getValue())) { jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin Account " + cellAt.getId(), unpinIcon) { @Override public void actionPerformed(ActionEvent e) { @@ -256,6 +185,19 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider })); } else { + jPopupMenu.add(new JMenuItem(new AbstractAction("Lock Account " + cellAt.getId(), addPinIcon) { + @Override + public void actionPerformed(ActionEvent e) { + graph.lockAccount((AccountDeviceInstanceKey) cellAt.getValue()); + } + })); + + jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock Account " + cellAt.getId(), addPinIcon) { + @Override + public void actionPerformed(ActionEvent e) { + graph.unlockAccount((AccountDeviceInstanceKey) cellAt.getValue()); + } + })); jPopupMenu.add(new JMenuItem(new AbstractAction("Pin Account " + cellAt.getId(), addPinIcon) { @Override public void actionPerformed(ActionEvent e) { @@ -287,59 +229,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider return proxyLookup; } - private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { - final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); - final String name =// accountDeviceInstance.getDeviceId() + ":" + - accountDeviceInstance.getAccount().getTypeSpecificID(); - - final mxCell vertex = nodeMap.computeIfAbsent(name, vertexName -> { - double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; - - mxCell newVertex = (mxCell) graph.insertVertex( - graph.getDefaultParent(), - vertexName, accountDeviceInstanceKey, - Math.random() * graphComponent.getWidth(), - Math.random() * graphComponent.getHeight(), - size, - size); - return newVertex; - }); - final mxCellState state = graph.getView().getState(vertex, true); - - graph.getView().updateLabel(state); - graph.getView().updateLabelBounds(state); - graph.getView().updateBoundingBox(state); - - return vertex; - } - - @SuppressWarnings("unchecked") - private mxCell addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) { - mxCell vertex1 = getOrCreateVertex(account1); - mxCell vertex2 = getOrCreateVertex(account2); - Object[] edgesBetween = graph.getEdgesBetween(vertex1, vertex2); - mxCell edge; - if (edgesBetween.length == 0) { - final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); - final HashSet hashSet = new HashSet<>(relSources); - // edgeMap.put(relSource, edge); - edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, hashSet, vertex1, vertex2, - "strokeWidth=" + Math.sqrt(hashSet.size())); - } else { - edge = (mxCell) edgesBetween[0]; - ((Collection) edge.getValue()).addAll(relSources); - edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); - } - return edge; - } - @Subscribe void handleUnPinEvent(CVTEvents.UnpinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); try { - - pinnedAccountDevices.removeAll(pinEvent.getAccountDeviceInstances()); - clearGraph(); + graph.unpinAccount(pinEvent.getAccountDeviceInstances()); + graph.clear(); rebuildGraph(); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error pinning accounts", ex); @@ -356,10 +251,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().beginUpdate(); try { if (pinEvent.isReplace()) { - pinnedAccountDevices.clear(); - clearGraph(); + graph.resetGraph(); } - pinnedAccountDevices.addAll(pinEvent.getAccountDeviceInstances()); + + graph.pinAccount(pinEvent.getAccountDeviceInstances()); rebuildGraph(); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error pinning accounts", ex); @@ -376,7 +271,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().beginUpdate(); try { - clearGraph(); + graph.clear(); currentFilter = filterChangeEvent.getNewFilter(); rebuildGraph(); } catch (TskCoreException ex) { @@ -391,101 +286,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void rebuildGraph() throws TskCoreException { - progressBar.setVisible(true); - progressBar.setIndeterminate(true); - progressBar.setStringPainted(false); + SwingWorker rebuild = graph.rebuild(new ProgressIndicatorImpl(), commsManager, currentFilter); - new SwingWorker, RelationshipModel>() { - @Override - protected Set doInBackground() throws Exception { - Set relationshipModels = new HashSet<>(); - try { - - /** - * set to keep track of accounts related to pinned accounts - */ - Set relatedAccounts = new HashSet<>(); - for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { - List relatedAccountDeviceInstances = - commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); - - relatedAccounts.add(adiKey); - //get accounts related to pinned account - for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { -// handle.progress(1); - long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); - final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); - relatedAccounts.add(relatedADIKey); //store related accounts - } - } - - //for each pair of related accounts add edges if they are related o each other. - // this is O(n^2) in the number of related accounts!!! - List relatedAccountsList = new ArrayList<>(relatedAccounts); - SwingUtilities.invokeLater(() -> { - progressBar.setString(""); - progressBar.setStringPainted(true); - progressBar.setValue(0); - progressBar.setMaximum(relatedAccountsList.size()); - progressBar.setIndeterminate(false); - }); - for (int i = 0; i < relatedAccountsList.size(); i++) { - AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); - for (int j = i; j < relatedAccountsList.size(); j++) { - - AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); - List relationships = commsManager.getRelationshipSources( - adiKey1.getAccountDeviceInstance(), - adiKey2.getAccountDeviceInstance(), - currentFilter); - if (relationships.size() > 0) { - RelationshipModel relationshipModel = new RelationshipModel(relationships, adiKey1, adiKey2); - publish(relationshipModel); - } - } - final int p = i; - SwingUtilities.invokeLater(() -> { - progressBar.setValue(p); - }); - } - } catch (TskCoreException tskCoreException) { - logger.log(Level.SEVERE, "Error", tskCoreException); - } finally { - } - return relationshipModels; - } - - @Override - protected void process(List chunks) { - super.process(chunks); - for (RelationshipModel relationShipModel : chunks) { - mxCell addEdge = addEdge(relationShipModel.getSources(), - relationShipModel.getAccount1(), - relationShipModel.getAccount2()); - progressBar.setString(addEdge.getId()); - } - } - - @Override - protected void done() { - super.done(); - try { - get(); - } catch (InterruptedException | ExecutionException ex) { - Exceptions.printStackTrace(ex); - } finally { - progressBar.setVisible(false); - } - } - }.execute(); + rebuild.execute(); } - private void clearGraph() { - nodeMap.clear(); - edgeMap.clear(); - graph.removeCells(graph.getChildVertices(graph.getDefaultParent())); - } + @Override public void addNotify() { @@ -504,7 +311,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { graph.getModel().beginUpdate(); try { - clearGraph(); + graph.resetGraph(); } finally { graph.getModel().endUpdate(); } @@ -644,6 +451,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider borderLayoutPanel.add(jToolBar1, BorderLayout.NORTH); progressBar.setMaximumSize(new Dimension(200, 14)); + progressBar.setStringPainted(true); GroupLayout statusPanelLayout = new GroupLayout(statusPanel); statusPanel.setLayout(statusPanelLayout); @@ -656,7 +464,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider statusPanelLayout.setVerticalGroup(statusPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(GroupLayout.TRAILING, statusPanelLayout.createSequentialGroup() .add(3, 3, 3) - .add(progressBar, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(progressBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) .add(3, 3, 3)) ); @@ -668,12 +476,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }// //GEN-END:initComponents private void jButton1ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - morph(new mxFastOrganicLayout(graph) { - @Override - public boolean isVertexMovable(Object vertex) { - return super.isVertexMovable(vertex) && false == pinnedAccountDevices.contains((AccountDeviceInstanceKey) ((mxICell) vertex).getValue()); - } - }); + morph(new mxFastOrganicLayout(graph)); }//GEN-LAST:event_jButton1ActionPerformed private void zoomInButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed @@ -697,7 +500,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton8ActionPerformed private void applyOrganicLayout(int iterations) { - mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph); + mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph) { + @Override + public boolean isVertexMovable(Object vertex) { + return super.isVertexMovable(vertex); //To change body of generated methods, choose Tools | Templates. + } + + }; mxOrganicLayout.setMaxIterations(iterations); morph(mxOrganicLayout); } @@ -718,27 +527,31 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } graph.getView().setTranslate(new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY())); -// graph.moveCells(childVertices, -boundsForCells.getX(), -boundsForCells.getY()); -// final double widthFactor = (double) graphComponent.getWidth() / (int) view.getGraphBounds().getWidth(); -// final double heightFactor = (double) graphComponent.getHeight() / (int) view.getGraphBounds().getHeight(); -// -// view.setScale(Math.min(widthFactor, heightFactor) * view.getScale()); + +// graphComponent.zoomActual(); +// graphComponent.zoomAndCenter(); +// graph.getGraphBounds().getWidth() } private void morph(mxIGraphLayout layout) { // layout using morphing graph.getModel().beginUpdate(); try { + progressBar.setVisible(true); + progressBar.setIndeterminate(true); + progressBar.setString("applying layout"); layout.execute(graph.getDefaultParent()); } finally { mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { graph.getModel().endUpdate(); fitGraph(); + progressBar.setVisible(false); + progressBar.setValue(0); }); morph.addListener(mxEvent.EXECUTE, (Object sender, mxEventObject event) -> { - fitGraph(); +// fitGraph(); }); morph.startAnimation(); @@ -793,28 +606,76 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } - private static class RelationshipModel { + private class ProgressIndicatorImpl implements ProgressIndicator { - private final List relationshipSources; - private final AccountDeviceInstanceKey adiKey1; - private final AccountDeviceInstanceKey adiKey2; - - private RelationshipModel(List relationships, AccountDeviceInstanceKey adiKey1, AccountDeviceInstanceKey adiKey2) { - this.relationshipSources = relationships; - this.adiKey1 = adiKey1; - this.adiKey2 = adiKey2; + @Override + public void start(String message, int totalWorkUnits) { + SwingUtilities.invokeLater(() -> { + progressBar.setVisible(true); + progressBar.setIndeterminate(false); + progressBar.setString(message); + progressBar.setMaximum(totalWorkUnits); + progressBar.setValue(0); + }); } - public List getSources() { - return Collections.unmodifiableList(relationshipSources); + @Override + public void start(String message) { + SwingUtilities.invokeLater(() -> { + progressBar.setVisible(true); + progressBar.setString(message); + progressBar.setIndeterminate(true); + }); } - public AccountDeviceInstanceKey getAccount1() { - return adiKey1; + @Override + public void switchToIndeterminate(String message) { + SwingUtilities.invokeLater(() -> { + progressBar.setVisible(true); + progressBar.setIndeterminate(true); + progressBar.setString(message); + }); } - public AccountDeviceInstanceKey getAccount2() { - return adiKey2; + @Override + public void switchToDeterminate(String message, int workUnitsCompleted, int totalWorkUnits) { + SwingUtilities.invokeLater(() -> { + progressBar.setVisible(true); + progressBar.setIndeterminate(false); + progressBar.setString(message); + progressBar.setMaximum(totalWorkUnits); + progressBar.setValue(workUnitsCompleted); + }); + } + + @Override + public void progress(String message) { + SwingUtilities.invokeLater(() -> { + progressBar.setString(message); + }); + } + + @Override + public void progress(int workUnitsCompleted) { + SwingUtilities.invokeLater(() -> { + progressBar.setValue(workUnitsCompleted); + }); + } + + @Override + public void progress(String message, int workUnitsCompleted) { + SwingUtilities.invokeLater(() -> { + progressBar.setString(message); + progressBar.setValue(workUnitsCompleted); + }); + } + + @Override + public void finish() { + SwingUtilities.invokeLater(() -> { + progressBar.setValue(progressBar.getValue()); + progressBar.setVisible(false); + }); } } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java new file mode 100644 index 0000000000..5f7397b1b5 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -0,0 +1,335 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.communications; + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; +import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxICell; +import com.mxgraph.util.mxConstants; +import com.mxgraph.view.mxCellState; +import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxStylesheet; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +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; +import java.util.logging.Level; +import javax.swing.SwingWorker; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.progress.ProgressIndicator; +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; + +final class mxGraphImpl extends mxGraph { + + private static final Logger logger = Logger.getLogger(mxGraphImpl.class.getName()); + private static final URL MARKER_PIN_URL = VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png"); + + static final private mxStylesheet mxStylesheet = new mxStylesheet(); + private final HashSet pinnedAccountDevices = new HashSet<>(); + private final HashSet lockedAccountDevices = new HashSet<>(); + + private final Map nodeMap = new HashMap<>(); + private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); + + static { + //initialize defaul cell (Vertex and/or Edge) properties + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE); + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "000000"); +// mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_WHITE_SPACE, "wrap"); + + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); +// mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_OPACITY, 50 ); +// mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ROUNDED, true); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_PERIMETER_SPACING, 0); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ENDARROW, mxConstants.NONE); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE); + } + + public mxGraphImpl() { + super(mxStylesheet); + setCellsCloneable(false); + setDropEnabled(false); + setCellsCloneable(false); + setCellsEditable(false); + setCellsResizable(false); + setCellsMovable(true); + setCellsDisconnectable(false); + setConnectableEdges(false); + setDisconnectOnMove(false); + setEdgeLabelsMovable(false); + setVertexLabelsMovable(false); + setAllowDanglingEdges(false); + setCellsBendable(true); + setKeepEdgesInBackground(true); + setResetEdgesOnMove(true); + setHtmlLabels(true); + } + + @Override + public boolean isCellMovable(Object cell) { + final mxICell mxCell = (mxICell) cell; + if (mxCell.isEdge()) { + return super.isCellMovable(cell); + } else { + return super.isCellMovable(cell) + && false == lockedAccountDevices.contains((AccountDeviceInstanceKey) mxCell.getValue()); + } + } + + void clear() { + nodeMap.clear(); + edgeMap.clear(); + removeCells(getChildVertices(getDefaultParent())); + } + + boolean isAccountPinned(AccountDeviceInstanceKey account) { + return pinnedAccountDevices.contains(account); + } + + @Override + public String convertValueToString(Object cell) { + Object value = getModel().getValue(cell); + if (value instanceof AccountDeviceInstanceKey) { + final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value; + final String accountName = adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID(); + String iconFileName = Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()); + String label = "" + accountName; + if (pinnedAccountDevices.contains(adiKey)) { + label += ""; + } + return "" + label + ""; + } else { + return ""; + } + } + + @Override + public String getToolTipForCell(Object cell) { + return ((mxICell) cell).getId(); + } + + void unpinAccount(ImmutableSet accountDeviceInstances) { + pinnedAccountDevices.removeAll(accountDeviceInstances); + } + + void pinAccount(ImmutableSet accountDeviceInstances) { + pinnedAccountDevices.addAll(accountDeviceInstances); + } + + void lockAccount(AccountDeviceInstanceKey accountDeviceInstance) { + lockedAccountDevices.add(accountDeviceInstance); + } + + void unlockAccount(AccountDeviceInstanceKey accountDeviceInstance) { + lockedAccountDevices.remove(accountDeviceInstance); + } + + SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + + return new SwingWorkerImpl(progress, commsManager, currentFilter); + } + + void resetGraph() { + clear(); + pinnedAccountDevices.clear(); + lockedAccountDevices.clear(); + } + + private static class RelationshipModel { + + @Override + public String toString() { + return adiKey1.getAccountDeviceInstance().getAccount().getTypeSpecificID() + + "<-" + relationshipSources.size() + "->" + + adiKey2.getAccountDeviceInstance().getAccount().getTypeSpecificID(); + } + + private final List relationshipSources; + private final AccountDeviceInstanceKey adiKey1; + private final AccountDeviceInstanceKey adiKey2; + + private RelationshipModel(List relationships, AccountDeviceInstanceKey adiKey1, AccountDeviceInstanceKey adiKey2) { + this.relationshipSources = relationships; + this.adiKey1 = adiKey1; + this.adiKey2 = adiKey2; + } + + public List getSources() { + return Collections.unmodifiableList(relationshipSources); + } + + public AccountDeviceInstanceKey getAccount1() { + return adiKey1; + } + + public AccountDeviceInstanceKey getAccount2() { + return adiKey2; + } + } + + private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { + final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); + final String name =// accountDeviceInstance.getDeviceId() + ":" + + accountDeviceInstance.getAccount().getTypeSpecificID(); + + final mxCell vertex = nodeMap.computeIfAbsent(name, vertexName -> { + double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; + + mxCell newVertex = (mxCell) insertVertex( + getDefaultParent(), + vertexName, accountDeviceInstanceKey, + Math.random() * getView().getGraphBounds().getWidth(), + Math.random() * getView().getGraphBounds().getHeight(), + size, + size); + return newVertex; + }); + final mxCellState state = getView().getState(vertex, true); + + getView().updateLabel(state); + getView().updateLabelBounds(state); + getView().updateBoundingBox(state); + + return vertex; + } + + @SuppressWarnings("unchecked") + private mxCell addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) { + mxCell vertex1 = getOrCreateVertex(account1); + mxCell vertex2 = getOrCreateVertex(account2); + Object[] edgesBetween = getEdgesBetween(vertex1, vertex2); + mxCell edge; + if (edgesBetween.length == 0) { + final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); + final HashSet hashSet = new HashSet<>(relSources); + // edgeMap.put(relSource, edge); + edge = (mxCell) insertEdge(getDefaultParent(), edgeName, hashSet, vertex1, vertex2, + "strokeWidth=" + Math.sqrt(hashSet.size())); + } else { + edge = (mxCell) edgesBetween[0]; + ((Collection) edge.getValue()).addAll(relSources); + edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); + } + return edge; + } + + class SwingWorkerImpl extends SwingWorker, mxGraphImpl.RelationshipModel> { + + private final ProgressIndicator progress; + private final CommunicationsManager commsManager; + private final CommunicationsFilter currentFilter; + + SwingWorkerImpl(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + this.progress = progress; + this.currentFilter = currentFilter; + this.commsManager = commsManager; + } + + @Override + protected Set doInBackground() throws Exception { + + progress.start("Loading accounts", pinnedAccountDevices.size()); + int i = 0; + Set relationshipModels = new HashSet<>(); + try { + /** + * set to keep track of accounts related to pinned accounts + */ + Set relatedAccounts = new HashSet<>(); + for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { + List relatedAccountDeviceInstances = + commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); + relatedAccounts.add(adiKey); + //get accounts related to pinned account + for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { +// handle.progress(1); + long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); + final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); + relatedAccounts.add(relatedADIKey); //store related accounts + } + progress.progress(++i); + } + + //for each pair of related accounts add edges if they are related o each other. + // this is O(n^2) in the number of related accounts!!! + List relatedAccountsList = new ArrayList<>(relatedAccounts); + progress.switchToDeterminate("", 0, relatedAccountsList.size()); + for (i = 0; i < relatedAccountsList.size(); i++) { + AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); + for (int j = i; j < relatedAccountsList.size(); j++) { + + AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); + List relationships = commsManager.getRelationshipSources( + adiKey1.getAccountDeviceInstance(), + adiKey2.getAccountDeviceInstance(), + currentFilter); + if (relationships.size() > 0) { + mxGraphImpl.RelationshipModel relationshipModel = new RelationshipModel(relationships, adiKey1, adiKey2); + publish(relationshipModel); + progress.progress(relationshipModel.toString()); + } + } + progress.progress(i); + } + } catch (TskCoreException tskCoreException) { + logger.log(Level.SEVERE, "Error", tskCoreException); + } finally { + } + return relationshipModels; + } + + @Override + protected void process(List chunks) { + super.process(chunks); + for (mxGraphImpl.RelationshipModel relationShipModel : chunks) { + mxCell addEdge = addEdge(relationShipModel.getSources(), + relationShipModel.getAccount1(), + relationShipModel.getAccount2()); + } + } + + @Override + protected void done() { + super.done(); + try { + get(); + } catch (InterruptedException | ExecutionException ex) { + Exceptions.printStackTrace(ex); + } finally { + progress.finish(); + } + } + } +} From 70997fa7da73a80c0282a68adb7f5cd5a16ab128 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 6 Feb 2018 13:53:00 +0100 Subject: [PATCH 034/128] allow Device Account Type (and filter) in Visualization panel --- .../AccountDeviceInstanceNodeFactory.java | 8 ++++++-- .../autopsy/communications/CVTTopComponent.java | 1 + .../autopsy/communications/FiltersPanel.java | 17 ++++++++++++++--- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java index 5fe069b523..49ae86d62b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java @@ -24,6 +24,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.openide.nodes.ChildFactory; import org.openide.nodes.Node; +import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; @@ -53,8 +54,11 @@ final class AccountDeviceInstanceNodeFactory extends ChildFactory accountDeviceInstancesWithRelationships = commsManager.getAccountDeviceInstancesWithRelationships(commsFilter); for (AccountDeviceInstance accountDeviceInstance : accountDeviceInstancesWithRelationships) { - long communicationsCount = commsManager.getRelationshipSourcesCount(accountDeviceInstance, commsFilter); - accountDeviceInstanceKeys.add(new AccountDeviceInstanceKey(accountDeviceInstance, commsFilter, communicationsCount)); + //Filter out device accounts, in the table. + if (Account.Type.DEVICE.equals(accountDeviceInstance.getAccount().getAccountType()) ==false) { + 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); diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 6585f6324a..11ae8809e8 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -57,6 +57,7 @@ public final class CVTTopComponent extends TopComponent { browseVisualizeTabPane.addChangeListener(changeEvent -> { Lookup.Provider selectedComponent = (Lookup.Provider) browseVisualizeTabPane.getSelectedComponent(); proxyLookup.changeLookups(selectedComponent.getLookup()); + filtersPane.setDeviceAccountTypeEnabled(browseVisualizeTabPane.getSelectedIndex() != 0); }); diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 36232eaf15..248412b07a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -78,6 +78,7 @@ final public class FiltersPanel extends JPanel { * results) */ private final ItemListener validationListener; + private boolean deviceAccountTypeEnabled = false; @NbBundle.Messages({"refreshText=Refresh Results", "applyText=Apply"}) @@ -196,8 +197,8 @@ final public class FiltersPanel extends JPanel { Account.Type.PREDEFINED_ACCOUNT_TYPES.forEach(type -> { if (type.equals(Account.Type.CREDIT_CARD)) { //don't show a check box for credit cards - } else if (type.equals(Account.Type.DEVICE)) { - //don't show a check box fro device +// } else if (type.equals(Account.Type.DEVICE)) { +// //don't show a check box fro device } else { accountTypeMap.computeIfAbsent(type, t -> { final JCheckBox jCheckBox = new JCheckBox( @@ -209,6 +210,9 @@ final public class FiltersPanel extends JPanel { ); jCheckBox.addItemListener(validationListener); accountTypePane.add(jCheckBox); + if (t.equals(Account.Type.DEVICE)) { + jCheckBox.setEnabled(deviceAccountTypeEnabled); + } return jCheckBox; }); } @@ -485,7 +489,7 @@ final public class FiltersPanel extends JPanel { validateFilters(); } - private CommunicationsFilter getFilter() { + private CommunicationsFilter getFilter() { CommunicationsFilter commsFilter = new CommunicationsFilter(); commsFilter.addAndFilter(getDeviceFilter()); commsFilter.addAndFilter(getAccountTypeFilter()); @@ -618,4 +622,11 @@ final public class FiltersPanel extends JPanel { private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton(); // End of variables declaration//GEN-END:variables + void setDeviceAccountTypeEnabled(boolean enableDeviceAccountType) { + deviceAccountTypeEnabled = enableDeviceAccountType; + JCheckBox deviceCheckbox = accountTypeMap.get(Account.Type.DEVICE); + if (deviceCheckbox != null) { + deviceCheckbox.setEnabled(deviceAccountTypeEnabled); + } + } } From 01859fc01294a02b562f647896a206691019dc40 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 6 Feb 2018 13:53:24 +0100 Subject: [PATCH 035/128] disable drag and drop that led to exceptions --- .../org/sleuthkit/autopsy/communications/VisualizationPanel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 9147552bcd..0caf011521 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -122,6 +122,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.setAutoScroll(true); graphComponent.setAutoscrolls(true); graphComponent.setConnectable(false); + graphComponent.setDragEnabled(false); graphComponent.setKeepSelectionVisibleOnZoom(true); graphComponent.setOpaque(true); graphComponent.setToolTips(true); From 702eab93d8b72fc31174f1bc74c1eeea5cf71a17 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 6 Feb 2018 15:43:44 +0100 Subject: [PATCH 036/128] add lock/unlock icons --- .../communications/VisualizationPanel.java | 26 +++++++++--------- .../images/lock_large_locked.png | Bin 0 -> 1688 bytes .../images/lock_large_unlocked.png | Bin 0 -> 1684 bytes 3 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/lock_large_locked.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/lock_large_unlocked.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 0caf011521..096abcc93a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -43,7 +43,6 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.beans.PropertyVetoException; -import java.net.URL; import java.util.Arrays; import static java.util.Collections.singleton; import java.util.EnumSet; @@ -92,13 +91,17 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); - - private static final URL MARKER_PIN_URL = VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png"); - static final private ImageIcon pinIcon = new ImageIcon(MARKER_PIN_URL); + private static final String BASE_IMAGE_PATH = "/org/sleuthkit/autopsy/communications/images"; + static final private ImageIcon pinIcon = + new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/marker--pin.png")); static final private ImageIcon addPinIcon = - new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--plus.png")); + new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/marker--plus.png")); static final private ImageIcon unpinIcon = - new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--minus.png")); + new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/marker--minus.png")); + static final private ImageIcon unlockIcon = + new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_unlocked.png")); + static final private ImageIcon lockIcon = + new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_locked.png")); private final ExplorerManager vizEM = new ExplorerManager(); private final ExplorerManager gacEM = new ExplorerManager(); @@ -186,14 +189,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider })); } else { - jPopupMenu.add(new JMenuItem(new AbstractAction("Lock Account " + cellAt.getId(), addPinIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Lock Account " + cellAt.getId(), unlockIcon) { @Override public void actionPerformed(ActionEvent e) { graph.lockAccount((AccountDeviceInstanceKey) cellAt.getValue()); } })); - jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock Account " + cellAt.getId(), addPinIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock Account " + cellAt.getId(), lockIcon) { @Override public void actionPerformed(ActionEvent e) { graph.unlockAccount((AccountDeviceInstanceKey) cellAt.getValue()); @@ -254,7 +257,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider if (pinEvent.isReplace()) { graph.resetGraph(); } - + graph.pinAccount(pinEvent.getAccountDeviceInstances()); rebuildGraph(); } catch (TskCoreException ex) { @@ -287,14 +290,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void rebuildGraph() throws TskCoreException { - SwingWorker rebuild = graph.rebuild(new ProgressIndicatorImpl(), commsManager, currentFilter); + SwingWorker rebuild = graph.rebuild(new ProgressIndicatorImpl(), commsManager, currentFilter); rebuild.execute(); } - - @Override public void addNotify() { super.addNotify(); @@ -529,7 +530,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getView().setTranslate(new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY())); - // graphComponent.zoomActual(); // graphComponent.zoomAndCenter(); // graph.getGraphBounds().getWidth() diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/lock_large_locked.png b/Core/src/org/sleuthkit/autopsy/communications/images/lock_large_locked.png new file mode 100644 index 0000000000000000000000000000000000000000..527a77429601b029873fef276f332c6aba7a6720 GIT binary patch literal 1688 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ za0`JjxV%QuQiw3m9zdD+&^mvr|hHl2X$%^K6wA z6cm&cGE;1o!cBb*d<&dYGcrA@ic*8C{6dnevXd=Slr%ES=p7%)(>;{wt)MJZ`kK`w4kJAph~rHqo20xNy} z^73-Ma$~*xqI7*jOG`_A10#JSBVC{h-Qvo;lEez#ykcdDAuw}XQj3#|G7CyF^Yauy z<|ZcPmzLNnDS<441E}W`3*Z*z6+;6L7>xQAi3R$GdItJ%lYts-fPz-eMX8A;`9&f5 z`8jqF@4ICdrIzOxWfvXKNJY6lDt3ta;vT|@H_10ySAGbl&GYwVPTQ8$z`sDS~RZ(FX+tQow*i9poo8aY6x>k_->8 zg3_WKaI~Z(>!;?V=BDPA6a&M|5H-wFlC9G6i*gf7>@sr`(^K^e^3rYe5rTN#2XQqp zSpbuzK0IB*oDPplnlKRIP7~uHhSJiKlInfRz!F0gR5*YWEhsyLWag$a80j0D>l;`o z%vluyR3HOV5S*V@Ql40p%77(F&$#Bs%)r2S#nZ(x#G-fUM1K!yN0DRuXTHhvNoua2 z6!!RFx0!;Ri_J?{t%xcu5!)>nW_NMKon700Z%_3Efjt~tk75P)XlR5vvMgig#Q`;H4JvdjziDs|>Kc2WERIC(Tb;;+-Q{xc;u{_Az8 z7)}x02})D8-I`|iA+xObdELG#60@#1J>+%eUc#`a+*QEo#AKQGMq|K@DCcxxy3g+E3G<-6+`R%G}y%!;d=J@3*JeT!!s zEpE@Q7b%EeYde|4ZTCrLhld$a3U}Ta8Q*Su^?i5RO!@Q|67wcA>})#B>E)LCJ?5H= z(bl%kjpZFKhl}FMotH{8fKn8e2F{3tbM6?!OWA*pRMOR$8x3|w`dgWa^QA#xGL~yuHludD?7@MIb5A^ z#iBli^_WJ_eMyl|(}VsP++ds1W#H{P!&3HZukV97Rm}U=$}=Af{?yI61~#wFEAZ2c)I$ztaD0e0sz`w4#@xj literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/lock_large_unlocked.png b/Core/src/org/sleuthkit/autopsy/communications/images/lock_large_unlocked.png new file mode 100644 index 0000000000000000000000000000000000000000..9df763fb96d645789619b56a482ab5c8080aa214 GIT binary patch literal 1684 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ za0`JjxV%QuQiw3m9zdD+&^mvr|hHl2X$%^K6wA z6cm&cGE;1o!cBb*d<&dYGcrA@ic*8C{6dnevXd=Slr%ES=p7%)(>;{wt)MJZ`kK`w4kJAph~rHqo20xNy} z^73-Ma$~*xqI7*jOG`_A10#JSBVC{h-Qvo;lEez#ykcdDAuw}XQj3#|G7CyF^Yauy z<|ZcPmzLNnDS<441E}W`3*Z*z6+;6L7>xQAi3R$GdItJ%lYts-fPz-eMX8A;`9&f5 z`8jqF@4ICdrIzOxWfvXKNJY6lDt3ta;vT|@H_10ySAGbl&GYwVPTQ8$z`sDS~RZ(FX+tQow*i9poo8aY6x>k_->8 zg3_WKaI~Z(>!;?V=BDPA6a&M|5H-wFlC9G6i*gf7>@sr`(^K^e^3rYe5rTN#2XQqp zSpbuzK0IB*oDPplnlKRIP7~uHhSJiKlInfRz!F0gR5*YWEhsyLWag$a80j0D>l;`o z%vluyR3HOV5S*V@Ql40p%77(F&$#Bs%)r2S!PCVt#GB-blTC%CDwSI*l%fS*S7nP0$tz5SvB}8kiJT3pL#eLiM;)k4TO(T_C7#`gG+MMni9cScIE$^^uk*jFy z_w#q^K8rYf;i@S7eB*SJ?(waD%^^p*8QL;?xe}*L(wJjeoBb^%@4#nicfIIMHTs>0 zX1cge7QS4Zw|Zi*!CQ6K=)U6%>yMr&VP^IA%bd$L`z!-Ua?v8swU-wpoLqT_t=vE7 z;(^_(H$QCDse8VohP!8GC2xwMcid5kpJ815@mH^3U&y*Y{YLEz$MA-k8jd&q+!ni&+V9>UU-<7% z;GBd1w#`$Ez1Q2Y;FsJSL54rOj{k9*di9z7GyA%P{~4{H4!CvvE}m!`$yk^ppi|9I zkvDh2L*ez>O>O7B*7gOh-8|`yefklvgy}MGCN(Ecm5^Jdu=MDM0M^4=TW1DYsvp_t z_&J<+|GrCN{=IKo3V8Kn4i`&LVc)heq>TCV=hyiGe Date: Tue, 6 Feb 2018 15:55:57 +0100 Subject: [PATCH 037/128] move adding edges/vertices off the EDT. remove unneeded RelationshipModel --- .../autopsy/communications/mxGraphImpl.java | 55 ++----------------- 1 file changed, 6 insertions(+), 49 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 5f7397b1b5..0d64d2f54e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -30,7 +30,6 @@ import com.mxgraph.view.mxStylesheet; import java.net.URL; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -77,6 +76,7 @@ final class mxGraphImpl extends mxGraph { public mxGraphImpl() { super(mxStylesheet); + setAutoSizeCells(true); setCellsCloneable(false); setDropEnabled(false); setCellsCloneable(false); @@ -167,37 +167,6 @@ final class mxGraphImpl extends mxGraph { lockedAccountDevices.clear(); } - private static class RelationshipModel { - - @Override - public String toString() { - return adiKey1.getAccountDeviceInstance().getAccount().getTypeSpecificID() - + "<-" + relationshipSources.size() + "->" - + adiKey2.getAccountDeviceInstance().getAccount().getTypeSpecificID(); - } - - private final List relationshipSources; - private final AccountDeviceInstanceKey adiKey1; - private final AccountDeviceInstanceKey adiKey2; - - private RelationshipModel(List relationships, AccountDeviceInstanceKey adiKey1, AccountDeviceInstanceKey adiKey2) { - this.relationshipSources = relationships; - this.adiKey1 = adiKey1; - this.adiKey2 = adiKey2; - } - - public List getSources() { - return Collections.unmodifiableList(relationshipSources); - } - - public AccountDeviceInstanceKey getAccount1() { - return adiKey1; - } - - public AccountDeviceInstanceKey getAccount2() { - return adiKey2; - } - } private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); @@ -245,7 +214,7 @@ final class mxGraphImpl extends mxGraph { return edge; } - class SwingWorkerImpl extends SwingWorker, mxGraphImpl.RelationshipModel> { + class SwingWorkerImpl extends SwingWorker { private final ProgressIndicator progress; private final CommunicationsManager commsManager; @@ -258,11 +227,10 @@ final class mxGraphImpl extends mxGraph { } @Override - protected Set doInBackground() throws Exception { + protected Void doInBackground() throws Exception { progress.start("Loading accounts", pinnedAccountDevices.size()); int i = 0; - Set relationshipModels = new HashSet<>(); try { /** * set to keep track of accounts related to pinned accounts @@ -296,9 +264,8 @@ final class mxGraphImpl extends mxGraph { adiKey2.getAccountDeviceInstance(), currentFilter); if (relationships.size() > 0) { - mxGraphImpl.RelationshipModel relationshipModel = new RelationshipModel(relationships, adiKey1, adiKey2); - publish(relationshipModel); - progress.progress(relationshipModel.toString()); + mxCell addEdge = addEdge(relationships,adiKey1,adiKey2); + progress.progress(addEdge.getId()); } } progress.progress(i); @@ -307,17 +274,7 @@ final class mxGraphImpl extends mxGraph { logger.log(Level.SEVERE, "Error", tskCoreException); } finally { } - return relationshipModels; - } - - @Override - protected void process(List chunks) { - super.process(chunks); - for (mxGraphImpl.RelationshipModel relationShipModel : chunks) { - mxCell addEdge = addEdge(relationShipModel.getSources(), - relationShipModel.getAccount1(), - relationShipModel.getAccount2()); - } + return null; } @Override From 3e534832391ff70fe0c8a311cdbd6deae419fa05 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 6 Feb 2018 16:04:42 +0100 Subject: [PATCH 038/128] add reset zoom button, rename buttons --- .../communications/VisualizationPanel.form | 72 +++++---- .../communications/VisualizationPanel.java | 138 ++++++++++-------- 2 files changed, 122 insertions(+), 88 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 13517bf10b..7010f8f9cf 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -50,56 +50,56 @@ - + - + - + - + - + - + - + - + - + - + - + - + @@ -120,22 +120,6 @@ - - - - - - - - - - - - - - - - @@ -152,6 +136,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 096abcc93a..2d3f26cbc1 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -349,14 +349,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider splitPane = new JSplitPane(); borderLayoutPanel = new JPanel(); jToolBar1 = new JToolBar(); - jButton6 = new JButton(); - jButton1 = new JButton(); - jButton8 = new JButton(); - jButton7 = new JButton(); + hierarchyLayoutButton = new JButton(); + fastOrganicLayoutButton = new JButton(); + OrganicLayoutButton = new JButton(); + circleLayoutButton = new JButton(); jSeparator1 = new JToolBar.Separator(); zoomOutButton = new JButton(); - fitGraphButton = new JButton(); zoomInButton = new JButton(); + zoomActualButton = new JButton(); + fitZoomButton = new JButton(); statusPanel = new JPanel(); progressBar = new JProgressBar(); @@ -369,49 +370,49 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jToolBar1.setRollover(true); - jButton6.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton6.text")); // NOI18N - jButton6.setFocusable(false); - jButton6.setHorizontalTextPosition(SwingConstants.CENTER); - jButton6.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton6.addActionListener(new ActionListener() { + hierarchyLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.hierarchyLayoutButton.text")); // NOI18N + hierarchyLayoutButton.setFocusable(false); + hierarchyLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER); + hierarchyLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); + hierarchyLayoutButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - jButton6ActionPerformed(evt); + hierarchyLayoutButtonActionPerformed(evt); } }); - jToolBar1.add(jButton6); + jToolBar1.add(hierarchyLayoutButton); - jButton1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N - jButton1.setFocusable(false); - jButton1.setHorizontalTextPosition(SwingConstants.CENTER); - jButton1.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton1.addActionListener(new ActionListener() { + fastOrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N + fastOrganicLayoutButton.setFocusable(false); + fastOrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER); + fastOrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); + fastOrganicLayoutButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - jButton1ActionPerformed(evt); + fastOrganicLayoutButtonActionPerformed(evt); } }); - jToolBar1.add(jButton1); + jToolBar1.add(fastOrganicLayoutButton); - jButton8.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton8.text")); // NOI18N - jButton8.setFocusable(false); - jButton8.setHorizontalTextPosition(SwingConstants.CENTER); - jButton8.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton8.addActionListener(new ActionListener() { + OrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.OrganicLayoutButton.text")); // NOI18N + OrganicLayoutButton.setFocusable(false); + OrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER); + OrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); + OrganicLayoutButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - jButton8ActionPerformed(evt); + OrganicLayoutButtonActionPerformed(evt); } }); - jToolBar1.add(jButton8); + jToolBar1.add(OrganicLayoutButton); - jButton7.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton7.text")); // NOI18N - jButton7.setFocusable(false); - jButton7.setHorizontalTextPosition(SwingConstants.CENTER); - jButton7.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton7.addActionListener(new ActionListener() { + circleLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.circleLayoutButton.text")); // NOI18N + circleLayoutButton.setFocusable(false); + circleLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER); + circleLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); + circleLayoutButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - jButton7ActionPerformed(evt); + circleLayoutButtonActionPerformed(evt); } }); - jToolBar1.add(jButton7); + jToolBar1.add(circleLayoutButton); jToolBar1.add(jSeparator1); zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N @@ -426,18 +427,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); jToolBar1.add(zoomOutButton); - fitGraphButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N - fitGraphButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitGraphButton.text")); // NOI18N - fitGraphButton.setFocusable(false); - fitGraphButton.setHorizontalTextPosition(SwingConstants.CENTER); - fitGraphButton.setVerticalTextPosition(SwingConstants.BOTTOM); - fitGraphButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - fitGraphButtonActionPerformed(evt); - } - }); - jToolBar1.add(fitGraphButton); - zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N zoomInButton.setFocusable(false); @@ -450,6 +439,30 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); jToolBar1.add(zoomInButton); + zoomActualButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N + zoomActualButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N + zoomActualButton.setFocusable(false); + zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER); + zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM); + zoomActualButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + zoomActualButtonActionPerformed(evt); + } + }); + jToolBar1.add(zoomActualButton); + + fitZoomButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N + fitZoomButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N + fitZoomButton.setFocusable(false); + fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER); + fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM); + fitZoomButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + fitZoomButtonActionPerformed(evt); + } + }); + jToolBar1.add(fitZoomButton); + borderLayoutPanel.add(jToolBar1, BorderLayout.NORTH); progressBar.setMaximumSize(new Dimension(200, 14)); @@ -477,9 +490,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider add(splitPane, BorderLayout.CENTER); }// //GEN-END:initComponents - private void jButton1ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed + private void fastOrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fastOrganicLayoutButtonActionPerformed morph(new mxFastOrganicLayout(graph)); - }//GEN-LAST:event_jButton1ActionPerformed + }//GEN-LAST:event_fastOrganicLayoutButtonActionPerformed private void zoomInButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed graphComponent.zoomIn(); @@ -489,17 +502,17 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.zoomOut(); }//GEN-LAST:event_zoomOutButtonActionPerformed - private void jButton6ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton6ActionPerformed + private void hierarchyLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_hierarchyLayoutButtonActionPerformed morph(new mxHierarchicalLayout(graph)); - }//GEN-LAST:event_jButton6ActionPerformed + }//GEN-LAST:event_hierarchyLayoutButtonActionPerformed - private void jButton7ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton7ActionPerformed + private void circleLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_circleLayoutButtonActionPerformed morph(new mxCircleLayout(graph)); - }//GEN-LAST:event_jButton7ActionPerformed + }//GEN-LAST:event_circleLayoutButtonActionPerformed - private void jButton8ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton8ActionPerformed + private void OrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_OrganicLayoutButtonActionPerformed applyOrganicLayout(10); - }//GEN-LAST:event_jButton8ActionPerformed + }//GEN-LAST:event_OrganicLayoutButtonActionPerformed private void applyOrganicLayout(int iterations) { mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph) { @@ -513,9 +526,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider morph(mxOrganicLayout); } - private void fitGraphButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitGraphButtonActionPerformed + private void fitZoomButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitZoomButtonActionPerformed fitGraph(); - }//GEN-LAST:event_fitGraphButtonActionPerformed + }//GEN-LAST:event_fitZoomButtonActionPerformed + + private void zoomActualButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomActualButtonActionPerformed + graphComponent.zoomActual(); + }//GEN-LAST:event_zoomActualButtonActionPerformed private void fitGraph() { final Object[] childVertices = graph.getChildVertices(graph.getDefaultParent()); @@ -560,17 +577,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } // Variables declaration - do not modify//GEN-BEGIN:variables + private JButton OrganicLayoutButton; private JPanel borderLayoutPanel; - private JButton fitGraphButton; - private JButton jButton1; - private JButton jButton6; - private JButton jButton7; - private JButton jButton8; + private JButton circleLayoutButton; + private JButton fastOrganicLayoutButton; + private JButton fitZoomButton; + private JButton hierarchyLayoutButton; private JToolBar.Separator jSeparator1; private JToolBar jToolBar1; private JProgressBar progressBar; private JSplitPane splitPane; private JPanel statusPanel; + private JButton zoomActualButton; private JButton zoomInButton; private JButton zoomOutButton; // End of variables declaration//GEN-END:variables From 5d948b7dfd2cf22ed44889a889961d6b2f73c9e7 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 6 Feb 2018 17:19:28 +0100 Subject: [PATCH 039/128] show instructional message when there are no pinned accounts --- .../autopsy/communications/Bundle.properties | 13 +- .../communications/VisualizationPanel.form | 192 ++++++++++++++---- .../communications/VisualizationPanel.java | 188 ++++++++++++----- .../autopsy/communications/mxGraphImpl.java | 7 +- 4 files changed, 298 insertions(+), 102 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 0161fd855c..27068a5f55 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -23,6 +23,15 @@ CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize VisualizationPanel.jButton6.text=Hierarchy VisualizationPanel.jButton7.text=Circle VisualizationPanel.jButton8.text=Organic -VisualizationPanel.zoomOutButton.text= -VisualizationPanel.zoomInButton.text= VisualizationPanel.fitGraphButton.text= +VisualizationPanel.statusLabel.text= +VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. +VisualizationPanel.jLabel1.text=Layouts: +VisualizationPanel.fitZoomButton.text= +VisualizationPanel.zoomActualButton.text= +VisualizationPanel.zoomInButton.text= +VisualizationPanel.zoomOutButton.text= +VisualizationPanel.circleLayoutButton.text=Circle +VisualizationPanel.OrganicLayoutButton.text=Organic +VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic +VisualizationPanel.hierarchyLayoutButton.text=Hierarchy diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 7010f8f9cf..baca6eec89 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -11,7 +11,7 @@ - + @@ -38,18 +38,166 @@ - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -170,44 +318,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 2d3f26cbc1..0dd0c4844d 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -52,16 +52,19 @@ import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JButton; +import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JProgressBar; import javax.swing.JSplitPane; +import javax.swing.JTextArea; import javax.swing.JToolBar; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; 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; @@ -289,11 +292,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } private void rebuildGraph() throws TskCoreException { - - SwingWorker rebuild = graph.rebuild(new ProgressIndicatorImpl(), commsManager, currentFilter); - - rebuild.execute(); - + if (graph.hasPinnedAccounts()) { + borderLayoutPanel.remove(jPanel1); + borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); + SwingWorker rebuild = graph.rebuild(new ProgressIndicatorImpl(), commsManager, currentFilter); + rebuild.execute(); + } else { + borderLayoutPanel.remove(graphComponent); + borderLayoutPanel.add(jPanel1, BorderLayout.CENTER); + } } @Override @@ -348,7 +355,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider splitPane = new JSplitPane(); borderLayoutPanel = new JPanel(); - jToolBar1 = new JToolBar(); + jToolBar2 = new JToolBar(); + statusLabel = new JLabel(); + progresPanel = new JPanel(); + progressBar = new JProgressBar(); + jPanel1 = new JPanel(); + jTextArea1 = new JTextArea(); + jPanel2 = new JPanel(); + jLabel1 = new JLabel(); hierarchyLayoutButton = new JButton(); fastOrganicLayoutButton = new JButton(); OrganicLayoutButton = new JButton(); @@ -358,8 +372,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomInButton = new JButton(); zoomActualButton = new JButton(); fitZoomButton = new JButton(); - statusPanel = new JPanel(); - progressBar = new JProgressBar(); setLayout(new BorderLayout()); @@ -368,7 +380,56 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider borderLayoutPanel.setLayout(new BorderLayout()); - jToolBar1.setRollover(true); + jToolBar2.setFloatable(false); + jToolBar2.setRollover(true); + + statusLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.statusLabel.text")); // NOI18N + jToolBar2.add(statusLabel); + + progressBar.setMaximumSize(new Dimension(200, 14)); + progressBar.setStringPainted(true); + + GroupLayout progresPanelLayout = new GroupLayout(progresPanel); + progresPanel.setLayout(progresPanelLayout); + progresPanelLayout.setHorizontalGroup(progresPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, progresPanelLayout.createSequentialGroup() + .add(0, 651, Short.MAX_VALUE) + .add(progressBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + ); + progresPanelLayout.setVerticalGroup(progresPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, progresPanelLayout.createSequentialGroup() + .add(0, 0, 0) + .add(progressBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + ); + + jToolBar2.add(progresPanel); + + borderLayoutPanel.add(jToolBar2, BorderLayout.PAGE_END); + + jTextArea1.setBackground(new Color(240, 240, 240)); + jTextArea1.setColumns(20); + jTextArea1.setLineWrap(true); + jTextArea1.setRows(5); + jTextArea1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N + + GroupLayout jPanel1Layout = new GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup(jPanel1Layout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, jPanel1Layout.createSequentialGroup() + .addContainerGap(213, Short.MAX_VALUE) + .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 372, GroupLayout.PREFERRED_SIZE) + .addContainerGap(214, Short.MAX_VALUE)) + ); + jPanel1Layout.setVerticalGroup(jPanel1Layout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, jPanel1Layout.createSequentialGroup() + .addContainerGap(200, Short.MAX_VALUE) + .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 43, GroupLayout.PREFERRED_SIZE) + .addContainerGap(200, Short.MAX_VALUE)) + ); + + borderLayoutPanel.add(jPanel1, BorderLayout.CENTER); + + jLabel1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel1.text")); // NOI18N hierarchyLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.hierarchyLayoutButton.text")); // NOI18N hierarchyLayoutButton.setFocusable(false); @@ -379,7 +440,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider hierarchyLayoutButtonActionPerformed(evt); } }); - jToolBar1.add(hierarchyLayoutButton); fastOrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fastOrganicLayoutButton.text")); // NOI18N fastOrganicLayoutButton.setFocusable(false); @@ -390,7 +450,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider fastOrganicLayoutButtonActionPerformed(evt); } }); - jToolBar1.add(fastOrganicLayoutButton); OrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.OrganicLayoutButton.text")); // NOI18N OrganicLayoutButton.setFocusable(false); @@ -401,7 +460,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider OrganicLayoutButtonActionPerformed(evt); } }); - jToolBar1.add(OrganicLayoutButton); circleLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.circleLayoutButton.text")); // NOI18N circleLayoutButton.setFocusable(false); @@ -412,8 +470,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider circleLayoutButtonActionPerformed(evt); } }); - jToolBar1.add(circleLayoutButton); - jToolBar1.add(jSeparator1); zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N @@ -425,7 +481,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomOutButtonActionPerformed(evt); } }); - jToolBar1.add(zoomOutButton); zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N @@ -437,7 +492,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomInButtonActionPerformed(evt); } }); - jToolBar1.add(zoomInButton); zoomActualButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N zoomActualButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N @@ -449,7 +503,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomActualButtonActionPerformed(evt); } }); - jToolBar1.add(zoomActualButton); fitZoomButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N fitZoomButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N @@ -461,38 +514,64 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider fitZoomButtonActionPerformed(evt); } }); - jToolBar1.add(fitZoomButton); - borderLayoutPanel.add(jToolBar1, BorderLayout.NORTH); - - progressBar.setMaximumSize(new Dimension(200, 14)); - progressBar.setStringPainted(true); - - GroupLayout statusPanelLayout = new GroupLayout(statusPanel); - statusPanel.setLayout(statusPanelLayout); - statusPanelLayout.setHorizontalGroup(statusPanelLayout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, statusPanelLayout.createSequentialGroup() - .addContainerGap(516, Short.MAX_VALUE) - .add(progressBar, GroupLayout.PREFERRED_SIZE, 200, GroupLayout.PREFERRED_SIZE) - .addContainerGap()) - ); - statusPanelLayout.setVerticalGroup(statusPanelLayout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, statusPanelLayout.createSequentialGroup() + GroupLayout jPanel2Layout = new GroupLayout(jPanel2); + jPanel2.setLayout(jPanel2Layout); + jPanel2Layout.setHorizontalGroup(jPanel2Layout.createParallelGroup(GroupLayout.LEADING) + .add(jPanel2Layout.createSequentialGroup() .add(3, 3, 3) - .add(progressBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE) + .add(jLabel1) + .addPreferredGap(LayoutStyle.RELATED) + .add(fastOrganicLayoutButton) + .addPreferredGap(LayoutStyle.RELATED) + .add(OrganicLayoutButton) + .addPreferredGap(LayoutStyle.RELATED) + .add(hierarchyLayoutButton) + .addPreferredGap(LayoutStyle.RELATED) + .add(circleLayoutButton) + .addPreferredGap(LayoutStyle.RELATED) + .add(jSeparator1, GroupLayout.PREFERRED_SIZE, 10, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(zoomOutButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(zoomInButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE) + .addContainerGap(214, Short.MAX_VALUE)) + ); + jPanel2Layout.setVerticalGroup(jPanel2Layout.createParallelGroup(GroupLayout.LEADING) + .add(jPanel2Layout.createSequentialGroup() + .add(3, 3, 3) + .add(jPanel2Layout.createParallelGroup(GroupLayout.CENTER) + .add(jLabel1, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE) + .add(hierarchyLayoutButton) + .add(fastOrganicLayoutButton) + .add(OrganicLayoutButton) + .add(circleLayoutButton) + .add(jSeparator1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(zoomOutButton) + .add(zoomInButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(zoomActualButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(fitZoomButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .add(3, 3, 3)) ); - borderLayoutPanel.add(statusPanel, BorderLayout.SOUTH); + borderLayoutPanel.add(jPanel2, BorderLayout.PAGE_START); splitPane.setLeftComponent(borderLayoutPanel); add(splitPane, BorderLayout.CENTER); }// //GEN-END:initComponents - private void fastOrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fastOrganicLayoutButtonActionPerformed - morph(new mxFastOrganicLayout(graph)); - }//GEN-LAST:event_fastOrganicLayoutButtonActionPerformed + private void fitZoomButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitZoomButtonActionPerformed + fitGraph(); + }//GEN-LAST:event_fitZoomButtonActionPerformed + + private void zoomActualButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomActualButtonActionPerformed + graphComponent.zoomActual(); + }//GEN-LAST:event_zoomActualButtonActionPerformed private void zoomInButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed graphComponent.zoomIn(); @@ -502,10 +581,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.zoomOut(); }//GEN-LAST:event_zoomOutButtonActionPerformed - private void hierarchyLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_hierarchyLayoutButtonActionPerformed - morph(new mxHierarchicalLayout(graph)); - }//GEN-LAST:event_hierarchyLayoutButtonActionPerformed - private void circleLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_circleLayoutButtonActionPerformed morph(new mxCircleLayout(graph)); }//GEN-LAST:event_circleLayoutButtonActionPerformed @@ -514,6 +589,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider applyOrganicLayout(10); }//GEN-LAST:event_OrganicLayoutButtonActionPerformed + private void fastOrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fastOrganicLayoutButtonActionPerformed + morph(new mxFastOrganicLayout(graph)); + }//GEN-LAST:event_fastOrganicLayoutButtonActionPerformed + + private void hierarchyLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_hierarchyLayoutButtonActionPerformed + morph(new mxHierarchicalLayout(graph)); + }//GEN-LAST:event_hierarchyLayoutButtonActionPerformed + private void applyOrganicLayout(int iterations) { mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph) { @Override @@ -526,14 +609,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider morph(mxOrganicLayout); } - private void fitZoomButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitZoomButtonActionPerformed - fitGraph(); - }//GEN-LAST:event_fitZoomButtonActionPerformed - - private void zoomActualButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomActualButtonActionPerformed - graphComponent.zoomActual(); - }//GEN-LAST:event_zoomActualButtonActionPerformed - private void fitGraph() { final Object[] childVertices = graph.getChildVertices(graph.getDefaultParent()); mxRectangle boundsForCells = graph.getBoundsForCells(childVertices, true, true, true); @@ -546,10 +621,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } graph.getView().setTranslate(new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY())); - -// graphComponent.zoomActual(); -// graphComponent.zoomAndCenter(); -// graph.getGraphBounds().getWidth() } private void morph(mxIGraphLayout layout) { @@ -583,11 +654,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JButton fastOrganicLayoutButton; private JButton fitZoomButton; private JButton hierarchyLayoutButton; + private JLabel jLabel1; + private JPanel jPanel1; + private JPanel jPanel2; private JToolBar.Separator jSeparator1; - private JToolBar jToolBar1; + private JTextArea jTextArea1; + private JToolBar jToolBar2; + private JPanel progresPanel; private JProgressBar progressBar; private JSplitPane splitPane; - private JPanel statusPanel; + private JLabel statusLabel; private JButton zoomActualButton; private JButton zoomInButton; private JButton zoomOutButton; diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 0d64d2f54e..da235f1526 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -167,7 +167,6 @@ final class mxGraphImpl extends mxGraph { lockedAccountDevices.clear(); } - private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + @@ -214,6 +213,9 @@ final class mxGraphImpl extends mxGraph { return edge; } + boolean hasPinnedAccounts() { + return pinnedAccountDevices.isEmpty() == false; } + class SwingWorkerImpl extends SwingWorker { private final ProgressIndicator progress; @@ -228,7 +230,6 @@ final class mxGraphImpl extends mxGraph { @Override protected Void doInBackground() throws Exception { - progress.start("Loading accounts", pinnedAccountDevices.size()); int i = 0; try { @@ -264,7 +265,7 @@ final class mxGraphImpl extends mxGraph { adiKey2.getAccountDeviceInstance(), currentFilter); if (relationships.size() > 0) { - mxCell addEdge = addEdge(relationships,adiKey1,adiKey2); + mxCell addEdge = addEdge(relationships, adiKey1, adiKey2); progress.progress(addEdge.getId()); } } From 168cc8bea64bec0c5c893a00b82433be0fffe9f1 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 7 Feb 2018 14:47:59 +0100 Subject: [PATCH 040/128] improve layout and zooming controls --- .../autopsy/communications/Bundle.properties | 2 + .../communications/VisualizationPanel.form | 22 ++++- .../communications/VisualizationPanel.java | 98 +++++++++++-------- .../autopsy/communications/mxGraphImpl.java | 34 ++++++- 4 files changed, 113 insertions(+), 43 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 27068a5f55..094538665e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -35,3 +35,5 @@ VisualizationPanel.circleLayoutButton.text=Circle VisualizationPanel.OrganicLayoutButton.text=Organic VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic VisualizationPanel.hierarchyLayoutButton.text=Hierarchy +VisualizationPanel.jLabel2.text=Zoom: +VisualizationPanel.zoomLabel.text=100% diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index baca6eec89..3353f365fe 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -165,7 +165,11 @@ - + + + + + @@ -184,6 +188,8 @@ + + @@ -316,6 +322,20 @@ + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 0dd0c4844d..69ea3b9368 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -43,6 +43,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.beans.PropertyVetoException; +import java.text.DecimalFormat; import java.util.Arrays; import static java.util.Collections.singleton; import java.util.EnumSet; @@ -138,41 +139,25 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider //install rubber band selection handler rubberband = new mxRubberband(graphComponent); -// new mxLayoutManager(graph) { -// final private mxOrganicLayout layout; -// private int counter; -// { -// this.layout = new mxOrganicLayout(graph); -// this.layout.setMaxIterations(1); -// } -// -// @Override -// protected void executeLayout(mxIGraphLayout layout, Object parent) { -// if (counter % 10 == 0) -// { -// super.executeLayout(layout, parent); -//// fitGraph(); -// } -// counter++; -// } -// -// @Override -// public mxIGraphLayout getLayout(Object parent) { -// if (graph.getModel().getChildCount(parent) > 0) { -// return layout; -// } -// return null; -// } -// }; - //right click handler - graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { + + graph.getView().addListener(mxEvent.SCALE, (Object sender, mxEventObject evt) -> { + zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getScale())); + }); + + graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, (Object sender, mxEventObject evt) -> { + zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getScale())); + }); + final MouseAdapter mouseAdapter = new MouseAdapter() { @Override public void mouseWheelMoved(MouseWheelEvent e) { super.mouseWheelMoved(e); if (e.getPreciseWheelRotation() > 0) { - graphComponent.zoomIn(); + graphComponent.zoomTo(graph.getScale() / graphComponent.getZoomFactor(), true); +// graphComponent.zoomIn(); } else if (e.getPreciseWheelRotation() < 0) { - graphComponent.zoomOut(); + graphComponent.zoomTo(graph.getScale() * graphComponent.getZoomFactor(), true); +// graphComponent.zoomOut(); + } } @@ -192,14 +177,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider })); } else { - jPopupMenu.add(new JMenuItem(new AbstractAction("Lock Account " + cellAt.getId(), unlockIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Lock Account " + cellAt.getId(), lockIcon) { @Override public void actionPerformed(ActionEvent e) { graph.lockAccount((AccountDeviceInstanceKey) cellAt.getValue()); } })); - jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock Account " + cellAt.getId(), lockIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock Account " + cellAt.getId(), unlockIcon) { @Override public void actionPerformed(ActionEvent e) { graph.unlockAccount((AccountDeviceInstanceKey) cellAt.getValue()); @@ -223,7 +208,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } } - }); + }; + //right click handler + graphComponent.getGraphControl().addMouseWheelListener(mouseAdapter); + graphComponent.getGraphControl().addMouseListener(mouseAdapter); splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); @@ -372,6 +360,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomInButton = new JButton(); zoomActualButton = new JButton(); fitZoomButton = new JButton(); + jLabel2 = new JLabel(); + zoomLabel = new JLabel(); setLayout(new BorderLayout()); @@ -515,6 +505,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } }); + jLabel2.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel2.text")); // NOI18N + + zoomLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomLabel.text")); // NOI18N + GroupLayout jPanel2Layout = new GroupLayout(jPanel2); jPanel2.setLayout(jPanel2Layout); jPanel2Layout.setHorizontalGroup(jPanel2Layout.createParallelGroup(GroupLayout.LEADING) @@ -539,7 +533,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider .add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.RELATED) .add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE) - .addContainerGap(214, Short.MAX_VALUE)) + .addPreferredGap(LayoutStyle.RELATED, 110, Short.MAX_VALUE) + .add(jLabel2) + .addPreferredGap(LayoutStyle.RELATED) + .add(zoomLabel) + .add(27, 27, 27)) ); jPanel2Layout.setVerticalGroup(jPanel2Layout.createParallelGroup(GroupLayout.LEADING) .add(jPanel2Layout.createSequentialGroup() @@ -554,7 +552,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider .add(zoomOutButton) .add(zoomInButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(zoomActualButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(fitZoomButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .add(fitZoomButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(jLabel2) + .add(zoomLabel)) .add(3, 3, 3)) ); @@ -610,17 +610,31 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } private void fitGraph() { - final Object[] childVertices = graph.getChildVertices(graph.getDefaultParent()); - mxRectangle boundsForCells = graph.getBoundsForCells(childVertices, true, true, true); - if (boundsForCells == null) { - boundsForCells = new mxRectangle(); - } + mxPoint translate = graph.getView().getTranslate(); - if (translate == null) { + if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) { translate = new mxPoint(); } + mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true); + if (boundsForCells == null||Double.isNaN( boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) { + boundsForCells = new mxRectangle(0,0,1,1); + } graph.getView().setTranslate(new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY())); + + boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true); + if (boundsForCells == null||Double.isNaN( boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) { + boundsForCells = new mxRectangle(0,0,1,1); + } + + Dimension size = graphComponent.getSize(); + + double widthFactor = size.getWidth() / boundsForCells.getWidth(); +// widthFactor = boundsForCells.getWidth() / size.getWidth(); + + graphComponent.zoom(widthFactor); + +// bounds = graph.getGraphBounds(); } private void morph(mxIGraphLayout layout) { @@ -655,6 +669,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JButton fitZoomButton; private JButton hierarchyLayoutButton; private JLabel jLabel1; + private JLabel jLabel2; private JPanel jPanel1; private JPanel jPanel2; private JToolBar.Separator jSeparator1; @@ -666,6 +681,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JLabel statusLabel; private JButton zoomActualButton; private JButton zoomInButton; + private JLabel zoomLabel; private JButton zoomOutButton; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index da235f1526..7da06754fb 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -93,6 +93,33 @@ final class mxGraphImpl extends mxGraph { setKeepEdgesInBackground(true); setResetEdgesOnMove(true); setHtmlLabels(true); + +// new mxLayoutManager(graph) { +// final private mxOrganicLayout layout; +// private int counter; +// { +// this.layout = new mxOrganicLayout(graph); +// this.layout.setMaxIterations(1); +// } +// +// @Override +// protected void executeLayout(mxIGraphLayout layout, Object parent) { +// if (counter % 10 == 0) +// { +// super.executeLayout(layout, parent); +//// fitGraph(); +// } +// counter++; +// } +// +// @Override +// public mxIGraphLayout getLayout(Object parent) { +// if (graph.getModel().getChildCount(parent) > 0) { +// return layout; +// } +// return null; +// } +// }; } @Override @@ -214,7 +241,12 @@ final class mxGraphImpl extends mxGraph { } boolean hasPinnedAccounts() { - return pinnedAccountDevices.isEmpty() == false; } + return pinnedAccountDevices.isEmpty() == false; + } + + double getScale() { + return getView().getScale(); + } class SwingWorkerImpl extends SwingWorker { From b6ea00af8c2bafd45138badb393c4c312cd45198 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 7 Feb 2018 16:47:48 +0100 Subject: [PATCH 041/128] locked nodes are not moved by SOME layouts. --- .../communications/VisualizationPanel.java | 85 ++++++++++--------- .../autopsy/communications/mxGraphImpl.java | 30 +++++-- 2 files changed, 68 insertions(+), 47 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 69ea3b9368..b193afe0b9 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -140,27 +140,25 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider //install rubber band selection handler rubberband = new mxRubberband(graphComponent); - graph.getView().addListener(mxEvent.SCALE, (Object sender, mxEventObject evt) -> { - zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getScale())); - }); + final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt) -> + zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getScale())); + graph.getView().addListener(mxEvent.SCALE, scaleListener); + graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener); - graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, (Object sender, mxEventObject evt) -> { - zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getScale())); - }); - final MouseAdapter mouseAdapter = new MouseAdapter() { + //right click handler + graphComponent.getGraphControl().addMouseWheelListener(new MouseAdapter() { @Override - public void mouseWheelMoved(MouseWheelEvent e) { + public void mouseWheelMoved(final MouseWheelEvent e) { super.mouseWheelMoved(e); if (e.getPreciseWheelRotation() > 0) { graphComponent.zoomTo(graph.getScale() / graphComponent.getZoomFactor(), true); -// graphComponent.zoomIn(); } else if (e.getPreciseWheelRotation() < 0) { graphComponent.zoomTo(graph.getScale() * graphComponent.getZoomFactor(), true); -// graphComponent.zoomOut(); - } } + }); + graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { super.mouseClicked(e); @@ -168,8 +166,27 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxICell cellAt = (mxICell) graphComponent.getCellAt(e.getX(), e.getY()); if (cellAt != null && cellAt.isVertex()) { JPopupMenu jPopupMenu = new JPopupMenu(); - if (graph.isAccountPinned((AccountDeviceInstanceKey) cellAt.getValue())) { - jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin Account " + cellAt.getId(), unpinIcon) { + AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue(); + + if (graph.isAccountLocked(adiKey)) { + jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock " + cellAt.getId(), unlockIcon) { + @Override + public void actionPerformed(ActionEvent e) { + graph.unlockAccount((AccountDeviceInstanceKey) cellAt.getValue()); + } + })); + + }else { + jPopupMenu.add(new JMenuItem(new AbstractAction("Lock " + cellAt.getId(), lockIcon) { + @Override + public void actionPerformed(ActionEvent e) { + graph.lockAccount((AccountDeviceInstanceKey) cellAt.getValue()); + } + })); + + } + if (graph.isAccountPinned(adiKey)) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin " + cellAt.getId(), unpinIcon) { @Override public void actionPerformed(ActionEvent e) { handleUnPinEvent(new CVTEvents.UnpinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()))); @@ -177,26 +194,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider })); } else { - jPopupMenu.add(new JMenuItem(new AbstractAction("Lock Account " + cellAt.getId(), lockIcon) { - @Override - public void actionPerformed(ActionEvent e) { - graph.lockAccount((AccountDeviceInstanceKey) cellAt.getValue()); - } - })); - - jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock Account " + cellAt.getId(), unlockIcon) { - @Override - public void actionPerformed(ActionEvent e) { - graph.unlockAccount((AccountDeviceInstanceKey) cellAt.getValue()); - } - })); - jPopupMenu.add(new JMenuItem(new AbstractAction("Pin Account " + cellAt.getId(), addPinIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Pin " + cellAt.getId(), addPinIcon) { @Override public void actionPerformed(ActionEvent e) { handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); } })); - jPopupMenu.add(new JMenuItem(new AbstractAction("Reset and Pin Account " + cellAt.getId(), pinIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Pin only " + cellAt.getId(), pinIcon) { @Override public void actionPerformed(ActionEvent e) { handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), true)); @@ -208,10 +212,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } } - }; - //right click handler - graphComponent.getGraphControl().addMouseWheelListener(mouseAdapter); - graphComponent.getGraphControl().addMouseListener(mouseAdapter); + }); splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); @@ -582,7 +583,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_zoomOutButtonActionPerformed private void circleLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_circleLayoutButtonActionPerformed - morph(new mxCircleLayout(graph)); + final mxCircleLayout mxCircleLayout = new mxCircleLayout(graph); + mxCircleLayout.setResetEdges(true); + morph(mxCircleLayout); }//GEN-LAST:event_circleLayoutButtonActionPerformed private void OrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_OrganicLayoutButtonActionPerformed @@ -590,11 +593,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_OrganicLayoutButtonActionPerformed private void fastOrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fastOrganicLayoutButtonActionPerformed - morph(new mxFastOrganicLayout(graph)); + final mxFastOrganicLayout mxFastOrganicLayout = new mxFastOrganicLayout(graph); + + morph(mxFastOrganicLayout); }//GEN-LAST:event_fastOrganicLayoutButtonActionPerformed private void hierarchyLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_hierarchyLayoutButtonActionPerformed - morph(new mxHierarchicalLayout(graph)); + final mxHierarchicalLayout mxHierarchicalLayout = new mxHierarchicalLayout(graph); + morph(mxHierarchicalLayout); }//GEN-LAST:event_hierarchyLayoutButtonActionPerformed private void applyOrganicLayout(int iterations) { @@ -605,6 +611,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } }; + mxOrganicLayout.setResetEdges(true); mxOrganicLayout.setMaxIterations(iterations); morph(mxOrganicLayout); } @@ -617,14 +624,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } mxRectangle boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true); - if (boundsForCells == null||Double.isNaN( boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) { - boundsForCells = new mxRectangle(0,0,1,1); + if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) { + boundsForCells = new mxRectangle(0, 0, 1, 1); } graph.getView().setTranslate(new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY())); boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true); - if (boundsForCells == null||Double.isNaN( boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) { - boundsForCells = new mxRectangle(0,0,1,1); + if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) { + boundsForCells = new mxRectangle(0, 0, 1, 1); } Dimension size = graphComponent.getSize(); diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 7da06754fb..8cda3c5e66 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -50,7 +50,8 @@ import org.sleuthkit.datamodel.TskCoreException; final class mxGraphImpl extends mxGraph { private static final Logger logger = Logger.getLogger(mxGraphImpl.class.getName()); - private static final URL MARKER_PIN_URL = VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png"); + private static final URL MARKER_PIN_URL = mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png"); + private static final URL LOCK_URL = mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/lock_large_locked.png"); static final private mxStylesheet mxStylesheet = new mxStylesheet(); private final HashSet pinnedAccountDevices = new HashSet<>(); @@ -128,8 +129,14 @@ final class mxGraphImpl extends mxGraph { if (mxCell.isEdge()) { return super.isCellMovable(cell); } else { - return super.isCellMovable(cell) - && false == lockedAccountDevices.contains((AccountDeviceInstanceKey) mxCell.getValue()); + final boolean cellMovable = super.isCellMovable(cell); + final boolean unlocked = false == lockedAccountDevices.contains((AccountDeviceInstanceKey) mxCell.getValue()); + + if (cellMovable && unlocked) { + return true; + } else { + return false; + } } } @@ -151,12 +158,15 @@ final class mxGraphImpl extends mxGraph { final String accountName = adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID(); String iconFileName = Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()); String label = "" + accountName; if (pinnedAccountDevices.contains(adiKey)) { label += ""; } - return "" + label + ""; + if (lockedAccountDevices.contains(adiKey)) { + label += ""; + } + return "
" + label + "
"; } else { return ""; } @@ -231,11 +241,11 @@ final class mxGraphImpl extends mxGraph { final HashSet hashSet = new HashSet<>(relSources); // edgeMap.put(relSource, edge); edge = (mxCell) insertEdge(getDefaultParent(), edgeName, hashSet, vertex1, vertex2, - "strokeWidth=" + Math.sqrt(hashSet.size())); + "strokeWidth=" + (Math.log(hashSet.size())+1)); } else { edge = (mxCell) edgesBetween[0]; ((Collection) edge.getValue()).addAll(relSources); - edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); + edge.setStyle("strokeWidth=" +( Math.log(((Collection) edge.getValue()).size())+1)); } return edge; } @@ -248,7 +258,11 @@ final class mxGraphImpl extends mxGraph { return getView().getScale(); } - class SwingWorkerImpl extends SwingWorker { + boolean isAccountLocked(AccountDeviceInstanceKey adiKey) { + return lockedAccountDevices.contains(adiKey); + } + + private class SwingWorkerImpl extends SwingWorker { private final ProgressIndicator progress; private final CommunicationsManager commsManager; From d1f07e033d95ae7de0bf32e8321e739853800ab1 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 9 Feb 2018 12:54:53 +0100 Subject: [PATCH 042/128] layouts respect cell 'locking' --- .../communications/VisualizationPanel.java | 76 ++++++++++++++++--- .../autopsy/communications/mxGraphImpl.java | 50 +++++------- 2 files changed, 86 insertions(+), 40 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index b193afe0b9..dd0406c7c0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -163,29 +163,29 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public void mouseClicked(MouseEvent e) { super.mouseClicked(e); if (SwingUtilities.isRightMouseButton(e)) { - mxICell cellAt = (mxICell) graphComponent.getCellAt(e.getX(), e.getY()); + mxCell cellAt = (mxCell) graphComponent.getCellAt(e.getX(), e.getY()); if (cellAt != null && cellAt.isVertex()) { JPopupMenu jPopupMenu = new JPopupMenu(); AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue(); - if (graph.isAccountLocked(adiKey)) { + if (graph.isVertexLocked(cellAt)) { jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock " + cellAt.getId(), unlockIcon) { @Override public void actionPerformed(ActionEvent e) { - graph.unlockAccount((AccountDeviceInstanceKey) cellAt.getValue()); + graph.unlockVertex(cellAt); } })); - }else { + } else { jPopupMenu.add(new JMenuItem(new AbstractAction("Lock " + cellAt.getId(), lockIcon) { @Override public void actionPerformed(ActionEvent e) { - graph.lockAccount((AccountDeviceInstanceKey) cellAt.getValue()); + graph.lockVertex(cellAt); } })); } - if (graph.isAccountPinned(adiKey)) { + if (graph.isAccountPinned(adiKey)) { jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin " + cellAt.getId(), unpinIcon) { @Override public void actionPerformed(ActionEvent e) { @@ -583,7 +583,22 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_zoomOutButtonActionPerformed private void circleLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_circleLayoutButtonActionPerformed - final mxCircleLayout mxCircleLayout = new mxCircleLayout(graph); + final mxCircleLayout mxCircleLayout = new mxCircleLayout(graph) { + @Override + public boolean isVertexIgnored(Object vertex) { + return super.isVertexIgnored(vertex) + || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + } + + @Override + public mxRectangle setVertexLocation(Object vertex, double x, double y) { + if (isVertexIgnored(vertex)) { + return getVertexBounds(vertex); + } else { + return super.setVertexLocation(vertex, x, y); + } + } + }; mxCircleLayout.setResetEdges(true); morph(mxCircleLayout); }//GEN-LAST:event_circleLayoutButtonActionPerformed @@ -593,23 +608,62 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_OrganicLayoutButtonActionPerformed private void fastOrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fastOrganicLayoutButtonActionPerformed - final mxFastOrganicLayout mxFastOrganicLayout = new mxFastOrganicLayout(graph); + final mxFastOrganicLayout mxFastOrganicLayout = new mxFastOrganicLayout(graph) { + @Override + public boolean isVertexIgnored(Object vertex) { + return super.isVertexIgnored(vertex) + || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + } + + @Override + public mxRectangle setVertexLocation(Object vertex, double x, double y) { + if (isVertexIgnored(vertex)) { + return getVertexBounds(vertex); + } else { + return super.setVertexLocation(vertex, x, y); + } + } + }; morph(mxFastOrganicLayout); }//GEN-LAST:event_fastOrganicLayoutButtonActionPerformed private void hierarchyLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_hierarchyLayoutButtonActionPerformed - final mxHierarchicalLayout mxHierarchicalLayout = new mxHierarchicalLayout(graph); + final mxHierarchicalLayout mxHierarchicalLayout = new mxHierarchicalLayout(graph) { + @Override + public boolean isVertexIgnored(Object vertex) { + return super.isVertexIgnored(vertex) + || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + } + + @Override + public mxRectangle setVertexLocation(Object vertex, double x, double y) { + if (isVertexIgnored(vertex)) { + return getVertexBounds(vertex); + } else { + return super.setVertexLocation(vertex, x, y); + } + } + }; morph(mxHierarchicalLayout); }//GEN-LAST:event_hierarchyLayoutButtonActionPerformed private void applyOrganicLayout(int iterations) { mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph) { @Override - public boolean isVertexMovable(Object vertex) { - return super.isVertexMovable(vertex); //To change body of generated methods, choose Tools | Templates. + public boolean isVertexIgnored(Object vertex) { + return super.isVertexIgnored(vertex) + || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); } + @Override + public mxRectangle setVertexLocation(Object vertex, double x, double y) { + if (isVertexIgnored(vertex)) { + return getVertexBounds(vertex); + } else { + return super.setVertexLocation(vertex, x, y); + } + } }; mxOrganicLayout.setResetEdges(true); mxOrganicLayout.setMaxIterations(iterations); diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 8cda3c5e66..101e803624 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -55,7 +55,7 @@ final class mxGraphImpl extends mxGraph { static final private mxStylesheet mxStylesheet = new mxStylesheet(); private final HashSet pinnedAccountDevices = new HashSet<>(); - private final HashSet lockedAccountDevices = new HashSet<>(); + private final HashSet lockedVertices = new HashSet<>(); private final Map nodeMap = new HashMap<>(); private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); @@ -123,23 +123,6 @@ final class mxGraphImpl extends mxGraph { // }; } - @Override - public boolean isCellMovable(Object cell) { - final mxICell mxCell = (mxICell) cell; - if (mxCell.isEdge()) { - return super.isCellMovable(cell); - } else { - final boolean cellMovable = super.isCellMovable(cell); - final boolean unlocked = false == lockedAccountDevices.contains((AccountDeviceInstanceKey) mxCell.getValue()); - - if (cellMovable && unlocked) { - return true; - } else { - return false; - } - } - } - void clear() { nodeMap.clear(); edgeMap.clear(); @@ -163,10 +146,10 @@ final class mxGraphImpl extends mxGraph { if (pinnedAccountDevices.contains(adiKey)) { label += ""; } - if (lockedAccountDevices.contains(adiKey)) { + if (lockedVertices.contains((mxCell) cell)) { label += ""; } - return "
" + label + "
"; + return "
" + label + "
"; } else { return ""; } @@ -185,12 +168,21 @@ final class mxGraphImpl extends mxGraph { pinnedAccountDevices.addAll(accountDeviceInstances); } - void lockAccount(AccountDeviceInstanceKey accountDeviceInstance) { - lockedAccountDevices.add(accountDeviceInstance); + void lockVertex(mxCell vertex) { + lockedVertices.add(vertex); + + final mxCellState state = getView().getState(vertex, true); + getView().clear(vertex, true, true); + getView().validate(); } - void unlockAccount(AccountDeviceInstanceKey accountDeviceInstance) { - lockedAccountDevices.remove(accountDeviceInstance); + void unlockVertex(mxCell vertex) { + lockedVertices.remove(vertex); + + final mxCellState state = getView().getState(vertex, true); + getView().updateLabel(state); + getView().updateLabelBounds(state); + getView().updateBoundingBox(state); } SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { @@ -201,7 +193,7 @@ final class mxGraphImpl extends mxGraph { void resetGraph() { clear(); pinnedAccountDevices.clear(); - lockedAccountDevices.clear(); + lockedVertices.clear(); } private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { @@ -241,11 +233,11 @@ final class mxGraphImpl extends mxGraph { final HashSet hashSet = new HashSet<>(relSources); // edgeMap.put(relSource, edge); edge = (mxCell) insertEdge(getDefaultParent(), edgeName, hashSet, vertex1, vertex2, - "strokeWidth=" + (Math.log(hashSet.size())+1)); + "strokeWidth=" + (Math.log(hashSet.size()) + 1)); } else { edge = (mxCell) edgesBetween[0]; ((Collection) edge.getValue()).addAll(relSources); - edge.setStyle("strokeWidth=" +( Math.log(((Collection) edge.getValue()).size())+1)); + edge.setStyle("strokeWidth=" + (Math.log(((Collection) edge.getValue()).size()) + 1)); } return edge; } @@ -258,8 +250,8 @@ final class mxGraphImpl extends mxGraph { return getView().getScale(); } - boolean isAccountLocked(AccountDeviceInstanceKey adiKey) { - return lockedAccountDevices.contains(adiKey); + boolean isVertexLocked(mxCell vertex) { + return lockedVertices.contains(vertex); } private class SwingWorkerImpl extends SwingWorker { From 9225730f02bff97fd6dd733ef9fcaa06773eb47e Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 9 Feb 2018 13:30:45 +0100 Subject: [PATCH 043/128] scale labes with node size --- .../communications/VisualizationPanel.form | 10 +- .../communications/VisualizationPanel.java | 176 +++++++++++------- .../autopsy/communications/mxGraphImpl.java | 4 +- 3 files changed, 119 insertions(+), 71 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 3353f365fe..cf08d3ba4c 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -59,13 +59,19 @@
+ + + + + + - - + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index dd0406c7c0..95fcfc52bc 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -34,6 +34,7 @@ import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxPoint; import com.mxgraph.util.mxRectangle; +import com.mxgraph.view.mxGraph; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; @@ -119,11 +120,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private CommunicationsManager commsManager; private CommunicationsFilter currentFilter; private final mxRubberband rubberband; + private mxFastOrganicLayout mxFastOrganicLayout; + private mxCircleLayout mxCircleLayout; + private final mxOrganicLayout mxOrganicLayout; + private mxHierarchicalLayout mxHierarchicalLayout; public VisualizationPanel() { initComponents(); graph = new mxGraphImpl(); + mxFastOrganicLayout = new mxFastOrganicLayoutImpl(graph); + mxCircleLayout = new mxCircleLayoutImpl(graph); + mxOrganicLayout = new mxOrganicLayoutImpl(graph); + mxHierarchicalLayout = new mxHierarchicalLayoutImpl(graph); + graphComponent = new mxGraphComponent(graph); graphComponent.setAutoExtend(true); graphComponent.setAutoScroll(true); @@ -377,6 +387,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider statusLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.statusLabel.text")); // NOI18N jToolBar2.add(statusLabel); + progresPanel.setMinimumSize(new Dimension(0, 20)); + progresPanel.setName(""); // NOI18N + progressBar.setMaximumSize(new Dimension(200, 14)); progressBar.setStringPainted(true); @@ -384,8 +397,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progresPanel.setLayout(progresPanelLayout); progresPanelLayout.setHorizontalGroup(progresPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(GroupLayout.TRAILING, progresPanelLayout.createSequentialGroup() - .add(0, 651, Short.MAX_VALUE) - .add(progressBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .add(0, 447, Short.MAX_VALUE) + .add(progressBar, GroupLayout.PREFERRED_SIZE, 350, GroupLayout.PREFERRED_SIZE)) ); progresPanelLayout.setVerticalGroup(progresPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(GroupLayout.TRAILING, progresPanelLayout.createSequentialGroup() @@ -583,23 +596,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_zoomOutButtonActionPerformed private void circleLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_circleLayoutButtonActionPerformed - final mxCircleLayout mxCircleLayout = new mxCircleLayout(graph) { - @Override - public boolean isVertexIgnored(Object vertex) { - return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); - } - - @Override - public mxRectangle setVertexLocation(Object vertex, double x, double y) { - if (isVertexIgnored(vertex)) { - return getVertexBounds(vertex); - } else { - return super.setVertexLocation(vertex, x, y); - } - } - }; - mxCircleLayout.setResetEdges(true); morph(mxCircleLayout); }//GEN-LAST:event_circleLayoutButtonActionPerformed @@ -608,64 +604,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_OrganicLayoutButtonActionPerformed private void fastOrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fastOrganicLayoutButtonActionPerformed - final mxFastOrganicLayout mxFastOrganicLayout = new mxFastOrganicLayout(graph) { - @Override - public boolean isVertexIgnored(Object vertex) { - return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); - } - - @Override - public mxRectangle setVertexLocation(Object vertex, double x, double y) { - if (isVertexIgnored(vertex)) { - return getVertexBounds(vertex); - } else { - return super.setVertexLocation(vertex, x, y); - } - } - }; morph(mxFastOrganicLayout); }//GEN-LAST:event_fastOrganicLayoutButtonActionPerformed private void hierarchyLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_hierarchyLayoutButtonActionPerformed - final mxHierarchicalLayout mxHierarchicalLayout = new mxHierarchicalLayout(graph) { - @Override - public boolean isVertexIgnored(Object vertex) { - return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); - } - @Override - public mxRectangle setVertexLocation(Object vertex, double x, double y) { - if (isVertexIgnored(vertex)) { - return getVertexBounds(vertex); - } else { - return super.setVertexLocation(vertex, x, y); - } - } - }; morph(mxHierarchicalLayout); }//GEN-LAST:event_hierarchyLayoutButtonActionPerformed private void applyOrganicLayout(int iterations) { - mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph) { - @Override - public boolean isVertexIgnored(Object vertex) { - return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); - } - - @Override - public mxRectangle setVertexLocation(Object vertex, double x, double y) { - if (isVertexIgnored(vertex)) { - return getVertexBounds(vertex); - } else { - return super.setVertexLocation(vertex, x, y); - } - } - }; - mxOrganicLayout.setResetEdges(true); mxOrganicLayout.setMaxIterations(iterations); morph(mxOrganicLayout); } @@ -850,4 +798,98 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); } } + + private class mxFastOrganicLayoutImpl extends mxFastOrganicLayout { + + public mxFastOrganicLayoutImpl(mxGraph graph) { + super(graph); + } + + @Override + public boolean isVertexIgnored(Object vertex) { + return super.isVertexIgnored(vertex) + || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + } + + @Override + public mxRectangle setVertexLocation(Object vertex, double x, double y) { + if (isVertexIgnored(vertex)) { + return getVertexBounds(vertex); + } else { + return super.setVertexLocation(vertex, x, y); + } + } + } + + private class mxCircleLayoutImpl extends mxCircleLayout { + + public mxCircleLayoutImpl(mxGraph graph) { + super(graph); + } + { + setResetEdges(true); + } + + @Override + public boolean isVertexIgnored(Object vertex) { + return super.isVertexIgnored(vertex) + || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + } + + @Override + public mxRectangle setVertexLocation(Object vertex, double x, double y) { + if (isVertexIgnored(vertex)) { + return getVertexBounds(vertex); + } else { + return super.setVertexLocation(vertex, x, y); + } + } + } + + private class mxOrganicLayoutImpl extends mxOrganicLayout { + + public mxOrganicLayoutImpl(mxGraph graph) { + super(graph); + } + { + setResetEdges(true); + } + + @Override + public boolean isVertexIgnored(Object vertex) { + return super.isVertexIgnored(vertex) + || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + } + + @Override + public mxRectangle setVertexLocation(Object vertex, double x, double y) { + if (isVertexIgnored(vertex)) { + return getVertexBounds(vertex); + } else { + return super.setVertexLocation(vertex, x, y); + } + } + } + + private class mxHierarchicalLayoutImpl extends mxHierarchicalLayout { + + public mxHierarchicalLayoutImpl(mxGraph graph) { + super(graph); + } + + @Override + public boolean isVertexIgnored(Object vertex) { + return super.isVertexIgnored(vertex) + || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + } + + @Override + public mxRectangle setVertexLocation(Object vertex, double x, double y) { + if (isVertexIgnored(vertex)) { + return getVertexBounds(vertex); + } else { + return super.setVertexLocation(vertex, x, y); + } + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 101e803624..b61cec8284 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -144,12 +144,12 @@ final class mxGraphImpl extends mxGraph { + mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/" + iconFileName) + "\">" + accountName; if (pinnedAccountDevices.contains(adiKey)) { - label += ""; + label = ""+label; } if (lockedVertices.contains((mxCell) cell)) { label += ""; } - return "
" + label + "
"; + return "
" + label + "
"; } else { return ""; } From cce4e918cd6218bbb05fb0bbdf44510edede4ff0 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 9 Feb 2018 17:51:32 +0100 Subject: [PATCH 044/128] improve fitGraph and other small tweaks --- .../autopsy/communications/Bundle.properties | 8 +- .../communications/VisualizationPanel.form | 28 ++- .../communications/VisualizationPanel.java | 163 +++++++++--------- .../autopsy/communications/mxGraphImpl.java | 49 +++--- 4 files changed, 137 insertions(+), 111 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 094538665e..271d5b3b5a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -24,7 +24,7 @@ VisualizationPanel.jButton6.text=Hierarchy VisualizationPanel.jButton7.text=Circle VisualizationPanel.jButton8.text=Organic VisualizationPanel.fitGraphButton.text= -VisualizationPanel.statusLabel.text= +VisualizationPanel.statusLabel.text=\ VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. VisualizationPanel.jLabel1.text=Layouts: VisualizationPanel.fitZoomButton.text= @@ -32,8 +32,12 @@ VisualizationPanel.zoomActualButton.text= VisualizationPanel.zoomInButton.text= VisualizationPanel.zoomOutButton.text= VisualizationPanel.circleLayoutButton.text=Circle -VisualizationPanel.OrganicLayoutButton.text=Organic VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic VisualizationPanel.hierarchyLayoutButton.text=Hierarchy VisualizationPanel.jLabel2.text=Zoom: VisualizationPanel.zoomLabel.text=100% +VisualizationPanel.zoomOutButton.toolTipText=Zoom out +VisualizationPanel.zoomInButton.toolTipText=Zoom in +VisualizationPanel.zoomActualButton.toolTipText=reset zoom +VisualizationPanel.fitZoomButton.toolTipText=fit visualization +VisualizationPanel.organicLayoutButton.text=Organic diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index cf08d3ba4c..fa4b00c17a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -97,7 +97,7 @@
- + @@ -140,7 +140,7 @@
- + @@ -156,7 +156,7 @@ - + @@ -171,7 +171,7 @@ - + @@ -187,7 +187,7 @@ - + @@ -236,17 +236,17 @@ - + - + - + @@ -272,6 +272,9 @@ + + + @@ -288,6 +291,9 @@ + + + @@ -304,6 +310,9 @@ + + + @@ -320,6 +329,9 @@ + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 95fcfc52bc..4ac198f1c4 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -43,6 +43,8 @@ import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.text.DecimalFormat; import java.util.Arrays; @@ -120,20 +122,21 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private CommunicationsManager commsManager; private CommunicationsFilter currentFilter; private final mxRubberband rubberband; - private mxFastOrganicLayout mxFastOrganicLayout; - private mxCircleLayout mxCircleLayout; - private final mxOrganicLayout mxOrganicLayout; - private mxHierarchicalLayout mxHierarchicalLayout; + private final mxFastOrganicLayout fastOrganicLayout; + private final mxCircleLayout circleLayout; + private final mxOrganicLayout organicLayout; + private final mxHierarchicalLayout hierarchicalLayout; + private SwingWorker worker; public VisualizationPanel() { initComponents(); graph = new mxGraphImpl(); - mxFastOrganicLayout = new mxFastOrganicLayoutImpl(graph); - mxCircleLayout = new mxCircleLayoutImpl(graph); - mxOrganicLayout = new mxOrganicLayoutImpl(graph); - mxHierarchicalLayout = new mxHierarchicalLayoutImpl(graph); - + fastOrganicLayout = new mxFastOrganicLayoutImpl(graph); + circleLayout = new mxCircleLayoutImpl(graph); + organicLayout = new mxOrganicLayoutImpl(graph); + hierarchicalLayout = new mxHierarchicalLayoutImpl(graph); + graphComponent = new mxGraphComponent(graph); graphComponent.setAutoExtend(true); graphComponent.setAutoScroll(true); @@ -249,7 +252,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyOrganicLayout(10); } @Subscribe @@ -269,7 +271,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyOrganicLayout(10); } @Subscribe @@ -287,18 +288,30 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyOrganicLayout(10); } private void rebuildGraph() throws TskCoreException { if (graph.hasPinnedAccounts()) { - borderLayoutPanel.remove(jPanel1); + borderLayoutPanel.remove(placeHolderPanel); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); - SwingWorker rebuild = graph.rebuild(new ProgressIndicatorImpl(), commsManager, currentFilter); - rebuild.execute(); + if (worker != null) { + worker.cancel(true); + } + worker = graph.rebuild(new ProgressIndicatorImpl(), commsManager, currentFilter); + + worker.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (worker.isDone()) { + applyOrganicLayout(10); + } + } + }); + worker.execute(); + } else { borderLayoutPanel.remove(graphComponent); - borderLayoutPanel.add(jPanel1, BorderLayout.CENTER); + borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); } } @@ -358,13 +371,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider statusLabel = new JLabel(); progresPanel = new JPanel(); progressBar = new JProgressBar(); - jPanel1 = new JPanel(); + placeHolderPanel = new JPanel(); jTextArea1 = new JTextArea(); - jPanel2 = new JPanel(); + toolbar = new JPanel(); jLabel1 = new JLabel(); hierarchyLayoutButton = new JButton(); fastOrganicLayoutButton = new JButton(); - OrganicLayoutButton = new JButton(); + organicLayoutButton = new JButton(); circleLayoutButton = new JButton(); jSeparator1 = new JToolBar.Separator(); zoomOutButton = new JButton(); @@ -416,22 +429,22 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jTextArea1.setRows(5); jTextArea1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jTextArea1.text")); // NOI18N - GroupLayout jPanel1Layout = new GroupLayout(jPanel1); - jPanel1.setLayout(jPanel1Layout); - jPanel1Layout.setHorizontalGroup(jPanel1Layout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, jPanel1Layout.createSequentialGroup() + GroupLayout placeHolderPanelLayout = new GroupLayout(placeHolderPanel); + placeHolderPanel.setLayout(placeHolderPanelLayout); + placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() .addContainerGap(213, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 372, GroupLayout.PREFERRED_SIZE) .addContainerGap(214, Short.MAX_VALUE)) ); - jPanel1Layout.setVerticalGroup(jPanel1Layout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, jPanel1Layout.createSequentialGroup() + placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() .addContainerGap(200, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 43, GroupLayout.PREFERRED_SIZE) .addContainerGap(200, Short.MAX_VALUE)) ); - borderLayoutPanel.add(jPanel1, BorderLayout.CENTER); + borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); jLabel1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jLabel1.text")); // NOI18N @@ -455,13 +468,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } }); - OrganicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.OrganicLayoutButton.text")); // NOI18N - OrganicLayoutButton.setFocusable(false); - OrganicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER); - OrganicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); - OrganicLayoutButton.addActionListener(new ActionListener() { + organicLayoutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.organicLayoutButton.text")); // NOI18N + organicLayoutButton.setFocusable(false); + organicLayoutButton.setHorizontalTextPosition(SwingConstants.CENTER); + organicLayoutButton.setVerticalTextPosition(SwingConstants.BOTTOM); + organicLayoutButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - OrganicLayoutButtonActionPerformed(evt); + organicLayoutButtonActionPerformed(evt); } }); @@ -477,6 +490,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N + zoomOutButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.toolTipText")); // NOI18N zoomOutButton.setFocusable(false); zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER); zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM); @@ -488,6 +502,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N + zoomInButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.toolTipText")); // NOI18N zoomInButton.setFocusable(false); zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER); zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM); @@ -499,6 +514,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomActualButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png"))); // NOI18N zoomActualButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.text")); // NOI18N + zoomActualButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomActualButton.toolTipText")); // NOI18N zoomActualButton.setFocusable(false); zoomActualButton.setHorizontalTextPosition(SwingConstants.CENTER); zoomActualButton.setVerticalTextPosition(SwingConstants.BOTTOM); @@ -510,6 +526,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider fitZoomButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N fitZoomButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.text")); // NOI18N + fitZoomButton.setToolTipText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitZoomButton.toolTipText")); // NOI18N fitZoomButton.setFocusable(false); fitZoomButton.setHorizontalTextPosition(SwingConstants.CENTER); fitZoomButton.setVerticalTextPosition(SwingConstants.BOTTOM); @@ -523,16 +540,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider zoomLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomLabel.text")); // NOI18N - GroupLayout jPanel2Layout = new GroupLayout(jPanel2); - jPanel2.setLayout(jPanel2Layout); - jPanel2Layout.setHorizontalGroup(jPanel2Layout.createParallelGroup(GroupLayout.LEADING) - .add(jPanel2Layout.createSequentialGroup() + GroupLayout toolbarLayout = new GroupLayout(toolbar); + toolbar.setLayout(toolbarLayout); + toolbarLayout.setHorizontalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING) + .add(toolbarLayout.createSequentialGroup() .add(3, 3, 3) .add(jLabel1) .addPreferredGap(LayoutStyle.RELATED) .add(fastOrganicLayoutButton) .addPreferredGap(LayoutStyle.RELATED) - .add(OrganicLayoutButton) + .add(organicLayoutButton) .addPreferredGap(LayoutStyle.RELATED) .add(hierarchyLayoutButton) .addPreferredGap(LayoutStyle.RELATED) @@ -547,20 +564,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider .add(zoomActualButton, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE) .addPreferredGap(LayoutStyle.RELATED) .add(fitZoomButton, GroupLayout.PREFERRED_SIZE, 32, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.RELATED, 110, Short.MAX_VALUE) + .addPreferredGap(LayoutStyle.RELATED, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(jLabel2) .addPreferredGap(LayoutStyle.RELATED) .add(zoomLabel) .add(27, 27, 27)) ); - jPanel2Layout.setVerticalGroup(jPanel2Layout.createParallelGroup(GroupLayout.LEADING) - .add(jPanel2Layout.createSequentialGroup() + toolbarLayout.setVerticalGroup(toolbarLayout.createParallelGroup(GroupLayout.LEADING) + .add(toolbarLayout.createSequentialGroup() .add(3, 3, 3) - .add(jPanel2Layout.createParallelGroup(GroupLayout.CENTER) + .add(toolbarLayout.createParallelGroup(GroupLayout.CENTER) .add(jLabel1, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE) .add(hierarchyLayoutButton) .add(fastOrganicLayoutButton) - .add(OrganicLayoutButton) + .add(organicLayoutButton) .add(circleLayoutButton) .add(jSeparator1, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(zoomOutButton) @@ -572,7 +589,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider .add(3, 3, 3)) ); - borderLayoutPanel.add(jPanel2, BorderLayout.PAGE_START); + borderLayoutPanel.add(toolbar, BorderLayout.PAGE_START); splitPane.setLeftComponent(borderLayoutPanel); @@ -596,30 +613,29 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_zoomOutButtonActionPerformed private void circleLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_circleLayoutButtonActionPerformed - morph(mxCircleLayout); + morph(circleLayout); }//GEN-LAST:event_circleLayoutButtonActionPerformed - private void OrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_OrganicLayoutButtonActionPerformed + private void organicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_organicLayoutButtonActionPerformed applyOrganicLayout(10); - }//GEN-LAST:event_OrganicLayoutButtonActionPerformed + }//GEN-LAST:event_organicLayoutButtonActionPerformed private void fastOrganicLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fastOrganicLayoutButtonActionPerformed - - morph(mxFastOrganicLayout); + morph(fastOrganicLayout); }//GEN-LAST:event_fastOrganicLayoutButtonActionPerformed private void hierarchyLayoutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_hierarchyLayoutButtonActionPerformed - - morph(mxHierarchicalLayout); + morph(hierarchicalLayout); }//GEN-LAST:event_hierarchyLayoutButtonActionPerformed private void applyOrganicLayout(int iterations) { - mxOrganicLayout.setMaxIterations(iterations); - morph(mxOrganicLayout); + organicLayout.setMaxIterations(iterations); + morph(organicLayout); } private void fitGraph() { + graphComponent.zoomTo(1, true); mxPoint translate = graph.getView().getTranslate(); if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) { translate = new mxPoint(); @@ -629,7 +645,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) { boundsForCells = new mxRectangle(0, 0, 1, 1); } - graph.getView().setTranslate(new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY())); + final mxPoint mxPoint = new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY()); + +// graph.getView().setTranslate(mxPoint); + graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(), false, false); boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true); if (boundsForCells == null || Double.isNaN(boundsForCells.getWidth()) || Double.isNaN(boundsForCells.getHeight())) { @@ -637,13 +656,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } Dimension size = graphComponent.getSize(); - double widthFactor = size.getWidth() / boundsForCells.getWidth(); -// widthFactor = boundsForCells.getWidth() / size.getWidth(); - graphComponent.zoom(widthFactor); + graphComponent.zoom(widthFactor);//, true); -// bounds = graph.getGraphBounds(); } private void morph(mxIGraphLayout layout) { @@ -662,16 +678,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progressBar.setVisible(false); progressBar.setValue(0); }); - morph.addListener(mxEvent.EXECUTE, (Object sender, mxEventObject event) -> { -// fitGraph(); - }); morph.startAnimation(); } } // Variables declaration - do not modify//GEN-BEGIN:variables - private JButton OrganicLayoutButton; private JPanel borderLayoutPanel; private JButton circleLayoutButton; private JButton fastOrganicLayoutButton; @@ -679,22 +691,23 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JButton hierarchyLayoutButton; private JLabel jLabel1; private JLabel jLabel2; - private JPanel jPanel1; - private JPanel jPanel2; private JToolBar.Separator jSeparator1; private JTextArea jTextArea1; private JToolBar jToolBar2; + private JButton organicLayoutButton; + private JPanel placeHolderPanel; private JPanel progresPanel; private JProgressBar progressBar; private JSplitPane splitPane; private JLabel statusLabel; + private JPanel toolbar; private JButton zoomActualButton; private JButton zoomInButton; private JLabel zoomLabel; private JButton zoomOutButton; // End of variables declaration//GEN-END:variables - private class SelectionListener implements mxEventSource.mxIEventListener { + final private class SelectionListener implements mxEventSource.mxIEventListener { @Override @@ -726,7 +739,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } - private class ProgressIndicatorImpl implements ProgressIndicator { + final private class ProgressIndicatorImpl implements ProgressIndicator { @Override public void start(String message, int totalWorkUnits) { @@ -799,9 +812,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } - private class mxFastOrganicLayoutImpl extends mxFastOrganicLayout { + final private class mxFastOrganicLayoutImpl extends mxFastOrganicLayout { - public mxFastOrganicLayoutImpl(mxGraph graph) { + private mxFastOrganicLayoutImpl(mxGraph graph) { super(graph); } @@ -821,12 +834,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } - private class mxCircleLayoutImpl extends mxCircleLayout { + final private class mxCircleLayoutImpl extends mxCircleLayout { - public mxCircleLayoutImpl(mxGraph graph) { + private mxCircleLayoutImpl(mxGraph graph) { super(graph); - } - { setResetEdges(true); } @@ -846,12 +857,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } - private class mxOrganicLayoutImpl extends mxOrganicLayout { + final private class mxOrganicLayoutImpl extends mxOrganicLayout { - public mxOrganicLayoutImpl(mxGraph graph) { + private mxOrganicLayoutImpl(mxGraph graph) { super(graph); - } - { setResetEdges(true); } @@ -871,9 +880,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } - private class mxHierarchicalLayoutImpl extends mxHierarchicalLayout { + final private class mxHierarchicalLayoutImpl extends mxHierarchicalLayout { - public mxHierarchicalLayoutImpl(mxGraph graph) { + private mxHierarchicalLayoutImpl(mxGraph graph) { super(graph); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index b61cec8284..9a890ec555 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -38,7 +38,6 @@ import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; -import org.openide.util.Exceptions; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -61,21 +60,19 @@ final class mxGraphImpl extends mxGraph { private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); static { - //initialize defaul cell (Vertex and/or Edge) properties + //initialize defaul vertex properties mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE); mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "000000"); -// mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_WHITE_SPACE, "wrap"); + //initialize defaul edge properties mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); -// mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_OPACITY, 50 ); -// mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ROUNDED, true); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_PERIMETER_SPACING, 0); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ENDARROW, mxConstants.NONE); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE); } - public mxGraphImpl() { + mxGraphImpl() { super(mxStylesheet); setAutoSizeCells(true); setCellsCloneable(false); @@ -139,17 +136,20 @@ final class mxGraphImpl extends mxGraph { if (value instanceof AccountDeviceInstanceKey) { final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value; final String accountName = adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID(); + + final double size = Math.round(Math.log(adiKey.getMessageCount()) + 5); + String iconFileName = Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()); - String label = "" + accountName; + + ">" + accountName; if (pinnedAccountDevices.contains(adiKey)) { - label = ""+label; + label = "" + label; } if (lockedVertices.contains((mxCell) cell)) { - label += ""; + label += ""; } - return "
" + label + "
"; + return "
" + label + "
"; } else { return ""; } @@ -184,14 +184,15 @@ final class mxGraphImpl extends mxGraph { getView().updateLabelBounds(state); getView().updateBoundingBox(state); } - + SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { - + return new SwingWorkerImpl(progress, commsManager, currentFilter); } - + void resetGraph() { clear(); + getView().setScale(1); pinnedAccountDevices.clear(); lockedVertices.clear(); } @@ -207,8 +208,8 @@ final class mxGraphImpl extends mxGraph { mxCell newVertex = (mxCell) insertVertex( getDefaultParent(), vertexName, accountDeviceInstanceKey, - Math.random() * getView().getGraphBounds().getWidth(), - Math.random() * getView().getGraphBounds().getHeight(), + Math.random() * 400, + Math.random() * 400, size, size); return newVertex; @@ -249,23 +250,23 @@ final class mxGraphImpl extends mxGraph { double getScale() { return getView().getScale(); } - + boolean isVertexLocked(mxCell vertex) { return lockedVertices.contains(vertex); } - + private class SwingWorkerImpl extends SwingWorker { - + private final ProgressIndicator progress; private final CommunicationsManager commsManager; private final CommunicationsFilter currentFilter; - + SwingWorkerImpl(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { this.progress = progress; this.currentFilter = currentFilter; this.commsManager = commsManager; } - + @Override protected Void doInBackground() throws Exception { progress.start("Loading accounts", pinnedAccountDevices.size()); @@ -296,7 +297,7 @@ final class mxGraphImpl extends mxGraph { for (i = 0; i < relatedAccountsList.size(); i++) { AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); for (int j = i; j < relatedAccountsList.size(); j++) { - + AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); List relationships = commsManager.getRelationshipSources( adiKey1.getAccountDeviceInstance(), @@ -315,14 +316,14 @@ final class mxGraphImpl extends mxGraph { } return null; } - + @Override protected void done() { super.done(); try { get(); } catch (InterruptedException | ExecutionException ex) { - Exceptions.printStackTrace(ex); + logger.log(Level.SEVERE, "Error building graph visualization. ", ex); } finally { progress.finish(); } From eddab91e433fccbe26682fc4d11b825d56dcee8a Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 12 Feb 2018 13:34:57 +0100 Subject: [PATCH 045/128] use mustache.java to make the node label generation more readable. --- .../communications/Vertex_Label_template.html | 10 +++ .../autopsy/communications/mxGraphImpl.java | 65 ++++++++++++------- 2 files changed, 52 insertions(+), 23 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html diff --git a/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html b/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html new file mode 100644 index 0000000000..dbc3f85a2c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html @@ -0,0 +1,10 @@ +
+ {{#pinned}} + + {{/pinned}} + {{#locked}} + + {{/locked}} + + {{accountName}} +
diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 9a890ec555..6b9a2b9391 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.communications; +import com.github.mustachejava.DefaultMustacheFactory; +import com.github.mustachejava.Mustache; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; @@ -27,6 +29,9 @@ import com.mxgraph.util.mxConstants; import com.mxgraph.view.mxCellState; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxStylesheet; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; import java.net.URL; import java.util.ArrayList; import java.util.Collection; @@ -51,6 +56,16 @@ final class mxGraphImpl extends mxGraph { private static final Logger logger = Logger.getLogger(mxGraphImpl.class.getName()); private static final URL MARKER_PIN_URL = mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png"); private static final URL LOCK_URL = mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/lock_large_locked.png"); + /** + * mustache.java template + */ + private final static Mustache labelMustache; + + static { +// String labelTemplatePath = "/org/sleuthkit/autopsy/communications/Vertex_Label_template.html"; + InputStream templateStream = mxGraphImpl.class.getResourceAsStream("/org/sleuthkit/autopsy/communications/Vertex_Label_template.html"); + labelMustache = new DefaultMustacheFactory().compile(new InputStreamReader(templateStream), "Vertex_Label"); + } static final private mxStylesheet mxStylesheet = new mxStylesheet(); private final HashSet pinnedAccountDevices = new HashSet<>(); @@ -132,24 +147,27 @@ final class mxGraphImpl extends mxGraph { @Override public String convertValueToString(Object cell) { + final StringWriter stringWriter = new StringWriter(); + HashMap scopes = new HashMap<>(); + Object value = getModel().getValue(cell); if (value instanceof AccountDeviceInstanceKey) { final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value; - final String accountName = adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID(); - final double size = Math.round(Math.log(adiKey.getMessageCount()) + 5); + scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); + scopes.put("size", Math.round(Math.log(adiKey.getMessageCount()) + 5)); - String iconFileName = Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()); - String label = "" + accountName; - if (pinnedAccountDevices.contains(adiKey)) { - label = "" + label; - } - if (lockedVertices.contains((mxCell) cell)) { - label += ""; - } - return "
" + label + "
"; + scopes.put("iconFileName", mxGraphImpl.class + .getResource("/org/sleuthkit/autopsy/communications/images/" + + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); + scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); + scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); + scopes.put("locked", lockedVertices.contains((mxCell) cell)); + scopes.put("LOCK_URL", LOCK_URL); + + labelMustache.execute(stringWriter, scopes); + + return stringWriter.toString(); } else { return ""; } @@ -184,12 +202,12 @@ final class mxGraphImpl extends mxGraph { getView().updateLabelBounds(state); getView().updateBoundingBox(state); } - + SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { - + return new SwingWorkerImpl(progress, commsManager, currentFilter); } - + void resetGraph() { clear(); getView().setScale(1); @@ -250,23 +268,24 @@ final class mxGraphImpl extends mxGraph { double getScale() { return getView().getScale(); } - + boolean isVertexLocked(mxCell vertex) { return lockedVertices.contains(vertex); + } - + private class SwingWorkerImpl extends SwingWorker { - + private final ProgressIndicator progress; private final CommunicationsManager commsManager; private final CommunicationsFilter currentFilter; - + SwingWorkerImpl(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { this.progress = progress; this.currentFilter = currentFilter; this.commsManager = commsManager; } - + @Override protected Void doInBackground() throws Exception { progress.start("Loading accounts", pinnedAccountDevices.size()); @@ -297,7 +316,7 @@ final class mxGraphImpl extends mxGraph { for (i = 0; i < relatedAccountsList.size(); i++) { AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); for (int j = i; j < relatedAccountsList.size(); j++) { - + AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); List relationships = commsManager.getRelationshipSources( adiKey1.getAccountDeviceInstance(), @@ -316,7 +335,7 @@ final class mxGraphImpl extends mxGraph { } return null; } - + @Override protected void done() { super.done(); From b7a91a7a8c04a174fb6aeca3a900642233cf2fa4 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 12 Feb 2018 15:00:40 +0100 Subject: [PATCH 046/128] cleanup and fix codacy issues --- .../communications/Vertex_Label_template.html | 13 +-- .../communications/VisualizationPanel.java | 67 ++++++----- .../autopsy/communications/mxGraphImpl.java | 108 +++++++++++------- 3 files changed, 105 insertions(+), 83 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html b/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html index dbc3f85a2c..4e489c992a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html +++ b/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html @@ -1,10 +1,3 @@ -
- {{#pinned}} - - {{/pinned}} - {{#locked}} - - {{/locked}} - - {{accountName}} -
+ +
{{#pinned}}{{/pinned}}{{#locked}}{{/locked}}{{accountName}}
+ \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 4ac198f1c4..217bbb168b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -154,7 +154,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider rubberband = new mxRubberband(graphComponent); final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt) -> - zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getScale())); + zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale())); graph.getView().addListener(mxEvent.SCALE, scaleListener); graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener); @@ -164,9 +164,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public void mouseWheelMoved(final MouseWheelEvent e) { super.mouseWheelMoved(e); if (e.getPreciseWheelRotation() > 0) { - graphComponent.zoomTo(graph.getScale() / graphComponent.getZoomFactor(), true); + graphComponent.zoomIn(); } else if (e.getPreciseWheelRotation() < 0) { - graphComponent.zoomTo(graph.getScale() * graphComponent.getZoomFactor(), true); + graphComponent.zoomOut(); } } }); @@ -291,7 +291,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } private void rebuildGraph() throws TskCoreException { - if (graph.hasPinnedAccounts()) { + if (graph.isEmpty()) { + borderLayoutPanel.remove(graphComponent); + borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); + } else { borderLayoutPanel.remove(placeHolderPanel); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); if (worker != null) { @@ -303,15 +306,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void propertyChange(PropertyChangeEvent evt) { if (worker.isDone()) { - applyOrganicLayout(10); + if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { + applyOrganicLayout(10); + } else { + statusLabel.setText("Too many cells, layout aborted."); + } } } }); worker.execute(); - } else { - borderLayoutPanel.remove(graphComponent); - borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); } } @@ -665,22 +669,28 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void morph(mxIGraphLayout layout) { // layout using morphing graph.getModel().beginUpdate(); - try { - progressBar.setVisible(true); - progressBar.setIndeterminate(true); - progressBar.setString("applying layout"); - layout.execute(graph.getDefaultParent()); - } finally { - mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); - morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { - graph.getModel().endUpdate(); - fitGraph(); - progressBar.setVisible(false); - progressBar.setValue(0); - }); - morph.startAnimation(); - } + progressBar.setVisible(true); + progressBar.setIndeterminate(true); + progressBar.setString("applying layout"); + + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + layout.execute(graph.getDefaultParent()); + mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); + morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { + graph.getModel().endUpdate(); + fitGraph(); + progressBar.setVisible(false); + progressBar.setValue(0); + }); + + morph.startAnimation(); + return null; + } + }.execute(); + } // Variables declaration - do not modify//GEN-BEGIN:variables @@ -709,9 +719,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class SelectionListener implements mxEventSource.mxIEventListener { - @Override - @SuppressWarnings("unchecked") + @Override public void invoke(Object sender, mxEventObject evt) { Object[] selectionCells = graph.getSelectionCells(); Node rootNode = Node.EMPTY; @@ -814,7 +823,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxFastOrganicLayoutImpl extends mxFastOrganicLayout { - private mxFastOrganicLayoutImpl(mxGraph graph) { + mxFastOrganicLayoutImpl(mxGraph graph) { super(graph); } @@ -836,7 +845,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxCircleLayoutImpl extends mxCircleLayout { - private mxCircleLayoutImpl(mxGraph graph) { + mxCircleLayoutImpl(mxGraph graph) { super(graph); setResetEdges(true); } @@ -859,7 +868,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxOrganicLayoutImpl extends mxOrganicLayout { - private mxOrganicLayoutImpl(mxGraph graph) { + mxOrganicLayoutImpl(mxGraph graph) { super(graph); setResetEdges(true); } @@ -882,7 +891,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxHierarchicalLayoutImpl extends mxHierarchicalLayout { - private mxHierarchicalLayoutImpl(mxGraph graph) { + mxHierarchicalLayoutImpl(mxGraph graph) { super(graph); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 6b9a2b9391..72210e37ff 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -106,33 +106,6 @@ final class mxGraphImpl extends mxGraph { setKeepEdgesInBackground(true); setResetEdgesOnMove(true); setHtmlLabels(true); - -// new mxLayoutManager(graph) { -// final private mxOrganicLayout layout; -// private int counter; -// { -// this.layout = new mxOrganicLayout(graph); -// this.layout.setMaxIterations(1); -// } -// -// @Override -// protected void executeLayout(mxIGraphLayout layout, Object parent) { -// if (counter % 10 == 0) -// { -// super.executeLayout(layout, parent); -//// fitGraph(); -// } -// counter++; -// } -// -// @Override -// public mxIGraphLayout getLayout(Object parent) { -// if (graph.getModel().getChildCount(parent) > 0) { -// return layout; -// } -// return null; -// } -// }; } void clear() { @@ -156,10 +129,8 @@ final class mxGraphImpl extends mxGraph { scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); scopes.put("size", Math.round(Math.log(adiKey.getMessageCount()) + 5)); - - scopes.put("iconFileName", mxGraphImpl.class - .getResource("/org/sleuthkit/autopsy/communications/images/" - + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); + scopes.put("iconFileName", mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/" + + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); scopes.put("locked", lockedVertices.contains((mxCell) cell)); @@ -175,25 +146,69 @@ final class mxGraphImpl extends mxGraph { @Override public String getToolTipForCell(Object cell) { - return ((mxICell) cell).getId(); + final StringWriter stringWriter = new StringWriter(); + HashMap scopes = new HashMap<>(); + + Object value = getModel().getValue(cell); + if (value instanceof AccountDeviceInstanceKey) { + final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value; + + scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); + scopes.put("size", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5)); + scopes.put("iconFileName", mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/" + + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); + scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); + scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); + scopes.put("locked", lockedVertices.contains((mxCell) cell)); + scopes.put("LOCK_URL", LOCK_URL); + + labelMustache.execute(stringWriter, scopes); + + return stringWriter.toString(); + } else { + return ((mxICell) cell).getId(); + } } + /** + * Unpin the given accounts from the graph. Pinned accounts will always be + * shown regardless of the filter state. Furthermore, accounts with + * relationships that pass the filters will also be shown. + * + * @param accountDeviceInstances The accounts to unpin. + */ void unpinAccount(ImmutableSet accountDeviceInstances) { pinnedAccountDevices.removeAll(accountDeviceInstances); } + /** + * Pin the given accounts to the graph. Pinned accounts will always be shown + * regardless of the filter state. Furthermore, accounts with relationships + * that pass the filters will also be shown. + * + * @param accountDeviceInstances The accounts to pin. + */ void pinAccount(ImmutableSet accountDeviceInstances) { pinnedAccountDevices.addAll(accountDeviceInstances); } + /** + * Lock the given vertex so that applying a layout algorithm doesn't move + * it. The user can still manually position the vertex. + * + * @param vertex The vertex to lock. + */ void lockVertex(mxCell vertex) { lockedVertices.add(vertex); - - final mxCellState state = getView().getState(vertex, true); getView().clear(vertex, true, true); getView().validate(); } + /** + * Lock the given vertex so that applying a layout algorithm can move it. + * + * @param vertex The vertex to unlock. + */ void unlockVertex(mxCell vertex) { lockedVertices.remove(vertex); @@ -204,8 +219,7 @@ final class mxGraphImpl extends mxGraph { } SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { - - return new SwingWorkerImpl(progress, commsManager, currentFilter); + return new RebuildWorker(progress, commsManager, currentFilter); } void resetGraph() { @@ -261,12 +275,14 @@ final class mxGraphImpl extends mxGraph { return edge; } - boolean hasPinnedAccounts() { - return pinnedAccountDevices.isEmpty() == false; - } - - double getScale() { - return getView().getScale(); + /** + * Are there any accounts in this graph? If there are no pinned accounts the + * graph will be empty. + * + * @return True if this graph is empty. + */ + boolean isEmpty() { + return pinnedAccountDevices.isEmpty(); } boolean isVertexLocked(mxCell vertex) { @@ -274,13 +290,17 @@ final class mxGraphImpl extends mxGraph { } - private class SwingWorkerImpl extends SwingWorker { + /** + * SwingWorker that loads the accounts and edges for this graph according to + * the pinned accounts and the current filters. + */ + private class RebuildWorker extends SwingWorker { private final ProgressIndicator progress; private final CommunicationsManager commsManager; private final CommunicationsFilter currentFilter; - SwingWorkerImpl(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + RebuildWorker(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { this.progress = progress; this.currentFilter = currentFilter; this.commsManager = commsManager; From a77ced5a4161d1a3440abb63cb66ad117354cc6f Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 12 Feb 2018 15:27:11 +0100 Subject: [PATCH 047/128] begin work on cancellable graph loading --- .../autopsy/communications/Bundle.properties | 1 + .../communications/VisualizationPanel.form | 34 +++++-- .../communications/VisualizationPanel.java | 91 +++++++++++-------- .../autopsy/communications/mxGraphImpl.java | 11 ++- 4 files changed, 87 insertions(+), 50 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 271d5b3b5a..0f9e5ce048 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -41,3 +41,4 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in VisualizationPanel.zoomActualButton.toolTipText=reset zoom VisualizationPanel.fitZoomButton.toolTipText=fit visualization VisualizationPanel.organicLayoutButton.text=Organic +VisualizationPanel.cancelButton.text= diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index fa4b00c17a..a4372087d1 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -11,7 +11,7 @@ - + @@ -58,7 +58,7 @@
- + @@ -70,16 +70,22 @@ - + + + + - - + + + + + @@ -93,6 +99,16 @@
+ + + + + + + + + +
@@ -108,18 +124,18 @@ - + - + - + - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 217bbb168b..d7148fde20 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -148,7 +148,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.setToolTips(true); graphComponent.setBackground(Color.WHITE); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); - progressBar.setVisible(false); + progressPanel.setVisible(false); //install rubber band selection handler rubberband = new mxRubberband(graphComponent); @@ -176,10 +176,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public void mouseClicked(MouseEvent e) { super.mouseClicked(e); if (SwingUtilities.isRightMouseButton(e)) { - mxCell cellAt = (mxCell) graphComponent.getCellAt(e.getX(), e.getY()); + final mxCell cellAt = (mxCell) graphComponent.getCellAt(e.getX(), e.getY()); if (cellAt != null && cellAt.isVertex()) { - JPopupMenu jPopupMenu = new JPopupMenu(); - AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue(); + final JPopupMenu jPopupMenu = new JPopupMenu(); + final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue(); if (graph.isVertexLocked(cellAt)) { jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock " + cellAt.getId(), unlockIcon) { @@ -304,7 +304,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider worker.addPropertyChangeListener(new PropertyChangeListener() { @Override - public void propertyChange(PropertyChangeEvent evt) { + public void propertyChange(final PropertyChangeEvent evt) { if (worker.isDone()) { if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { applyOrganicLayout(10); @@ -314,6 +314,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } }); + + cancelButton.setAction(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + worker.cancel(true); + } + }); worker.execute(); } @@ -322,7 +329,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void addNotify() { super.addNotify(); -// IngestManager.getInstance().addIngestModuleEventListener(ingestListener); try { commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); @@ -357,7 +363,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void removeNotify() { super.removeNotify(); -// IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); } /** @@ -373,8 +378,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider borderLayoutPanel = new JPanel(); jToolBar2 = new JToolBar(); statusLabel = new JLabel(); - progresPanel = new JPanel(); + progressPanel = new JPanel(); progressBar = new JProgressBar(); + cancelButton = new JButton(); placeHolderPanel = new JPanel(); jTextArea1 = new JTextArea(); toolbar = new JPanel(); @@ -404,26 +410,34 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider statusLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.statusLabel.text")); // NOI18N jToolBar2.add(statusLabel); - progresPanel.setMinimumSize(new Dimension(0, 20)); - progresPanel.setName(""); // NOI18N + progressPanel.setMinimumSize(new Dimension(0, 20)); + progressPanel.setName(""); // NOI18N progressBar.setMaximumSize(new Dimension(200, 14)); progressBar.setStringPainted(true); - GroupLayout progresPanelLayout = new GroupLayout(progresPanel); - progresPanel.setLayout(progresPanelLayout); - progresPanelLayout.setHorizontalGroup(progresPanelLayout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, progresPanelLayout.createSequentialGroup() - .add(0, 447, Short.MAX_VALUE) - .add(progressBar, GroupLayout.PREFERRED_SIZE, 350, GroupLayout.PREFERRED_SIZE)) + cancelButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/cross-script.png"))); // NOI18N + cancelButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.cancelButton.text")); // NOI18N + + GroupLayout progressPanelLayout = new GroupLayout(progressPanel); + progressPanel.setLayout(progressPanelLayout); + progressPanelLayout.setHorizontalGroup(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, progressPanelLayout.createSequentialGroup() + .addContainerGap(415, Short.MAX_VALUE) + .add(progressBar, GroupLayout.PREFERRED_SIZE, 350, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.RELATED) + .add(cancelButton, GroupLayout.PREFERRED_SIZE, 24, GroupLayout.PREFERRED_SIZE) + .add(5, 5, 5)) ); - progresPanelLayout.setVerticalGroup(progresPanelLayout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, progresPanelLayout.createSequentialGroup() + progressPanelLayout.setVerticalGroup(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(GroupLayout.TRAILING, progressPanelLayout.createSequentialGroup() .add(0, 0, 0) - .add(progressBar, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)) + .add(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) + .add(cancelButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .add(progressBar, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) ); - jToolBar2.add(progresPanel); + jToolBar2.add(progressPanel); borderLayoutPanel.add(jToolBar2, BorderLayout.PAGE_END); @@ -437,15 +451,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider placeHolderPanel.setLayout(placeHolderPanelLayout); placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(213, Short.MAX_VALUE) + .addContainerGap(217, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 372, GroupLayout.PREFERRED_SIZE) - .addContainerGap(214, Short.MAX_VALUE)) + .addContainerGap(218, Short.MAX_VALUE)) ); placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(200, Short.MAX_VALUE) + .addContainerGap(387, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 43, GroupLayout.PREFERRED_SIZE) - .addContainerGap(200, Short.MAX_VALUE)) + .addContainerGap()) ); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); @@ -638,7 +652,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } private void fitGraph() { - graphComponent.zoomTo(1, true); mxPoint translate = graph.getView().getTranslate(); if (translate == null || Double.isNaN(translate.getX()) || Double.isNaN(translate.getY())) { @@ -651,7 +664,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } final mxPoint mxPoint = new mxPoint(translate.getX() - boundsForCells.getX(), translate.getY() - boundsForCells.getY()); -// graph.getView().setTranslate(mxPoint); graph.cellsMoved(graph.getChildCells(graph.getDefaultParent()), mxPoint.getX(), mxPoint.getY(), false, false); boundsForCells = graph.getCellBounds(graph.getDefaultParent(), true, true, true); @@ -662,7 +674,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider Dimension size = graphComponent.getSize(); double widthFactor = size.getWidth() / boundsForCells.getWidth(); - graphComponent.zoom(widthFactor);//, true); + graphComponent.zoom(widthFactor); } @@ -670,9 +682,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // layout using morphing graph.getModel().beginUpdate(); - progressBar.setVisible(true); + progressPanel.setVisible(true); progressBar.setIndeterminate(true); - progressBar.setString("applying layout"); + progressBar.setString("Computing layout"); new SwingWorker() { @Override @@ -682,19 +694,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { graph.getModel().endUpdate(); fitGraph(); - progressBar.setVisible(false); + progressPanel.setVisible(false); progressBar.setValue(0); }); - + + SwingUtilities.invokeLater(() -> progressBar.setString("Applying layout")); morph.startAnimation(); return null; } }.execute(); - } // Variables declaration - do not modify//GEN-BEGIN:variables private JPanel borderLayoutPanel; + private JButton cancelButton; private JButton circleLayoutButton; private JButton fastOrganicLayoutButton; private JButton fitZoomButton; @@ -706,8 +719,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JToolBar jToolBar2; private JButton organicLayoutButton; private JPanel placeHolderPanel; - private JPanel progresPanel; private JProgressBar progressBar; + private JPanel progressPanel; private JSplitPane splitPane; private JLabel statusLabel; private JPanel toolbar; @@ -753,7 +766,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void start(String message, int totalWorkUnits) { SwingUtilities.invokeLater(() -> { - progressBar.setVisible(true); + progressPanel.setVisible(true); progressBar.setIndeterminate(false); progressBar.setString(message); progressBar.setMaximum(totalWorkUnits); @@ -764,7 +777,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void start(String message) { SwingUtilities.invokeLater(() -> { - progressBar.setVisible(true); + progressPanel.setVisible(true); progressBar.setString(message); progressBar.setIndeterminate(true); }); @@ -773,7 +786,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void switchToIndeterminate(String message) { SwingUtilities.invokeLater(() -> { - progressBar.setVisible(true); + progressPanel.setVisible(true); progressBar.setIndeterminate(true); progressBar.setString(message); }); @@ -782,7 +795,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void switchToDeterminate(String message, int workUnitsCompleted, int totalWorkUnits) { SwingUtilities.invokeLater(() -> { - progressBar.setVisible(true); + progressPanel.setVisible(true); progressBar.setIndeterminate(false); progressBar.setString(message); progressBar.setMaximum(totalWorkUnits); @@ -815,8 +828,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void finish() { SwingUtilities.invokeLater(() -> { - progressBar.setValue(progressBar.getValue()); - progressBar.setVisible(false); + progressBar.setValue(progressBar.getMaximum()); + progressPanel.setVisible(false); }); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 72210e37ff..46bec948c9 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -40,6 +40,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; @@ -264,7 +265,6 @@ final class mxGraphImpl extends mxGraph { if (edgesBetween.length == 0) { final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); final HashSet hashSet = new HashSet<>(relSources); - // edgeMap.put(relSource, edge); edge = (mxCell) insertEdge(getDefaultParent(), edgeName, hashSet, vertex1, vertex2, "strokeWidth=" + (Math.log(hashSet.size()) + 1)); } else { @@ -316,6 +316,9 @@ final class mxGraphImpl extends mxGraph { */ Set relatedAccounts = new HashSet<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { + if (isCancelled()) { + break; + } List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); relatedAccounts.add(adiKey); @@ -336,7 +339,9 @@ final class mxGraphImpl extends mxGraph { for (i = 0; i < relatedAccountsList.size(); i++) { AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); for (int j = i; j < relatedAccountsList.size(); j++) { - + if (isCancelled()) { + break; + } AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); List relationships = commsManager.getRelationshipSources( adiKey1.getAccountDeviceInstance(), @@ -363,6 +368,8 @@ final class mxGraphImpl extends mxGraph { get(); } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, "Error building graph visualization. ", ex); + } catch (CancellationException ex) { + logger.log(Level.INFO, "Graph visualization cancelled"); } finally { progress.finish(); } From efdd5a403d3ce42948446e7b11065a02ad6e6c33 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 12 Feb 2018 15:00:40 +0100 Subject: [PATCH 048/128] cleanup and fix codacy issues --- .../autopsy/communications/FiltersPanel.java | 3 +- .../communications/Vertex_Label_template.html | 13 +-- .../communications/VisualizationPanel.java | 67 ++++++----- .../autopsy/communications/mxGraphImpl.java | 108 +++++++++++------- 4 files changed, 106 insertions(+), 85 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 248412b07a..36f9ac4410 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -197,8 +197,6 @@ final public class FiltersPanel extends JPanel { Account.Type.PREDEFINED_ACCOUNT_TYPES.forEach(type -> { if (type.equals(Account.Type.CREDIT_CARD)) { //don't show a check box for credit cards -// } else if (type.equals(Account.Type.DEVICE)) { -// //don't show a check box fro device } else { accountTypeMap.computeIfAbsent(type, t -> { final JCheckBox jCheckBox = new JCheckBox( @@ -211,6 +209,7 @@ final public class FiltersPanel extends JPanel { jCheckBox.addItemListener(validationListener); accountTypePane.add(jCheckBox); if (t.equals(Account.Type.DEVICE)) { + //Deveice type filter is enabled based on whether we are in table or graph view. jCheckBox.setEnabled(deviceAccountTypeEnabled); } return jCheckBox; diff --git a/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html b/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html index dbc3f85a2c..4e489c992a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html +++ b/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html @@ -1,10 +1,3 @@ -
- {{#pinned}} - - {{/pinned}} - {{#locked}} - - {{/locked}} - - {{accountName}} -
+ +
{{#pinned}}{{/pinned}}{{#locked}}{{/locked}}{{accountName}}
+ \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 4ac198f1c4..217bbb168b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -154,7 +154,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider rubberband = new mxRubberband(graphComponent); final mxEventSource.mxIEventListener scaleListener = (Object sender, mxEventObject evt) -> - zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getScale())); + zoomLabel.setText(DecimalFormat.getPercentInstance().format(graph.getView().getScale())); graph.getView().addListener(mxEvent.SCALE, scaleListener); graph.getView().addListener(mxEvent.SCALE_AND_TRANSLATE, scaleListener); @@ -164,9 +164,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public void mouseWheelMoved(final MouseWheelEvent e) { super.mouseWheelMoved(e); if (e.getPreciseWheelRotation() > 0) { - graphComponent.zoomTo(graph.getScale() / graphComponent.getZoomFactor(), true); + graphComponent.zoomIn(); } else if (e.getPreciseWheelRotation() < 0) { - graphComponent.zoomTo(graph.getScale() * graphComponent.getZoomFactor(), true); + graphComponent.zoomOut(); } } }); @@ -291,7 +291,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } private void rebuildGraph() throws TskCoreException { - if (graph.hasPinnedAccounts()) { + if (graph.isEmpty()) { + borderLayoutPanel.remove(graphComponent); + borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); + } else { borderLayoutPanel.remove(placeHolderPanel); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); if (worker != null) { @@ -303,15 +306,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void propertyChange(PropertyChangeEvent evt) { if (worker.isDone()) { - applyOrganicLayout(10); + if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { + applyOrganicLayout(10); + } else { + statusLabel.setText("Too many cells, layout aborted."); + } } } }); worker.execute(); - } else { - borderLayoutPanel.remove(graphComponent); - borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); } } @@ -665,22 +669,28 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void morph(mxIGraphLayout layout) { // layout using morphing graph.getModel().beginUpdate(); - try { - progressBar.setVisible(true); - progressBar.setIndeterminate(true); - progressBar.setString("applying layout"); - layout.execute(graph.getDefaultParent()); - } finally { - mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); - morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { - graph.getModel().endUpdate(); - fitGraph(); - progressBar.setVisible(false); - progressBar.setValue(0); - }); - morph.startAnimation(); - } + progressBar.setVisible(true); + progressBar.setIndeterminate(true); + progressBar.setString("applying layout"); + + new SwingWorker() { + @Override + protected Void doInBackground() throws Exception { + layout.execute(graph.getDefaultParent()); + mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); + morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { + graph.getModel().endUpdate(); + fitGraph(); + progressBar.setVisible(false); + progressBar.setValue(0); + }); + + morph.startAnimation(); + return null; + } + }.execute(); + } // Variables declaration - do not modify//GEN-BEGIN:variables @@ -709,9 +719,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class SelectionListener implements mxEventSource.mxIEventListener { - @Override - @SuppressWarnings("unchecked") + @Override public void invoke(Object sender, mxEventObject evt) { Object[] selectionCells = graph.getSelectionCells(); Node rootNode = Node.EMPTY; @@ -814,7 +823,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxFastOrganicLayoutImpl extends mxFastOrganicLayout { - private mxFastOrganicLayoutImpl(mxGraph graph) { + mxFastOrganicLayoutImpl(mxGraph graph) { super(graph); } @@ -836,7 +845,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxCircleLayoutImpl extends mxCircleLayout { - private mxCircleLayoutImpl(mxGraph graph) { + mxCircleLayoutImpl(mxGraph graph) { super(graph); setResetEdges(true); } @@ -859,7 +868,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxOrganicLayoutImpl extends mxOrganicLayout { - private mxOrganicLayoutImpl(mxGraph graph) { + mxOrganicLayoutImpl(mxGraph graph) { super(graph); setResetEdges(true); } @@ -882,7 +891,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class mxHierarchicalLayoutImpl extends mxHierarchicalLayout { - private mxHierarchicalLayoutImpl(mxGraph graph) { + mxHierarchicalLayoutImpl(mxGraph graph) { super(graph); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 6b9a2b9391..72210e37ff 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -106,33 +106,6 @@ final class mxGraphImpl extends mxGraph { setKeepEdgesInBackground(true); setResetEdgesOnMove(true); setHtmlLabels(true); - -// new mxLayoutManager(graph) { -// final private mxOrganicLayout layout; -// private int counter; -// { -// this.layout = new mxOrganicLayout(graph); -// this.layout.setMaxIterations(1); -// } -// -// @Override -// protected void executeLayout(mxIGraphLayout layout, Object parent) { -// if (counter % 10 == 0) -// { -// super.executeLayout(layout, parent); -//// fitGraph(); -// } -// counter++; -// } -// -// @Override -// public mxIGraphLayout getLayout(Object parent) { -// if (graph.getModel().getChildCount(parent) > 0) { -// return layout; -// } -// return null; -// } -// }; } void clear() { @@ -156,10 +129,8 @@ final class mxGraphImpl extends mxGraph { scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); scopes.put("size", Math.round(Math.log(adiKey.getMessageCount()) + 5)); - - scopes.put("iconFileName", mxGraphImpl.class - .getResource("/org/sleuthkit/autopsy/communications/images/" - + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); + scopes.put("iconFileName", mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/" + + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); scopes.put("locked", lockedVertices.contains((mxCell) cell)); @@ -175,25 +146,69 @@ final class mxGraphImpl extends mxGraph { @Override public String getToolTipForCell(Object cell) { - return ((mxICell) cell).getId(); + final StringWriter stringWriter = new StringWriter(); + HashMap scopes = new HashMap<>(); + + Object value = getModel().getValue(cell); + if (value instanceof AccountDeviceInstanceKey) { + final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value; + + scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); + scopes.put("size", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5)); + scopes.put("iconFileName", mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/" + + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); + scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); + scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); + scopes.put("locked", lockedVertices.contains((mxCell) cell)); + scopes.put("LOCK_URL", LOCK_URL); + + labelMustache.execute(stringWriter, scopes); + + return stringWriter.toString(); + } else { + return ((mxICell) cell).getId(); + } } + /** + * Unpin the given accounts from the graph. Pinned accounts will always be + * shown regardless of the filter state. Furthermore, accounts with + * relationships that pass the filters will also be shown. + * + * @param accountDeviceInstances The accounts to unpin. + */ void unpinAccount(ImmutableSet accountDeviceInstances) { pinnedAccountDevices.removeAll(accountDeviceInstances); } + /** + * Pin the given accounts to the graph. Pinned accounts will always be shown + * regardless of the filter state. Furthermore, accounts with relationships + * that pass the filters will also be shown. + * + * @param accountDeviceInstances The accounts to pin. + */ void pinAccount(ImmutableSet accountDeviceInstances) { pinnedAccountDevices.addAll(accountDeviceInstances); } + /** + * Lock the given vertex so that applying a layout algorithm doesn't move + * it. The user can still manually position the vertex. + * + * @param vertex The vertex to lock. + */ void lockVertex(mxCell vertex) { lockedVertices.add(vertex); - - final mxCellState state = getView().getState(vertex, true); getView().clear(vertex, true, true); getView().validate(); } + /** + * Lock the given vertex so that applying a layout algorithm can move it. + * + * @param vertex The vertex to unlock. + */ void unlockVertex(mxCell vertex) { lockedVertices.remove(vertex); @@ -204,8 +219,7 @@ final class mxGraphImpl extends mxGraph { } SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { - - return new SwingWorkerImpl(progress, commsManager, currentFilter); + return new RebuildWorker(progress, commsManager, currentFilter); } void resetGraph() { @@ -261,12 +275,14 @@ final class mxGraphImpl extends mxGraph { return edge; } - boolean hasPinnedAccounts() { - return pinnedAccountDevices.isEmpty() == false; - } - - double getScale() { - return getView().getScale(); + /** + * Are there any accounts in this graph? If there are no pinned accounts the + * graph will be empty. + * + * @return True if this graph is empty. + */ + boolean isEmpty() { + return pinnedAccountDevices.isEmpty(); } boolean isVertexLocked(mxCell vertex) { @@ -274,13 +290,17 @@ final class mxGraphImpl extends mxGraph { } - private class SwingWorkerImpl extends SwingWorker { + /** + * SwingWorker that loads the accounts and edges for this graph according to + * the pinned accounts and the current filters. + */ + private class RebuildWorker extends SwingWorker { private final ProgressIndicator progress; private final CommunicationsManager commsManager; private final CommunicationsFilter currentFilter; - SwingWorkerImpl(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + RebuildWorker(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { this.progress = progress; this.currentFilter = currentFilter; this.commsManager = commsManager; From ae6dc361d19af1a401608f5311491a49603e2ca5 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 14 Feb 2018 11:15:11 +0100 Subject: [PATCH 049/128] Cancelation WIP --- .../communications/VisualizationPanel.java | 92 +++++++++++-------- .../autopsy/communications/mxGraphImpl.java | 18 ++-- 2 files changed, 67 insertions(+), 43 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index d7148fde20..02d365247e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -64,14 +64,19 @@ import javax.swing.JProgressBar; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.JToolBar; +import javax.swing.ProgressMonitor; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.jdesktop.layout.GroupLayout; import org.jdesktop.layout.LayoutStyle; +import org.netbeans.api.progress.BaseProgressUtils; +import org.netbeans.api.progress.ProgressHandle; +import org.netbeans.api.progress.ProgressRunnable; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.Node; +import org.openide.util.Cancellable; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; @@ -241,35 +246,24 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Subscribe void handleUnPinEvent(CVTEvents.UnpinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); - try { - graph.unpinAccount(pinEvent.getAccountDeviceInstances()); - graph.clear(); - rebuildGraph(); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error pinning accounts", ex); - } finally { - // Updates the display - graph.getModel().endUpdate(); - } + graph.unpinAccount(pinEvent.getAccountDeviceInstances()); + graph.clear(); + rebuildGraph(); + // Updates the display + graph.getModel().endUpdate(); } @Subscribe void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); - try { - if (pinEvent.isReplace()) { - graph.resetGraph(); - } - - graph.pinAccount(pinEvent.getAccountDeviceInstances()); - rebuildGraph(); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error pinning accounts", ex); - } finally { - // Updates the display - graph.getModel().endUpdate(); + if (pinEvent.isReplace()) { + graph.resetGraph(); } + graph.pinAccount(pinEvent.getAccountDeviceInstances()); + rebuildGraph(); + // Updates the display + graph.getModel().endUpdate(); } @@ -277,20 +271,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { graph.getModel().beginUpdate(); - try { - graph.clear(); - currentFilter = filterChangeEvent.getNewFilter(); - rebuildGraph(); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error filtering accounts", ex); - } finally { - // Updates the display - graph.getModel().endUpdate(); - } + graph.clear(); + currentFilter = filterChangeEvent.getNewFilter(); + rebuildGraph(); + // Updates the display + graph.getModel().endUpdate(); } - private void rebuildGraph() throws TskCoreException { + private void rebuildGraph() { if (graph.isEmpty()) { borderLayoutPanel.remove(graphComponent); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); @@ -300,8 +289,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider if (worker != null) { worker.cancel(true); } - worker = graph.rebuild(new ProgressIndicatorImpl(), commsManager, currentFilter); - + ; + BaseProgressUtils.showProgressDialogAndRun(new ProgressRunnableImpl(), "Loading Visualization", true); worker.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent evt) { @@ -312,6 +301,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider statusLabel.setText("Too many cells, layout aborted."); } } + if (worker.isCancelled()) { + graph.resetGraph(); + rebuildGraph(); + } } }); @@ -685,7 +678,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progressPanel.setVisible(true); progressBar.setIndeterminate(true); progressBar.setString("Computing layout"); - + final ProgressMonitor progressMonitor = new ProgressMonitor(this, "Computing layout", "", 0, 100); + progressMonitor.setProgress(0); new SwingWorker() { @Override protected Void doInBackground() throws Exception { @@ -696,8 +690,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider fitGraph(); progressPanel.setVisible(false); progressBar.setValue(0); + progressMonitor.close(); }); - + + progressMonitor.setProgress(50); SwingUtilities.invokeLater(() -> progressBar.setString("Applying layout")); morph.startAnimation(); return null; @@ -763,6 +759,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final private class ProgressIndicatorImpl implements ProgressIndicator { + ProgressMonitor progressMonitor = new ProgressMonitor(VisualizationPanel.this, "title", "detail", 0, 100); + @Override public void start(String message, int totalWorkUnits) { SwingUtilities.invokeLater(() -> { @@ -771,6 +769,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progressBar.setString(message); progressBar.setMaximum(totalWorkUnits); progressBar.setValue(0); + }); } @@ -780,6 +779,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progressPanel.setVisible(true); progressBar.setString(message); progressBar.setIndeterminate(true); + }); } @@ -815,6 +815,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider SwingUtilities.invokeLater(() -> { progressBar.setValue(workUnitsCompleted); }); + progressMonitor.setProgress(workUnitsCompleted); } @Override @@ -823,6 +824,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progressBar.setString(message); progressBar.setValue(workUnitsCompleted); }); + progressMonitor.setProgress(workUnitsCompleted); } @Override @@ -831,6 +833,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider progressBar.setValue(progressBar.getMaximum()); progressPanel.setVisible(false); }); + progressMonitor.close(); } } @@ -923,4 +926,19 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } } + + private class ProgressRunnableImpl implements ProgressRunnable, Cancellable { + + @Override + public Void run(ProgressHandle ph) { + worker = graph.rebuild(ph, commsManager, currentFilter); + + return null; + } + + @Override + public boolean cancel() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 46bec948c9..6263c8c175 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -44,8 +44,8 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; +import org.netbeans.api.progress.ProgressHandle; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; @@ -219,7 +219,7 @@ final class mxGraphImpl extends mxGraph { getView().updateBoundingBox(state); } - SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + SwingWorker rebuild(ProgressHandle progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { return new RebuildWorker(progress, commsManager, currentFilter); } @@ -296,19 +296,22 @@ final class mxGraphImpl extends mxGraph { */ private class RebuildWorker extends SwingWorker { - private final ProgressIndicator progress; + private final ProgressHandle progress; private final CommunicationsManager commsManager; private final CommunicationsFilter currentFilter; - RebuildWorker(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + RebuildWorker(ProgressHandle progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { this.progress = progress; this.currentFilter = currentFilter; this.commsManager = commsManager; + } @Override protected Void doInBackground() throws Exception { - progress.start("Loading accounts", pinnedAccountDevices.size()); + progress.progress("Loading accounts"); + progress.switchToDeterminate(pinnedAccountDevices.size()); + progress.progress(0); int i = 0; try { /** @@ -317,6 +320,7 @@ final class mxGraphImpl extends mxGraph { Set relatedAccounts = new HashSet<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { if (isCancelled()) { + cancel(true); break; } List relatedAccountDeviceInstances = @@ -335,11 +339,12 @@ final class mxGraphImpl extends mxGraph { //for each pair of related accounts add edges if they are related o each other. // this is O(n^2) in the number of related accounts!!! List relatedAccountsList = new ArrayList<>(relatedAccounts); - progress.switchToDeterminate("", 0, relatedAccountsList.size()); + progress.switchToDeterminate(relatedAccountsList.size()); for (i = 0; i < relatedAccountsList.size(); i++) { AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); for (int j = i; j < relatedAccountsList.size(); j++) { if (isCancelled()) { + cancel(true); break; } AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); @@ -369,6 +374,7 @@ final class mxGraphImpl extends mxGraph { } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, "Error building graph visualization. ", ex); } catch (CancellationException ex) { + logger.log(Level.INFO, "Graph visualization cancelled"); } finally { progress.finish(); From 535a1feee31f73fd4566f75252eb2c3f7dc8a468 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 14 Feb 2018 15:21:47 +0100 Subject: [PATCH 050/128] show cancellable progress in a dialog. --- .../autopsy/communications/Bundle.properties | 2 - .../communications/VisualizationPanel.form | 85 +------ .../communications/VisualizationPanel.java | 239 +++++------------- .../autopsy/communications/mxGraphImpl.java | 19 +- .../ModalDialogProgressIndicator.java | 3 +- 5 files changed, 76 insertions(+), 272 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 0f9e5ce048..dfbdb97ed0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -24,7 +24,6 @@ VisualizationPanel.jButton6.text=Hierarchy VisualizationPanel.jButton7.text=Circle VisualizationPanel.jButton8.text=Organic VisualizationPanel.fitGraphButton.text= -VisualizationPanel.statusLabel.text=\ VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. VisualizationPanel.jLabel1.text=Layouts: VisualizationPanel.fitZoomButton.text= @@ -41,4 +40,3 @@ VisualizationPanel.zoomInButton.toolTipText=Zoom in VisualizationPanel.zoomActualButton.toolTipText=reset zoom VisualizationPanel.fitZoomButton.toolTipText=fit visualization VisualizationPanel.organicLayoutButton.text=Organic -VisualizationPanel.cancelButton.text= diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index a4372087d1..c6eba12459 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -11,7 +11,7 @@ - + @@ -38,81 +38,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -124,18 +49,18 @@ - + - + - + - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 02d365247e..43f24b5899 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -38,6 +38,7 @@ import com.mxgraph.view.mxGraph; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; +import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; @@ -52,38 +53,35 @@ import static java.util.Collections.singleton; import java.util.EnumSet; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.Future; import java.util.logging.Level; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JMenuItem; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; -import javax.swing.JProgressBar; import javax.swing.JSplitPane; import javax.swing.JTextArea; import javax.swing.JToolBar; -import javax.swing.ProgressMonitor; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.jdesktop.layout.GroupLayout; import org.jdesktop.layout.LayoutStyle; -import org.netbeans.api.progress.BaseProgressUtils; -import org.netbeans.api.progress.ProgressHandle; -import org.netbeans.api.progress.ProgressRunnable; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.Node; -import org.openide.util.Cancellable; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.progress.ProgressIndicator; +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; @@ -131,7 +129,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final mxCircleLayout circleLayout; private final mxOrganicLayout organicLayout; private final mxHierarchicalLayout hierarchicalLayout; + + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private SwingWorker worker; + private Frame windowAncestor; public VisualizationPanel() { initComponents(); @@ -153,7 +154,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.setToolTips(true); graphComponent.setBackground(Color.WHITE); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); - progressPanel.setVisible(false); //install rubber band selection handler rubberband = new mxRubberband(graphComponent); @@ -193,7 +193,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.unlockVertex(cellAt); } })); - } else { jPopupMenu.add(new JMenuItem(new AbstractAction("Lock " + cellAt.getId(), lockIcon) { @Override @@ -201,7 +200,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.lockVertex(cellAt); } })); - } if (graph.isAccountPinned(adiKey)) { jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin " + cellAt.getId(), unpinIcon) { @@ -211,7 +209,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } })); } else { - jPopupMenu.add(new JMenuItem(new AbstractAction("Pin " + cellAt.getId(), addPinIcon) { @Override public void actionPerformed(ActionEvent e) { @@ -225,7 +222,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } })); } - jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY()); } } @@ -279,41 +275,42 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private void rebuildGraph() { if (graph.isEmpty()) { borderLayoutPanel.remove(graphComponent); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); + repaint(); } else { borderLayoutPanel.remove(placeHolderPanel); borderLayoutPanel.add(graphComponent, BorderLayout.CENTER); if (worker != null) { worker.cancel(true); } - ; - BaseProgressUtils.showProgressDialogAndRun(new ProgressRunnableImpl(), "Loading Visualization", true); + + CancelationListener cancelationListener = new CancelationListener(); + ModalDialogProgressIndicator modalDialogProgressIndicator = new ModalDialogProgressIndicator(windowAncestor, "Loading Visualization", new String[]{"Cancel"}, "Cancel", cancelationListener); + worker = graph.rebuild(modalDialogProgressIndicator, commsManager, currentFilter); + cancelationListener.setCancellable(worker); worker.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(final PropertyChangeEvent evt) { if (worker.isDone()) { - if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { + if (worker.isCancelled()) { + graph.resetGraph(); + rebuildGraph(); + } else if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { applyOrganicLayout(10); } else { - statusLabel.setText("Too many cells, layout aborted."); + JOptionPane.showMessageDialog(VisualizationPanel.this, + "Too many accounts, layout aborted.", + "Autopsy", + JOptionPane.WARNING_MESSAGE); } } - if (worker.isCancelled()) { - graph.resetGraph(); - rebuildGraph(); - } } }); - cancelButton.setAction(new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - worker.cancel(true); - } - }); worker.execute(); } @@ -322,9 +319,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public void addNotify() { super.addNotify(); + windowAncestor = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, this); + 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) { @@ -369,11 +367,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider splitPane = new JSplitPane(); borderLayoutPanel = new JPanel(); - jToolBar2 = new JToolBar(); - statusLabel = new JLabel(); - progressPanel = new JPanel(); - progressBar = new JProgressBar(); - cancelButton = new JButton(); placeHolderPanel = new JPanel(); jTextArea1 = new JTextArea(); toolbar = new JPanel(); @@ -397,43 +390,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider borderLayoutPanel.setLayout(new BorderLayout()); - jToolBar2.setFloatable(false); - jToolBar2.setRollover(true); - - statusLabel.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.statusLabel.text")); // NOI18N - jToolBar2.add(statusLabel); - - progressPanel.setMinimumSize(new Dimension(0, 20)); - progressPanel.setName(""); // NOI18N - - progressBar.setMaximumSize(new Dimension(200, 14)); - progressBar.setStringPainted(true); - - cancelButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/cross-script.png"))); // NOI18N - cancelButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.cancelButton.text")); // NOI18N - - GroupLayout progressPanelLayout = new GroupLayout(progressPanel); - progressPanel.setLayout(progressPanelLayout); - progressPanelLayout.setHorizontalGroup(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, progressPanelLayout.createSequentialGroup() - .addContainerGap(415, Short.MAX_VALUE) - .add(progressBar, GroupLayout.PREFERRED_SIZE, 350, GroupLayout.PREFERRED_SIZE) - .addPreferredGap(LayoutStyle.RELATED) - .add(cancelButton, GroupLayout.PREFERRED_SIZE, 24, GroupLayout.PREFERRED_SIZE) - .add(5, 5, 5)) - ); - progressPanelLayout.setVerticalGroup(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) - .add(GroupLayout.TRAILING, progressPanelLayout.createSequentialGroup() - .add(0, 0, 0) - .add(progressPanelLayout.createParallelGroup(GroupLayout.LEADING) - .add(cancelButton, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .add(progressBar, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) - ); - - jToolBar2.add(progressPanel); - - borderLayoutPanel.add(jToolBar2, BorderLayout.PAGE_END); - jTextArea1.setBackground(new Color(240, 240, 240)); jTextArea1.setColumns(20); jTextArea1.setLineWrap(true); @@ -444,15 +400,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider placeHolderPanel.setLayout(placeHolderPanelLayout); placeHolderPanelLayout.setHorizontalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(217, Short.MAX_VALUE) + .addContainerGap(208, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 372, GroupLayout.PREFERRED_SIZE) - .addContainerGap(218, Short.MAX_VALUE)) + .addContainerGap(209, Short.MAX_VALUE)) ); placeHolderPanelLayout.setVerticalGroup(placeHolderPanelLayout.createParallelGroup(GroupLayout.LEADING) .add(GroupLayout.TRAILING, placeHolderPanelLayout.createSequentialGroup() - .addContainerGap(387, Short.MAX_VALUE) + .addContainerGap(213, Short.MAX_VALUE) .add(jTextArea1, GroupLayout.PREFERRED_SIZE, 43, GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + .addContainerGap(214, Short.MAX_VALUE)) ); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); @@ -675,35 +631,46 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // layout using morphing graph.getModel().beginUpdate(); - progressPanel.setVisible(true); - progressBar.setIndeterminate(true); - progressBar.setString("Computing layout"); - final ProgressMonitor progressMonitor = new ProgressMonitor(this, "Computing layout", "", 0, 100); - progressMonitor.setProgress(0); - new SwingWorker() { + CancelationListener cancelationListener = new CancelationListener(); + ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Computing layout", new String[]{"Cancel"}, "Cancel", cancelationListener); + SwingWorker morphWorker = new SwingWorker() { @Override protected Void doInBackground() throws Exception { + progress.start("Computing layout"); layout.execute(graph.getDefaultParent()); - mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); + if (isCancelled()) { + progress.finish(); + return null; + } + mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20) { + @Override + public void updateAnimation() { + fireEvent(new mxEventObject(mxEvent.EXECUTE)); + super.updateAnimation(); //To change body of generated methods, choose Tools | Templates. + } + + }; + morph.addListener(mxEvent.EXECUTE, (Object sender, mxEventObject evt) -> { + if (isCancelled()) { + morph.stopAnimation(); + } + }); morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { graph.getModel().endUpdate(); fitGraph(); - progressPanel.setVisible(false); - progressBar.setValue(0); - progressMonitor.close(); + progress.finish(); }); - progressMonitor.setProgress(50); - SwingUtilities.invokeLater(() -> progressBar.setString("Applying layout")); morph.startAnimation(); return null; } - }.execute(); + }; + cancelationListener.setCancellable(morphWorker); + morphWorker.execute(); } // Variables declaration - do not modify//GEN-BEGIN:variables private JPanel borderLayoutPanel; - private JButton cancelButton; private JButton circleLayoutButton; private JButton fastOrganicLayoutButton; private JButton fitZoomButton; @@ -712,13 +679,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JLabel jLabel2; private JToolBar.Separator jSeparator1; private JTextArea jTextArea1; - private JToolBar jToolBar2; private JButton organicLayoutButton; private JPanel placeHolderPanel; - private JProgressBar progressBar; - private JPanel progressPanel; private JSplitPane splitPane; - private JLabel statusLabel; private JPanel toolbar; private JButton zoomActualButton; private JButton zoomInButton; @@ -757,86 +720,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } - final private class ProgressIndicatorImpl implements ProgressIndicator { - - ProgressMonitor progressMonitor = new ProgressMonitor(VisualizationPanel.this, "title", "detail", 0, 100); - - @Override - public void start(String message, int totalWorkUnits) { - SwingUtilities.invokeLater(() -> { - progressPanel.setVisible(true); - progressBar.setIndeterminate(false); - progressBar.setString(message); - progressBar.setMaximum(totalWorkUnits); - progressBar.setValue(0); - - }); - } - - @Override - public void start(String message) { - SwingUtilities.invokeLater(() -> { - progressPanel.setVisible(true); - progressBar.setString(message); - progressBar.setIndeterminate(true); - - }); - } - - @Override - public void switchToIndeterminate(String message) { - SwingUtilities.invokeLater(() -> { - progressPanel.setVisible(true); - progressBar.setIndeterminate(true); - progressBar.setString(message); - }); - } - - @Override - public void switchToDeterminate(String message, int workUnitsCompleted, int totalWorkUnits) { - SwingUtilities.invokeLater(() -> { - progressPanel.setVisible(true); - progressBar.setIndeterminate(false); - progressBar.setString(message); - progressBar.setMaximum(totalWorkUnits); - progressBar.setValue(workUnitsCompleted); - }); - } - - @Override - public void progress(String message) { - SwingUtilities.invokeLater(() -> { - progressBar.setString(message); - }); - } - - @Override - public void progress(int workUnitsCompleted) { - SwingUtilities.invokeLater(() -> { - progressBar.setValue(workUnitsCompleted); - }); - progressMonitor.setProgress(workUnitsCompleted); - } - - @Override - public void progress(String message, int workUnitsCompleted) { - SwingUtilities.invokeLater(() -> { - progressBar.setString(message); - progressBar.setValue(workUnitsCompleted); - }); - progressMonitor.setProgress(workUnitsCompleted); - } - - @Override - public void finish() { - SwingUtilities.invokeLater(() -> { - progressBar.setValue(progressBar.getMaximum()); - progressPanel.setVisible(false); - }); - progressMonitor.close(); - } - } - final private class mxFastOrganicLayoutImpl extends mxFastOrganicLayout { mxFastOrganicLayoutImpl(mxGraph graph) { @@ -927,18 +810,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } - private class ProgressRunnableImpl implements ProgressRunnable, Cancellable { + private class CancelationListener implements ActionListener { - @Override - public Void run(ProgressHandle ph) { - worker = graph.rebuild(ph, commsManager, currentFilter); + private Future cancellable; - return null; + void setCancellable(Future cancellable) { + this.cancellable = cancellable; } @Override - public boolean cancel() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + public void actionPerformed(ActionEvent e) { + cancellable.cancel(true); } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java index 6263c8c175..e4c3dc9746 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java @@ -44,8 +44,8 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; -import org.netbeans.api.progress.ProgressHandle; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; @@ -219,7 +219,7 @@ final class mxGraphImpl extends mxGraph { getView().updateBoundingBox(state); } - SwingWorker rebuild(ProgressHandle progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { return new RebuildWorker(progress, commsManager, currentFilter); } @@ -296,11 +296,11 @@ final class mxGraphImpl extends mxGraph { */ private class RebuildWorker extends SwingWorker { - private final ProgressHandle progress; + private final ProgressIndicator progress; private final CommunicationsManager commsManager; private final CommunicationsFilter currentFilter; - RebuildWorker(ProgressHandle progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { + RebuildWorker(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { this.progress = progress; this.currentFilter = currentFilter; this.commsManager = commsManager; @@ -309,9 +309,8 @@ final class mxGraphImpl extends mxGraph { @Override protected Void doInBackground() throws Exception { - progress.progress("Loading accounts"); - progress.switchToDeterminate(pinnedAccountDevices.size()); - progress.progress(0); + progress.start("Loading accounts"); + progress.switchToDeterminate("Loading accounts", 0,pinnedAccountDevices.size()); int i = 0; try { /** @@ -320,7 +319,6 @@ final class mxGraphImpl extends mxGraph { Set relatedAccounts = new HashSet<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { if (isCancelled()) { - cancel(true); break; } List relatedAccountDeviceInstances = @@ -339,12 +337,11 @@ final class mxGraphImpl extends mxGraph { //for each pair of related accounts add edges if they are related o each other. // this is O(n^2) in the number of related accounts!!! List relatedAccountsList = new ArrayList<>(relatedAccounts); - progress.switchToDeterminate(relatedAccountsList.size()); + progress.switchToDeterminate("",0,relatedAccountsList.size()); for (i = 0; i < relatedAccountsList.size(); i++) { AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); for (int j = i; j < relatedAccountsList.size(); j++) { if (isCancelled()) { - cancel(true); break; } AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); @@ -363,6 +360,7 @@ final class mxGraphImpl extends mxGraph { logger.log(Level.SEVERE, "Error", tskCoreException); } finally { } + return null; } @@ -374,7 +372,6 @@ final class mxGraphImpl extends mxGraph { } catch (InterruptedException | ExecutionException ex) { logger.log(Level.SEVERE, "Error building graph visualization. ", ex); } catch (CancellationException ex) { - logger.log(Level.INFO, "Graph visualization cancelled"); } finally { progress.finish(); diff --git a/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java b/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java index d49574bf24..992a35acc0 100644 --- a/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java +++ b/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java @@ -247,7 +247,7 @@ public final class ModalDialogProgressIndicator implements ProgressIndicator { DialogDescriptor.BOTTOM_ALIGN, HelpCtx.DEFAULT_HELP, buttonListener); - dialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor); + dialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor, parent); } else { /* * Dialog without buttons. @@ -256,6 +256,7 @@ public final class ModalDialogProgressIndicator implements ProgressIndicator { dialog.add(progressPanel); dialog.pack(); } + dialog.setResizable(false); dialog.setLocationRelativeTo(parent); this.dialog.setVisible(true); } From 54da8d314515a03eb0d09409d73c93d49b355f46 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 14 Feb 2018 17:02:51 +0100 Subject: [PATCH 051/128] use undoManager to implement better layout cancelation --- ...raphImpl.java => CommunicationsGraph.java} | 28 ++++---- .../communications/VisualizationPanel.java | 72 ++++++++++++------- 2 files changed, 59 insertions(+), 41 deletions(-) rename Core/src/org/sleuthkit/autopsy/communications/{mxGraphImpl.java => CommunicationsGraph.java} (92%) diff --git a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java similarity index 92% rename from Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java rename to Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index e4c3dc9746..b7559141ec 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/mxGraphImpl.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -52,25 +52,25 @@ import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; -final class mxGraphImpl extends mxGraph { +final class CommunicationsGraph extends mxGraph { + + private static final Logger logger = Logger.getLogger(CommunicationsGraph.class.getName()); + private static final URL MARKER_PIN_URL = CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png"); + private static final URL LOCK_URL = CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/lock_large_locked.png"); - private static final Logger logger = Logger.getLogger(mxGraphImpl.class.getName()); - private static final URL MARKER_PIN_URL = mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png"); - private static final URL LOCK_URL = mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/lock_large_locked.png"); /** * mustache.java template */ private final static Mustache labelMustache; static { -// String labelTemplatePath = "/org/sleuthkit/autopsy/communications/Vertex_Label_template.html"; - InputStream templateStream = mxGraphImpl.class.getResourceAsStream("/org/sleuthkit/autopsy/communications/Vertex_Label_template.html"); + InputStream templateStream = CommunicationsGraph.class.getResourceAsStream("/org/sleuthkit/autopsy/communications/Vertex_Label_template.html"); labelMustache = new DefaultMustacheFactory().compile(new InputStreamReader(templateStream), "Vertex_Label"); } static final private mxStylesheet mxStylesheet = new mxStylesheet(); - private final HashSet pinnedAccountDevices = new HashSet<>(); - private final HashSet lockedVertices = new HashSet<>(); + private final Set pinnedAccountDevices = new HashSet<>(); + private final Set lockedVertices = new HashSet<>(); private final Map nodeMap = new HashMap<>(); private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); @@ -88,7 +88,7 @@ final class mxGraphImpl extends mxGraph { mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE); } - mxGraphImpl() { + CommunicationsGraph() { super(mxStylesheet); setAutoSizeCells(true); setCellsCloneable(false); @@ -130,7 +130,7 @@ final class mxGraphImpl extends mxGraph { scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); scopes.put("size", Math.round(Math.log(adiKey.getMessageCount()) + 5)); - scopes.put("iconFileName", mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/" + scopes.put("iconFileName", CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); @@ -156,7 +156,7 @@ final class mxGraphImpl extends mxGraph { scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); scopes.put("size", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5)); - scopes.put("iconFileName", mxGraphImpl.class.getResource("/org/sleuthkit/autopsy/communications/images/" + scopes.put("iconFileName", CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); @@ -310,7 +310,7 @@ final class mxGraphImpl extends mxGraph { @Override protected Void doInBackground() throws Exception { progress.start("Loading accounts"); - progress.switchToDeterminate("Loading accounts", 0,pinnedAccountDevices.size()); +// progress.switchToDeterminate("Loading accounts", 0,pinnedAccountDevices.size()); int i = 0; try { /** @@ -337,7 +337,7 @@ final class mxGraphImpl extends mxGraph { //for each pair of related accounts add edges if they are related o each other. // this is O(n^2) in the number of related accounts!!! List relatedAccountsList = new ArrayList<>(relatedAccounts); - progress.switchToDeterminate("",0,relatedAccountsList.size()); + progress.switchToDeterminate("", 0, relatedAccountsList.size()); for (i = 0; i < relatedAccountsList.size(); i++) { AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); for (int j = i; j < relatedAccountsList.size(); j++) { @@ -360,7 +360,7 @@ final class mxGraphImpl extends mxGraph { logger.log(Level.SEVERE, "Error", tskCoreException); } finally { } - + return null; } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 43f24b5899..0d0991f840 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -34,6 +34,8 @@ import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxPoint; import com.mxgraph.util.mxRectangle; +import com.mxgraph.util.mxUndoManager; +import com.mxgraph.util.mxUndoableEdit; import com.mxgraph.view.mxGraph; import java.awt.BorderLayout; import java.awt.Color; @@ -45,7 +47,6 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.text.DecimalFormat; import java.util.Arrays; @@ -97,6 +98,7 @@ import org.sleuthkit.datamodel.TskCoreException; * CVTTopComponent when this tab is active allowing for context sensitive * actions to work correctly. */ +@NbBundle.Messages("VisualizationPanel.cancelButton.text=Cancel") final public class VisualizationPanel extends JPanel implements Lookup.Provider { private static final long serialVersionUID = 1L; @@ -113,17 +115,23 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider static final private ImageIcon lockIcon = new ImageIcon(VisualizationPanel.class.getResource(BASE_IMAGE_PATH + "/lock_large_locked.png")); + 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 = new ProxyLookup( ExplorerUtils.createLookup(gacEM, getActionMap()), ExplorerUtils.createLookup(vizEM, getActionMap())); - private final mxGraphComponent graphComponent; - private final mxGraphImpl graph; + private Frame windowAncestor; private CommunicationsManager commsManager; private CommunicationsFilter currentFilter; + + private final mxGraphComponent graphComponent; + private final CommunicationsGraph graph; + + protected mxUndoManager undoManager = new mxUndoManager(); private final mxRubberband rubberband; private final mxFastOrganicLayout fastOrganicLayout; private final mxCircleLayout circleLayout; @@ -132,11 +140,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private SwingWorker worker; - private Frame windowAncestor; public VisualizationPanel() { initComponents(); - graph = new mxGraphImpl(); + graph = new CommunicationsGraph(); fastOrganicLayout = new mxFastOrganicLayoutImpl(graph); circleLayout = new mxCircleLayoutImpl(graph); @@ -232,6 +239,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider //feed selection to explorermanager graph.getSelectionModel().addListener(null, new SelectionListener()); + final mxEventSource.mxIEventListener undoListener = (Object sender, mxEventObject evt) -> + undoManager.undoableEditHappened((mxUndoableEdit) evt.getProperty("edit")); + + graph.getModel().addListener(mxEvent.UNDO, undoListener); + graph.getView().addListener(mxEvent.UNDO, undoListener); } @Override @@ -289,24 +301,21 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } CancelationListener cancelationListener = new CancelationListener(); - ModalDialogProgressIndicator modalDialogProgressIndicator = new ModalDialogProgressIndicator(windowAncestor, "Loading Visualization", new String[]{"Cancel"}, "Cancel", cancelationListener); - worker = graph.rebuild(modalDialogProgressIndicator, commsManager, currentFilter); - cancelationListener.setCancellable(worker); - worker.addPropertyChangeListener(new PropertyChangeListener() { - @Override - public void propertyChange(final PropertyChangeEvent evt) { - if (worker.isDone()) { - if (worker.isCancelled()) { - graph.resetGraph(); - rebuildGraph(); - } else if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { - applyOrganicLayout(10); - } else { - JOptionPane.showMessageDialog(VisualizationPanel.this, - "Too many accounts, layout aborted.", - "Autopsy", - JOptionPane.WARNING_MESSAGE); - } + ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Loading Visualization", new String[]{CANCEL}, CANCEL, cancelationListener); + worker = graph.rebuild(progress, commsManager, currentFilter); + cancelationListener.configure(worker, progress); + worker.addPropertyChangeListener((final PropertyChangeEvent evt) -> { + if (worker.isDone()) { + if (worker.isCancelled()) { + graph.resetGraph(); + rebuildGraph(); + } else if (graph.getModel().getChildCount(graph.getDefaultParent()) < 64) { + applyOrganicLayout(10); + } else { + JOptionPane.showMessageDialog(VisualizationPanel.this, + "Too many accounts, layout aborted.", + "Autopsy", + JOptionPane.WARNING_MESSAGE); } } }); @@ -632,7 +641,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().beginUpdate(); CancelationListener cancelationListener = new CancelationListener(); - ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Computing layout", new String[]{"Cancel"}, "Cancel", cancelationListener); + ModalDialogProgressIndicator progress = new ModalDialogProgressIndicator(windowAncestor, "Computing layout", new String[]{CANCEL}, CANCEL, cancelationListener); SwingWorker morphWorker = new SwingWorker() { @Override protected Void doInBackground() throws Exception { @@ -657,15 +666,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { graph.getModel().endUpdate(); - fitGraph(); + if (isCancelled()) { + undoManager.undo(); + } else { + fitGraph(); + } progress.finish(); }); morph.startAnimation(); return null; + } }; - cancelationListener.setCancellable(morphWorker); + cancelationListener.configure(morphWorker, progress); morphWorker.execute(); } @@ -813,14 +827,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private class CancelationListener implements ActionListener { private Future cancellable; + private ModalDialogProgressIndicator progress; - void setCancellable(Future cancellable) { + void configure(Future cancellable, ModalDialogProgressIndicator progress) { this.cancellable = cancellable; + this.progress = progress; } @Override public void actionPerformed(ActionEvent e) { + progress.setCancelling("Cancelling..."); cancellable.cancel(true); + progress.finish(); } } From cb8eb44104af8bedf85187f445c91d917283261b Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 15 Feb 2018 13:16:37 +0100 Subject: [PATCH 052/128] factor lockedVertexModel and PinnedAccountModel out of CommunicationsGraph --- .../communications/CommunicationsGraph.java | 138 ++++++++---------- .../communications/LockedVertexModel.java | 86 +++++++++++ .../communications/PinnedAccountModel.java | 71 +++++++++ .../communications/VisualizationPanel.java | 32 ++-- .../visualization/EventHandler.java | 9 ++ 5 files changed, 243 insertions(+), 93 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index b7559141ec..dd0adef1d4 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -20,9 +20,9 @@ package org.sleuthkit.autopsy.communications; import com.github.mustachejava.DefaultMustacheFactory; import com.github.mustachejava.Mustache; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; +import com.google.common.eventbus.Subscribe; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; import com.mxgraph.util.mxConstants; @@ -44,6 +44,7 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; +import org.sleuthkit.autopsy.communications.visualization.EventHandler; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -52,6 +53,10 @@ import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; +/** + * Implementation of mxGraph customized for our use in the CVT visualize mode. + * Acts as the primary entry point into the JGraphX API. + */ final class CommunicationsGraph extends mxGraph { private static final Logger logger = Logger.getLogger(CommunicationsGraph.class.getName()); @@ -68,12 +73,11 @@ final class CommunicationsGraph extends mxGraph { labelMustache = new DefaultMustacheFactory().compile(new InputStreamReader(templateStream), "Vertex_Label"); } + /** + * Style sheet for default vertex and edge styles. These are initialized in + * the static block below. + */ static final private mxStylesheet mxStylesheet = new mxStylesheet(); - private final Set pinnedAccountDevices = new HashSet<>(); - private final Set lockedVertices = new HashSet<>(); - - private final Map nodeMap = new HashMap<>(); - private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); static { //initialize defaul vertex properties @@ -88,8 +92,22 @@ final class CommunicationsGraph extends mxGraph { mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE); } + /** + * Map from type specific account identifier to mxCell(vertex). + */ + private final Map nodeMap = new HashMap<>(); + + /** + * Map from relationship source (Content) to mxCell (edge). + */ + private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); + private final LockedVertexModel lockedVertexModel; + + private final PinnedAccountModel pinnedAccountModel; + CommunicationsGraph() { super(mxStylesheet); + //set fixed properties of graph. setAutoSizeCells(true); setCellsCloneable(false); setDropEnabled(false); @@ -107,6 +125,33 @@ final class CommunicationsGraph extends mxGraph { setKeepEdgesInBackground(true); setResetEdgesOnMove(true); setHtmlLabels(true); + + lockedVertexModel = new LockedVertexModel(); + lockedVertexModel.registerhandler(new EventHandler() { + @Override + @Subscribe + public void handle(LockedVertexModel.VertexLockEvent event) { + if (event.isVertexLocked()) { + getView().clear(event.getVertex(), true, true); + getView().validate(); + } else { + final mxCellState state = getView().getState(event.getVertex(), true); + getView().updateLabel(state); + getView().updateLabelBounds(state); + getView().updateBoundingBox(state); + } + } + }); + + pinnedAccountModel = new PinnedAccountModel(this); + } + + public LockedVertexModel getLockedVertexModel() { + return lockedVertexModel; + } + + public PinnedAccountModel getPinnedAccountModel() { + return pinnedAccountModel; } void clear() { @@ -115,10 +160,6 @@ final class CommunicationsGraph extends mxGraph { removeCells(getChildVertices(getDefaultParent())); } - boolean isAccountPinned(AccountDeviceInstanceKey account) { - return pinnedAccountDevices.contains(account); - } - @Override public String convertValueToString(Object cell) { final StringWriter stringWriter = new StringWriter(); @@ -132,9 +173,9 @@ final class CommunicationsGraph extends mxGraph { scopes.put("size", Math.round(Math.log(adiKey.getMessageCount()) + 5)); scopes.put("iconFileName", CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); - scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); + scopes.put("pinned", pinnedAccountModel.isAccountPinned(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); - scopes.put("locked", lockedVertices.contains((mxCell) cell)); + scopes.put("locked", lockedVertexModel.isVertexLocked((mxCell) cell)); scopes.put("LOCK_URL", LOCK_URL); labelMustache.execute(stringWriter, scopes); @@ -158,9 +199,9 @@ final class CommunicationsGraph extends mxGraph { scopes.put("size", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5)); scopes.put("iconFileName", CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); - scopes.put("pinned", pinnedAccountDevices.contains(adiKey)); + scopes.put("pinned", pinnedAccountModel.isAccountPinned(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); - scopes.put("locked", lockedVertices.contains((mxCell) cell)); + scopes.put("locked", lockedVertexModel.isVertexLocked((mxCell) cell)); scopes.put("LOCK_URL", LOCK_URL); labelMustache.execute(stringWriter, scopes); @@ -171,54 +212,6 @@ final class CommunicationsGraph extends mxGraph { } } - /** - * Unpin the given accounts from the graph. Pinned accounts will always be - * shown regardless of the filter state. Furthermore, accounts with - * relationships that pass the filters will also be shown. - * - * @param accountDeviceInstances The accounts to unpin. - */ - void unpinAccount(ImmutableSet accountDeviceInstances) { - pinnedAccountDevices.removeAll(accountDeviceInstances); - } - - /** - * Pin the given accounts to the graph. Pinned accounts will always be shown - * regardless of the filter state. Furthermore, accounts with relationships - * that pass the filters will also be shown. - * - * @param accountDeviceInstances The accounts to pin. - */ - void pinAccount(ImmutableSet accountDeviceInstances) { - pinnedAccountDevices.addAll(accountDeviceInstances); - } - - /** - * Lock the given vertex so that applying a layout algorithm doesn't move - * it. The user can still manually position the vertex. - * - * @param vertex The vertex to lock. - */ - void lockVertex(mxCell vertex) { - lockedVertices.add(vertex); - getView().clear(vertex, true, true); - getView().validate(); - } - - /** - * Lock the given vertex so that applying a layout algorithm can move it. - * - * @param vertex The vertex to unlock. - */ - void unlockVertex(mxCell vertex) { - lockedVertices.remove(vertex); - - final mxCellState state = getView().getState(vertex, true); - getView().updateLabel(state); - getView().updateLabelBounds(state); - getView().updateBoundingBox(state); - } - SwingWorker rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) { return new RebuildWorker(progress, commsManager, currentFilter); } @@ -226,8 +219,8 @@ final class CommunicationsGraph extends mxGraph { void resetGraph() { clear(); getView().setScale(1); - pinnedAccountDevices.clear(); - lockedVertices.clear(); + pinnedAccountModel.clear(); + lockedVertexModel.clear(); } private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { @@ -275,21 +268,6 @@ final class CommunicationsGraph extends mxGraph { return edge; } - /** - * Are there any accounts in this graph? If there are no pinned accounts the - * graph will be empty. - * - * @return True if this graph is empty. - */ - boolean isEmpty() { - return pinnedAccountDevices.isEmpty(); - } - - boolean isVertexLocked(mxCell vertex) { - return lockedVertices.contains(vertex); - - } - /** * SwingWorker that loads the accounts and edges for this graph according to * the pinned accounts and the current filters. @@ -317,7 +295,7 @@ final class CommunicationsGraph extends mxGraph { * set to keep track of accounts related to pinned accounts */ Set relatedAccounts = new HashSet<>(); - for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { + for (AccountDeviceInstanceKey adiKey : pinnedAccountModel.getPinnedAccounts()) { if (isCancelled()) { break; } diff --git a/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java b/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java new file mode 100644 index 0000000000..fd84f6d31b --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java @@ -0,0 +1,86 @@ +/* + * 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. + */ +package org.sleuthkit.autopsy.communications; + +import com.google.common.eventbus.EventBus; +import com.mxgraph.model.mxCell; +import java.util.HashSet; +import java.util.Set; +import org.sleuthkit.autopsy.communications.visualization.EventHandler; + +class LockedVertexModel { + + void registerhandler(EventHandler handler) { + eventBus.register(handler); + } + + void unregisterhandler(EventHandler handler) { + eventBus.unregister(handler); + } + + private final EventBus eventBus = new EventBus(); + + /** + * Set of mxCells (vertices) that are 'locked'. Locked vertices are not + * affected by layout algorithms, but may be repositioned manually by the + * user. + */ + private final Set lockedVertices = new HashSet<>(); + + LockedVertexModel() { + } + + /** + * Lock the given vertex so that applying a layout algorithm doesn't move + * it. The user can still manually position the vertex. + * + * @param vertex The vertex to lock. + */ + void lockVertex(mxCell vertex) { + lockedVertices.add(vertex); + eventBus.post(new VertexLockEvent(vertex, true)); + + } + + /** + * Lock the given vertex so that applying a layout algorithm can move it. + * + * @param vertex The vertex to unlock. + */ + void unlockVertex(mxCell vertex) { + lockedVertices.remove(vertex); + eventBus.post(new VertexLockEvent(vertex, false)); + + } + + boolean isVertexLocked(mxCell vertex) { + return lockedVertices.contains(vertex); + + } + + void clear() { + lockedVertices.clear(); + } + + static class VertexLockEvent { + + private final mxCell vertex; + + public mxCell getVertex() { + return vertex; + } + + public boolean isVertexLocked() { + return locked; + } + private final boolean locked; + + VertexLockEvent(mxCell vertex, boolean locked) { + this.vertex = vertex; + this.locked = locked; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java b/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java new file mode 100644 index 0000000000..2afa5b8318 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java @@ -0,0 +1,71 @@ +/* + * 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. + */ +package org.sleuthkit.autopsy.communications; + +import com.google.common.collect.ImmutableSet; +import java.util.HashSet; +import java.util.Set; + +class PinnedAccountModel { + + /** + * Set of AccountDeviceInstanceKeys that are 'Pinned' to this graph. Pinned + * accounts are shown regardless of filters, and accounts that are related + * to pinned accounts and pass the filters are show. Pinning accounts is the + * primary way to populate the graph. + */ + private final Set pinnedAccountDevices = new HashSet<>(); + private final CommunicationsGraph graph; + + PinnedAccountModel(CommunicationsGraph graph) { + this.graph = graph; + } + + boolean isAccountPinned(AccountDeviceInstanceKey account) { + return pinnedAccountDevices.contains(account); + } + + /** + * Unpin the given accounts from the graph. Pinned accounts will always be + * shown regardless of the filter state. Furthermore, accounts with + * relationships that pass the filters will also be shown. + * + * @param accountDeviceInstances The accounts to unpin. + */ + void unpinAccount(ImmutableSet accountDeviceInstances) { + pinnedAccountDevices.removeAll(accountDeviceInstances); + } + + /** + * Pin the given accounts to the graph. Pinned accounts will always be shown + * regardless of the filter state. Furthermore, accounts with relationships + * that pass the filters will also be shown. + * + * @param accountDeviceInstances The accounts to pin. + */ + void pinAccount(ImmutableSet accountDeviceInstances) { + pinnedAccountDevices.addAll(accountDeviceInstances); + } + + /** + * Are there any accounts in this graph? If there are no pinned accounts the + * graph will be empty. + * + * @return True if this graph is empty. + */ + boolean isEmpty() { + return pinnedAccountDevices.isEmpty(); + } + + void clear() { + pinnedAccountDevices.clear(); + } + + Iterable getPinnedAccounts() { + return ImmutableSet.copyOf(pinnedAccountDevices); + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 0d0991f840..12b66824e6 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -91,8 +91,9 @@ 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. + * Tool. Hosts an JGraphX mxGraphComponent that implements 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 @@ -140,10 +141,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private SwingWorker worker; + private final PinnedAccountModel pinnedAccountModel; + private final LockedVertexModel lockedVertexModel; public VisualizationPanel() { initComponents(); graph = new CommunicationsGraph(); + pinnedAccountModel = graph.getPinnedAccountModel(); + lockedVertexModel = graph.getLockedVertexModel(); fastOrganicLayout = new mxFastOrganicLayoutImpl(graph); circleLayout = new mxCircleLayoutImpl(graph); @@ -193,22 +198,22 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final JPopupMenu jPopupMenu = new JPopupMenu(); final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) cellAt.getValue(); - if (graph.isVertexLocked(cellAt)) { + if (lockedVertexModel.isVertexLocked(cellAt)) { jPopupMenu.add(new JMenuItem(new AbstractAction("UnLock " + cellAt.getId(), unlockIcon) { @Override public void actionPerformed(ActionEvent e) { - graph.unlockVertex(cellAt); + lockedVertexModel.unlockVertex(cellAt); } })); } else { jPopupMenu.add(new JMenuItem(new AbstractAction("Lock " + cellAt.getId(), lockIcon) { @Override public void actionPerformed(ActionEvent e) { - graph.lockVertex(cellAt); + lockedVertexModel.lockVertex(cellAt); } })); } - if (graph.isAccountPinned(adiKey)) { + if (pinnedAccountModel.isAccountPinned(adiKey)) { jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin " + cellAt.getId(), unpinIcon) { @Override public void actionPerformed(ActionEvent e) { @@ -247,6 +252,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Override + public Lookup getLookup() { return proxyLookup; } @@ -254,7 +260,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Subscribe void handleUnPinEvent(CVTEvents.UnpinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); - graph.unpinAccount(pinEvent.getAccountDeviceInstances()); + pinnedAccountModel.unpinAccount(pinEvent.getAccountDeviceInstances()); graph.clear(); rebuildGraph(); // Updates the display @@ -268,7 +274,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider if (pinEvent.isReplace()) { graph.resetGraph(); } - graph.pinAccount(pinEvent.getAccountDeviceInstances()); + pinnedAccountModel.pinAccount(pinEvent.getAccountDeviceInstances()); rebuildGraph(); // Updates the display graph.getModel().endUpdate(); @@ -289,7 +295,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private void rebuildGraph() { - if (graph.isEmpty()) { + if (pinnedAccountModel.isEmpty()) { borderLayoutPanel.remove(graphComponent); borderLayoutPanel.add(placeHolderPanel, BorderLayout.CENTER); repaint(); @@ -743,7 +749,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public boolean isVertexIgnored(Object vertex) { return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + || lockedVertexModel.isVertexLocked((mxCell) vertex); } @Override @@ -766,7 +772,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public boolean isVertexIgnored(Object vertex) { return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + || lockedVertexModel.isVertexLocked((mxCell) vertex); } @Override @@ -789,7 +795,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public boolean isVertexIgnored(Object vertex) { return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + || lockedVertexModel.isVertexLocked((mxCell) vertex); } @Override @@ -811,7 +817,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Override public boolean isVertexIgnored(Object vertex) { return super.isVertexIgnored(vertex) - || VisualizationPanel.this.graph.isVertexLocked((mxCell) vertex); + || lockedVertexModel.isVertexLocked((mxCell) vertex); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java b/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java new file mode 100644 index 0000000000..539da4be4a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java @@ -0,0 +1,9 @@ +package org.sleuthkit.autopsy.communications.visualization; + +/** + * + */ +public interface EventHandler { + + void handle(T event); +} From 81b7b471aba3b3d330ae71a47df58c445a6b47de Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 16 Feb 2018 16:02:28 +0100 Subject: [PATCH 053/128] make sure pinned accounts always show in visualization; cleanup --- .../autopsy/communications/Bundle.properties | 23 ++++++++------- .../communications/CVTTopComponent.form | 2 +- .../communications/CommunicationsGraph.java | 7 +++-- .../autopsy/communications/EventHandler.java | 28 +++++++++++++++++++ .../communications/LockedVertexModel.java | 20 ++++++++++--- .../communications/PinnedAccountModel.java | 19 +++++++++++-- .../visualization/EventHandler.java | 9 ------ 7 files changed, 78 insertions(+), 30 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/EventHandler.java delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index dfbdb97ed0..96d76fe34a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -24,19 +24,22 @@ VisualizationPanel.jButton6.text=Hierarchy VisualizationPanel.jButton7.text=Circle VisualizationPanel.jButton8.text=Organic VisualizationPanel.fitGraphButton.text= -VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. -VisualizationPanel.jLabel1.text=Layouts: -VisualizationPanel.fitZoomButton.text= -VisualizationPanel.zoomActualButton.text= -VisualizationPanel.zoomInButton.text= VisualizationPanel.zoomOutButton.text= +# 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. VisualizationPanel.circleLayoutButton.text=Circle +VisualizationPanel.organicLayoutButton.text=Organic VisualizationPanel.fastOrganicLayoutButton.text=Fast Organic VisualizationPanel.hierarchyLayoutButton.text=Hierarchy -VisualizationPanel.jLabel2.text=Zoom: VisualizationPanel.zoomLabel.text=100% -VisualizationPanel.zoomOutButton.toolTipText=Zoom out -VisualizationPanel.zoomInButton.toolTipText=Zoom in -VisualizationPanel.zoomActualButton.toolTipText=reset zoom +VisualizationPanel.jLabel1.text=Layouts: +VisualizationPanel.jLabel2.text=Zoom: VisualizationPanel.fitZoomButton.toolTipText=fit visualization -VisualizationPanel.organicLayoutButton.text=Organic +VisualizationPanel.fitZoomButton.text= +VisualizationPanel.jTextArea1.text=Right-click an account in the Browse Accounts table, and select 'Visualize' to begin. +VisualizationPanel.zoomActualButton.toolTipText=reset zoom +VisualizationPanel.zoomActualButton.text= +VisualizationPanel.zoomInButton.toolTipText=Zoom in +VisualizationPanel.zoomInButton.text= +VisualizationPanel.zoomOutButton.toolTipText=Zoom out diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form index 9315465965..75898c859e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form @@ -92,4 +92,4 @@
- + \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index dd0adef1d4..f278bc311b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -44,7 +44,6 @@ import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.SwingWorker; -import org.sleuthkit.autopsy.communications.visualization.EventHandler; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -146,11 +145,11 @@ final class CommunicationsGraph extends mxGraph { pinnedAccountModel = new PinnedAccountModel(this); } - public LockedVertexModel getLockedVertexModel() { + LockedVertexModel getLockedVertexModel() { return lockedVertexModel; } - public PinnedAccountModel getPinnedAccountModel() { + PinnedAccountModel getPinnedAccountModel() { return pinnedAccountModel; } @@ -302,6 +301,8 @@ final class CommunicationsGraph extends mxGraph { List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); relatedAccounts.add(adiKey); + getOrCreateVertex(adiKey); + //get accounts related to pinned account for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { // handle.progress(1); diff --git a/Core/src/org/sleuthkit/autopsy/communications/EventHandler.java b/Core/src/org/sleuthkit/autopsy/communications/EventHandler.java new file mode 100644 index 0000000000..f1acedf1cf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/EventHandler.java @@ -0,0 +1,28 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.sleuthkit.autopsy.communications; + +/** + * + */ +public interface EventHandler { + + void handle(T event); +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java b/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java index fd84f6d31b..f26e9515b3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/LockedVertexModel.java @@ -1,7 +1,20 @@ /* - * 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 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.communications; @@ -9,7 +22,6 @@ import com.google.common.eventbus.EventBus; import com.mxgraph.model.mxCell; import java.util.HashSet; import java.util.Set; -import org.sleuthkit.autopsy.communications.visualization.EventHandler; class LockedVertexModel { diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java b/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java index 2afa5b8318..d5f7a7cc03 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/PinnedAccountModel.java @@ -1,7 +1,20 @@ /* - * 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 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.communications; diff --git a/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java b/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java deleted file mode 100644 index 539da4be4a..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/visualization/EventHandler.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.sleuthkit.autopsy.communications.visualization; - -/** - * - */ -public interface EventHandler { - - void handle(T event); -} From 85d9577ea2cb46d5fefda58956b7660ccb217e1a Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 21 Feb 2018 13:21:02 +0100 Subject: [PATCH 054/128] use new getRelationshionshipsCount(a1,a2) method to improve graph loading time --- .../communications/CommunicationsGraph.java | 91 +++++++++++-------- .../communications/VisualizationPanel.java | 16 +++- 2 files changed, 65 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index f278bc311b..81ec5d7838 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -33,10 +33,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; import java.net.URL; -import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -50,6 +47,7 @@ import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.Relationship; import org.sleuthkit.datamodel.TskCoreException; /** @@ -239,30 +237,29 @@ final class CommunicationsGraph extends mxGraph { size); return newVertex; }); - final mxCellState state = getView().getState(vertex, true); - - getView().updateLabel(state); - getView().updateLabelBounds(state); - getView().updateBoundingBox(state); +// final mxCellState state = getView().getState(vertex, true); +// +// getView().updateLabel(state); +// getView().updateLabelBounds(state); +// getView().updateBoundingBox(state); return vertex; } @SuppressWarnings("unchecked") - private mxCell addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) { + private mxCell addOrUpdateEdge(long relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) { mxCell vertex1 = getOrCreateVertex(account1); mxCell vertex2 = getOrCreateVertex(account2); Object[] edgesBetween = getEdgesBetween(vertex1, vertex2); mxCell edge; if (edgesBetween.length == 0) { final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); - final HashSet hashSet = new HashSet<>(relSources); - edge = (mxCell) insertEdge(getDefaultParent(), edgeName, hashSet, vertex1, vertex2, - "strokeWidth=" + (Math.log(hashSet.size()) + 1)); + edge = (mxCell) insertEdge(getDefaultParent(), edgeName, relSources, vertex1, vertex2, + "strokeWidth=" + (Math.log(relSources) + 1)); } else { edge = (mxCell) edgesBetween[0]; - ((Collection) edge.getValue()).addAll(relSources); - edge.setStyle("strokeWidth=" + (Math.log(((Collection) edge.getValue()).size()) + 1)); +// ((Collection) edge.getValue()).addAll(relSources); + edge.setStyle("strokeWidth=" + (Math.log(relSources) + 1)); } return edge; } @@ -287,54 +284,68 @@ final class CommunicationsGraph extends mxGraph { @Override protected Void doInBackground() throws Exception { progress.start("Loading accounts"); -// progress.switchToDeterminate("Loading accounts", 0,pinnedAccountDevices.size()); int i = 0; try { /** * set to keep track of accounts related to pinned accounts */ - Set relatedAccounts = new HashSet<>(); + Map relatedAccounts = new HashMap<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountModel.getPinnedAccounts()) { if (isCancelled()) { break; } List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); - relatedAccounts.add(adiKey); + relatedAccounts.put(adiKey.getAccountDeviceInstance().getAccount().getAccountID(), adiKey); getOrCreateVertex(adiKey); //get accounts related to pinned account for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { -// handle.progress(1); long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); - relatedAccounts.add(relatedADIKey); //store related accounts + relatedAccounts.put(relatedADI.getAccount().getAccountID(), relatedADIKey); //store related accounts } progress.progress(++i); } - //for each pair of related accounts add edges if they are related o each other. - // this is O(n^2) in the number of related accounts!!! - List relatedAccountsList = new ArrayList<>(relatedAccounts); - progress.switchToDeterminate("", 0, relatedAccountsList.size()); - for (i = 0; i < relatedAccountsList.size(); i++) { - AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); - for (int j = i; j < relatedAccountsList.size(); j++) { - if (isCancelled()) { - break; - } - AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); - List relationships = commsManager.getRelationshipSources( - adiKey1.getAccountDeviceInstance(), - adiKey2.getAccountDeviceInstance(), - currentFilter); - if (relationships.size() > 0) { - mxCell addEdge = addEdge(relationships, adiKey1, adiKey2); - progress.progress(addEdge.getId()); - } - } - progress.progress(i); + Set accountIDs = relatedAccounts.keySet(); + + Map relationshipCounts = commsManager.getRelationshipCounts(accountIDs, currentFilter); + + int total = relationshipCounts.size(); + int k = 0; + progress.switchToDeterminate("", 0,total); + for (Map.Entry entry : relationshipCounts.entrySet()) { + Long count = entry.getValue(); + Relationship.RelationshipKey relationshipKey = entry.getKey(); + AccountDeviceInstanceKey account1 = relatedAccounts.get(relationshipKey.getAccount1ID()); + AccountDeviceInstanceKey account2 = relatedAccounts.get(relationshipKey.getAccount2ID()); + mxCell addEdge = addOrUpdateEdge(count, account1, account2); + progress.progress(addEdge.getId(),k++); } +// //for each pair of related accounts add edges if they are related o each other. +// // this is O(n^2) in the number of related accounts!!! +// List relatedAccountsList = new ArrayList<>(relatedAccounts); +// progress.switchToDeterminate("", 0, relatedAccountsList.size()); +// +// for (i = 0; i < relatedAccountsList.size(); i++) { +// AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); +// for (int j = i; j < relatedAccountsList.size(); j++) { +// if (isCancelled()) { +// break; +// } +// AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); +// List relationships = commsManager.getRelationshipSources( +// adiKey1.getAccountDeviceInstance(), +// adiKey2.getAccountDeviceInstance(), +// currentFilter); +// if (relationships.size() > 0) { +// mxCell addEdge = addEdge(relationships, adiKey1, adiKey2); +// progress.progress(addEdge.getId()); +// } +// } +// progress.progress(i); +// } } catch (TskCoreException tskCoreException) { logger.log(Level.SEVERE, "Error", tskCoreException); } finally { diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 12b66824e6..889b81cf74 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -53,7 +53,7 @@ import java.util.Arrays; import static java.util.Collections.singleton; import java.util.EnumSet; import java.util.HashSet; -import java.util.Set; +import java.util.List; import java.util.concurrent.Future; import java.util.logging.Level; import javax.swing.AbstractAction; @@ -723,11 +723,23 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider HashSet adis = new HashSet<>(); for (mxICell cell : selectedCells) { if (cell.isEdge()) { - relationshipSources.addAll((Set) cell.getValue()); + 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 relationshipSources1 = commsManager.getRelationshipSources(account1.getAccountDeviceInstance(), + account2.getAccountDeviceInstance(), + currentFilter); + relationshipSources.addAll(relationshipSources1); + } catch (TskCoreException tskCoreException) { + logger.log(Level.SEVERE, " Error getting relationsips....", tskCoreException); + } } else if (cell.isVertex()) { adis.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance()); } } + rootNode = SelectionNode.createFromAccountsAndRelationships(relationshipSources, adis, currentFilter, commsManager); selectedNodes = new Node[]{rootNode}; } From 62a17e69f8212c35bac18583d0392c031f64a342 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 26 Feb 2018 12:00:19 +0100 Subject: [PATCH 055/128] distinguish account from seperate devices --- .../communications/CommunicationsGraph.java | 14 ++++---------- .../autopsy/communications/VisualizationPanel.java | 3 ++- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index 81ec5d7838..4cc456a500 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -222,27 +222,21 @@ final class CommunicationsGraph extends mxGraph { private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); - final String name =// accountDeviceInstance.getDeviceId() + ":" + + final String name = accountDeviceInstance.getAccount().getTypeSpecificID(); - final mxCell vertex = nodeMap.computeIfAbsent(name, vertexName -> { + final mxCell vertex = nodeMap.computeIfAbsent(name+accountDeviceInstance.getDeviceId(), vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; mxCell newVertex = (mxCell) insertVertex( getDefaultParent(), - vertexName, accountDeviceInstanceKey, + name, accountDeviceInstanceKey, Math.random() * 400, Math.random() * 400, size, size); return newVertex; }); -// final mxCellState state = getView().getState(vertex, true); -// -// getView().updateLabel(state); -// getView().updateLabelBounds(state); -// getView().updateBoundingBox(state); - return vertex; } @@ -310,7 +304,7 @@ final class CommunicationsGraph extends mxGraph { Set accountIDs = relatedAccounts.keySet(); - Map relationshipCounts = commsManager.getRelationshipCounts(accountIDs, currentFilter); + Map relationshipCounts = commsManager.getRelationshipCountsBetween(accountIDs, currentFilter); int total = relationshipCounts.size(); int k = 0; diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 889b81cf74..54ca6e425f 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -728,7 +728,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxICell target = (mxICell) graph.getModel().getTerminal(cell, false); AccountDeviceInstanceKey account2 = (AccountDeviceInstanceKey) target.getValue(); try { - final List relationshipSources1 = commsManager.getRelationshipSources(account1.getAccountDeviceInstance(), + final List relationshipSources1 = commsManager.getRelationshipSources( + account1.getAccountDeviceInstance(), account2.getAccountDeviceInstance(), currentFilter); relationshipSources.addAll(relationshipSources1); From 4239c92f9e8f52de45e714cae3794887bcf295b6 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 26 Feb 2018 15:25:53 +0100 Subject: [PATCH 056/128] cleanup and add info to tool tips --- .../communications/CommunicationsGraph.java | 42 +++++-------------- .../communications/Vertex_Label_template.html | 2 +- 2 files changed, 11 insertions(+), 33 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index 4cc456a500..1a64f10c47 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -193,19 +193,22 @@ final class CommunicationsGraph extends mxGraph { final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value; scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); - scopes.put("size", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5)); + scopes.put("relationships", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5)); scopes.put("iconFileName", CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); scopes.put("pinned", pinnedAccountModel.isAccountPinned(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); scopes.put("locked", lockedVertexModel.isVertexLocked((mxCell) cell)); scopes.put("LOCK_URL", LOCK_URL); + scopes.put("device_id", adiKey.getAccountDeviceInstance().getDeviceId()); labelMustache.execute(stringWriter, scopes); return stringWriter.toString(); } else { - return ((mxICell) cell).getId(); + final mxICell edge = (mxICell) cell; + final long count = (long) edge.getValue(); + return""+ edge.getId() + "
" + count + (count == 1 ? " relationship" : " relationships")+""; } } @@ -222,10 +225,9 @@ final class CommunicationsGraph extends mxGraph { private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); - final String name = - accountDeviceInstance.getAccount().getTypeSpecificID(); + final String name = accountDeviceInstance.getAccount().getTypeSpecificID(); - final mxCell vertex = nodeMap.computeIfAbsent(name+accountDeviceInstance.getDeviceId(), vertexName -> { + final mxCell vertex = nodeMap.computeIfAbsent(name + accountDeviceInstance.getDeviceId(), vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; mxCell newVertex = (mxCell) insertVertex( @@ -247,12 +249,11 @@ final class CommunicationsGraph extends mxGraph { Object[] edgesBetween = getEdgesBetween(vertex1, vertex2); mxCell edge; if (edgesBetween.length == 0) { - final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); + final String edgeName = vertex1.getId() + " - " + vertex2.getId(); edge = (mxCell) insertEdge(getDefaultParent(), edgeName, relSources, vertex1, vertex2, "strokeWidth=" + (Math.log(relSources) + 1)); } else { edge = (mxCell) edgesBetween[0]; -// ((Collection) edge.getValue()).addAll(relSources); edge.setStyle("strokeWidth=" + (Math.log(relSources) + 1)); } return edge; @@ -308,38 +309,15 @@ final class CommunicationsGraph extends mxGraph { int total = relationshipCounts.size(); int k = 0; - progress.switchToDeterminate("", 0,total); + progress.switchToDeterminate("", 0, total); for (Map.Entry entry : relationshipCounts.entrySet()) { Long count = entry.getValue(); Relationship.RelationshipKey relationshipKey = entry.getKey(); AccountDeviceInstanceKey account1 = relatedAccounts.get(relationshipKey.getAccount1ID()); AccountDeviceInstanceKey account2 = relatedAccounts.get(relationshipKey.getAccount2ID()); mxCell addEdge = addOrUpdateEdge(count, account1, account2); - progress.progress(addEdge.getId(),k++); + progress.progress(addEdge.getId(), k++); } -// //for each pair of related accounts add edges if they are related o each other. -// // this is O(n^2) in the number of related accounts!!! -// List relatedAccountsList = new ArrayList<>(relatedAccounts); -// progress.switchToDeterminate("", 0, relatedAccountsList.size()); -// -// for (i = 0; i < relatedAccountsList.size(); i++) { -// AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); -// for (int j = i; j < relatedAccountsList.size(); j++) { -// if (isCancelled()) { -// break; -// } -// AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); -// List relationships = commsManager.getRelationshipSources( -// adiKey1.getAccountDeviceInstance(), -// adiKey2.getAccountDeviceInstance(), -// currentFilter); -// if (relationships.size() > 0) { -// mxCell addEdge = addEdge(relationships, adiKey1, adiKey2); -// progress.progress(addEdge.getId()); -// } -// } -// progress.progress(i); -// } } catch (TskCoreException tskCoreException) { logger.log(Level.SEVERE, "Error", tskCoreException); } finally { diff --git a/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html b/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html index 4e489c992a..4b6d3db580 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html +++ b/Core/src/org/sleuthkit/autopsy/communications/Vertex_Label_template.html @@ -1,3 +1,3 @@ -
{{#pinned}}{{/pinned}}{{#locked}}{{/locked}}{{accountName}}
+
{{#pinned}}{{/pinned}}{{#locked}}{{/locked}}{{accountName}} {{#device_id}}
data source: {{device_id}}{{/device_id}}
\ No newline at end of file From 291c33dc63c88207ef5739f76695acd1e4b49c36 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 26 Feb 2018 18:12:26 +0100 Subject: [PATCH 057/128] fix accout type icons in the DirectoryTree --- .../AccountDeviceInstanceNode.java | 2 +- .../communications/CommunicationsGraph.java | 8 +- .../autopsy/communications/FiltersPanel.form | 2 +- .../autopsy/communications/FiltersPanel.java | 5 +- .../autopsy/communications/Utils.java | 33 +----- .../autopsy/communications/images/calllog.png | Bin 691 -> 0 bytes .../communications/images/credit-card.png | Bin 536 -> 0 bytes .../autopsy/communications/images/image.png | Bin 1286 -> 0 bytes .../communications/images/web-file.png | Bin 928 -> 0 bytes .../autopsy/datamodel/accounts/Accounts.java | 112 +++++++++++------- .../{communications => }/images/WhatsApp.png | Bin .../{communications => }/images/email.png | Bin .../{communications => }/images/facebook.png | Bin .../{communications => }/images/instagram.png | Bin .../{communications => }/images/messaging.png | Bin .../{communications => }/images/phone.png | Bin .../{communications => }/images/twitter.png | Bin 17 files changed, 83 insertions(+), 79 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/calllog.png delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/credit-card.png delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/image.png delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/web-file.png rename Core/src/org/sleuthkit/autopsy/{communications => }/images/WhatsApp.png (100%) rename Core/src/org/sleuthkit/autopsy/{communications => }/images/email.png (100%) rename Core/src/org/sleuthkit/autopsy/{communications => }/images/facebook.png (100%) rename Core/src/org/sleuthkit/autopsy/{communications => }/images/instagram.png (100%) rename Core/src/org/sleuthkit/autopsy/{communications => }/images/messaging.png (100%) rename Core/src/org/sleuthkit/autopsy/{communications => }/images/phone.png (100%) rename Core/src/org/sleuthkit/autopsy/{communications => }/images/twitter.png (100%) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index eec27f30e5..40cb15d27d 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -52,7 +52,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount(); setName(account.getTypeSpecificID()); setDisplayName(getName()); - setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType())); + setIconBaseWithExtension(Utils.getIconFilePath(account.getAccountType())); } AccountDeviceInstance getAccountDeviceInstance() { diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index 81ec5d7838..27bcded4e2 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -168,8 +168,7 @@ final class CommunicationsGraph extends mxGraph { scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); scopes.put("size", Math.round(Math.log(adiKey.getMessageCount()) + 5)); - scopes.put("iconFileName", CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/" - + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); + scopes.put("iconFileName", CommunicationsGraph.class.getResource( Utils.getIconFilePath(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); scopes.put("pinned", pinnedAccountModel.isAccountPinned(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); scopes.put("locked", lockedVertexModel.isVertexLocked((mxCell) cell)); @@ -194,8 +193,7 @@ final class CommunicationsGraph extends mxGraph { scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID()); scopes.put("size", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5)); - scopes.put("iconFileName", CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/" - + Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); + scopes.put("iconFileName", CommunicationsGraph.class.getResource( Utils.getIconFilePath(adiKey.getAccountDeviceInstance().getAccount().getAccountType()))); scopes.put("pinned", pinnedAccountModel.isAccountPinned(adiKey)); scopes.put("MARKER_PIN_URL", MARKER_PIN_URL); scopes.put("locked", lockedVertexModel.isVertexLocked((mxCell) cell)); @@ -310,7 +308,7 @@ final class CommunicationsGraph extends mxGraph { Set accountIDs = relatedAccounts.keySet(); - Map relationshipCounts = commsManager.getRelationshipCounts(accountIDs, currentFilter); + Map relationshipCounts = commsManager.getRelationshipCountsBetween(accountIDs, currentFilter); int total = relationshipCounts.size(); int k = 0; diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form index 25153de997..eca450180a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form @@ -244,7 +244,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 36f9ac4410..1832e305f2 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -201,8 +201,7 @@ final public class FiltersPanel extends JPanel { accountTypeMap.computeIfAbsent(type, t -> { final JCheckBox jCheckBox = new JCheckBox( "
" + type.getDisplayName() + "
", true ); @@ -327,7 +326,7 @@ final public class FiltersPanel extends JPanel { } }); - devicesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/image.png"))); // NOI18N + devicesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/image.png"))); // NOI18N devicesLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.devicesLabel.text")); // NOI18N checkAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.checkAllDevicesButton.text")); // NOI18N diff --git a/Core/src/org/sleuthkit/autopsy/communications/Utils.java b/Core/src/org/sleuthkit/autopsy/communications/Utils.java index 91ee1e9fdf..b155e3b4b4 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Utils.java +++ b/Core/src/org/sleuthkit/autopsy/communications/Utils.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.communications; import java.time.ZoneId; import java.time.ZoneOffset; import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.datamodel.accounts.Accounts; import org.sleuthkit.datamodel.Account; /** @@ -37,36 +38,12 @@ class Utils { } /** - * The file name of the icon for the given Account Type. Will not include - * the path but will include the extension. + * Get the path of the icon for the given Account Type. * - * @return The file name of the icon for the given Account Type. + * @return The path of the icon for the given Account Type. */ - static final String getIconFileName(Account.Type type) { - if (type.equals(Account.Type.CREDIT_CARD)) { - return "credit-card.png"; - } else if (type.equals(Account.Type.DEVICE)) { - return "image.png"; - } else if (type.equals(Account.Type.EMAIL)) { - return "email.png"; - } else if (type.equals(Account.Type.FACEBOOK)) { - return "facebook.png"; - } else if (type.equals(Account.Type.INSTAGRAM)) { - return "instagram.png"; - } else if (type.equals(Account.Type.MESSAGING_APP)) { - return "messaging.png"; - } else if (type.equals(Account.Type.PHONE)) { - return "phone.png"; - } else if (type.equals(Account.Type.TWITTER)) { - return "twitter.png"; - } else if (type.equals(Account.Type.WEBSITE)) { - return "web-file.png"; - } else if (type.equals(Account.Type.WHATSAPP)) { - return "WhatsApp.png"; - } else { - //there could be a default icon instead... - throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName()); - } + static final String getIconFilePath(Account.Type type) { + return Accounts.getIconFilePath(type); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/calllog.png b/Core/src/org/sleuthkit/autopsy/communications/images/calllog.png deleted file mode 100644 index 83eb9c448d592e1cc125710cd5d02e9520543457..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 691 zcmV;k0!;mhP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf0!T?jK~y+TjgoCh zQ&AYl|9880cXtjqbbg5Z;Dkjb=7)k&R75l=C>PWh2lh6~K#EcdBFLZyWe~LwjrvrO z4#om2h_a|82}*QEnvNu#m=5!8tG(QLx7)dQw{5*F{BiF&-2XYx|D5|g4183s#&04;6A1%ExNOnSH)hvdz_xZXL8`KW-)$Lq2; z*u3@#qKPn4LJV`!2{?D$1E;6qKGX-db5Rb8#d$Jj(DmvI+BzcW9r`Zoxhgcd%b?S7 zs5EXs>;8xGVj-cgbJ%@u7z+svHm#=MyJVI1TosykcHvuO3L{h9NW@9a{$M#eatwSriSzes74>%x+l*+on=v>VRuqEt3x%AyT`M+RgXrqmt|)ng z)Vvr7`jxLN|Lnw5x6BB~$5CmppmpyvtT36;`_YTLua82*s9~*Y!ogkb^4_AYeA;8d zOni)7QGUQ_RM2ZHK&x5A_&Mxbe-k#Vv$%moyVs6^$wx5gbf8GOD2jqw#gJnuqi zli~jxNc04}@H};ZP5QYc-1MhL{N;xsM|AwM7lLDIN%<;ozE6b6F}n?P(y_qDgX zli8CH$OIL3;f1-Kxw$uQ=Ix#!BHU$3+;J(jd$G;?)-A9C)*dK!HrHP~SfSF@B=;lv zp#s5`B|;_GWaayJ0h*XE5Jf zrE$K1RnWy`=2>ljGB05=$E`b*r9~|wP(EZ4i+s+j?FE}P^YaElauATeJQ7ELERgn< zBAv(nsNOCC-AmwU!!khQaggG7B5^oq;t7M7->)G$ImEM%8(UU27CcUEuZC4#;&X6tS|-jrNc-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%uvD1M9IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr4|esh**NZ(?$0 z9!LbN!`Ii!Gq1QLF)umQ)5TT^Xog;9W{Q=oo0FxHp|iP}o1>|rp{t>Vo2#X(rHh-R zqqBjFi;)vduSMv>2~2MaLazl*y`aR9TL84#CABECEH%ZgC_h&L>}jh^+-@<& zX&zK>3U0TU;nb@Sbc{YIYLTKECIn1BASOKF0y*%cpPC0u??u3b9iF@#n8O$=JY5_^ zDsCnH`TyUZS+!we7Tb&&GYptl<~y8mYq4uIPCr+}Vi+-j<>`^5syCjUo!uXvUR9-a zc(S^`N`ns%^A%RsR*7kPv3(0q-Z;T>?%9I}Oo5jiYJ~4Hh&nntvc<~U@-Wx^{l%L8 zxuCdNgHuBODl3x{kA3~WmLKkMY)2=!^UrU{^wZOu!=~}+!-qq;xA#`_cQJNxc?mFE ze2_T6TOxOYAyw3sEz&ZxZFl**W>tq80fvt^ZycPc(v`$u!WAlKTXllrkb9pTlXp>Z z){gdBN^78)|e))gy@^*jXeL_Wb?1WeKw+2l5{r9i+;lsJNCI0QJ zJ^eu}zopr08J6R{Qv*} diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/web-file.png b/Core/src/org/sleuthkit/autopsy/communications/images/web-file.png deleted file mode 100644 index ac5957ad62d73408cd754a27453b4ce601a2b042..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 928 zcmV;R17G}!P)Mh53JODVWnpw>WFU8GbZ8({Xk{Qr zNlj3Y*^6%g00QhuL_t(I%dL}LXj^3*hoASH_nc#sG-;dWqm6CW(4s83b+X9Bt0|~3 z6mOI%-9=?M5O0K`ptn*G#rs`|6-PnD>5WW=H?uIcvzCo^E3WHYKeL=HA5C(SbCR6b z3)#f&*5~^FdoKR`eu4)${@&~<;4NLs{R%AQ`wgX7&~)wW+|1M$58jLW!S}zMGrL#P4P+<|J^kR=q!LjUR<=1mzj7BLp1miL0MTgZr{|x^iYGDyl!|!#OL6OV`f;PXlOge)!c#$#{SGXl@)s^XbSCXaYk@OhXc|B#CG* zL-81~z96~mqojuij=b@~*=YbR90{B}oE@c7E^@~W5Fg%$Qs65I} diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java index b3e8a9a1b1..203ca38a70 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -54,7 +54,6 @@ import org.openide.nodes.Node; import org.openide.nodes.NodeNotFoundException; import org.openide.nodes.NodeOp; import org.openide.nodes.Sheet; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.openide.util.lookup.Lookups; @@ -73,7 +72,6 @@ import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Account; -import org.sleuthkit.datamodel.AccountFileInstance; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -89,6 +87,7 @@ import org.sleuthkit.datamodel.TskData.DbType; final public class Accounts implements AutopsyVisitableItem { private static final Logger LOGGER = Logger.getLogger(Accounts.class.getName()); + private static final String iconBasePath = "/org/sleuthkit/autopsy/images/"; //NON-NLS @NbBundle.Messages("AccountsRootNode.name=Accounts") final public static String NAME = Bundle.AccountsRootNode_name(); @@ -126,7 +125,7 @@ final public class Accounts implements AutopsyVisitableItem { * results from db queries. * * @return A clause that will or will not filter out rejected artifacts - * based on the state of showRejected. + * based on the state of showRejected. */ private String getRejectedArtifactFilterClause() { return showRejected ? " " : " AND blackboard_artifacts.review_status_id != " + BlackboardArtifact.ReviewStatus.REJECTED.getID() + " "; //NON-NLS @@ -137,7 +136,7 @@ final public class Accounts implements AutopsyVisitableItem { * or off. * * @return An Action that will toggle whether rejected artifacts are shown - * in the tree rooted by this Accounts instance. + * in the tree rooted by this Accounts instance. */ public Action newToggleShowRejectedAction() { return new ToggleShowRejected(); @@ -225,8 +224,8 @@ final public class Accounts implements AutopsyVisitableItem { private class AccountTypeFactory extends ObservingChildren { /* - * The pcl is in this class because it has the easiest mechanisms to - * add and remove itself during its life cycles. + * The pcl is in this class because it has the easiest mechanisms to add + * and remove itself during its life cycles. */ private final PropertyChangeListener pcl = new PropertyChangeListener() { @Override @@ -310,23 +309,21 @@ final public class Accounts implements AutopsyVisitableItem { } @Override - protected Node[] createNodesForKey(String key) { + protected Node[] createNodesForKey(String acountTypeName) { - String accountType = key; - if (accountType.equals(Account.Type.CREDIT_CARD.getTypeName())) { + if (Account.Type.CREDIT_CARD.getTypeName().equals(acountTypeName)) { return new Node[]{new CreditCardNumberAccountTypeNode()}; } else { - String accountTypeDisplayname; + try { - accountTypeDisplayname = skCase.getCommunicationsManager().getAccountType(accountType).getDisplayName(); + Account.Type accountType = skCase.getCommunicationsManager().getAccountType(acountTypeName); + return new Node[]{new DefaultAccountTypeNode(accountType)}; } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error getting display name for account type. ", ex); - accountTypeDisplayname = accountType; } - return new Node[]{new DefaultAccountTypeNode(key, accountTypeDisplayname)}; + return new Node[]{}; } - } @Override @@ -350,10 +347,10 @@ final public class Accounts implements AutopsyVisitableItem { final private class DefaultAccountFactory extends ObservingChildren { - private final String accountTypeName; + private final Account.Type accountType; - private DefaultAccountFactory(String accountTypeName) { - this.accountTypeName = accountTypeName; + private DefaultAccountFactory(Account.Type accountType) { + this.accountType = accountType; } private final PropertyChangeListener pcl = new PropertyChangeListener() { @@ -424,13 +421,13 @@ final public class Accounts implements AutopsyVisitableItem { @Override protected boolean createKeys(List list) { - String query - = "SELECT blackboard_artifacts.artifact_id " //NON-NLS + String query = + "SELECT blackboard_artifacts.artifact_id " //NON-NLS + " FROM blackboard_artifacts " //NON-NLS + " JOIN blackboard_attributes ON blackboard_artifacts.artifact_id = blackboard_attributes.artifact_id " //NON-NLS + " WHERE blackboard_artifacts.artifact_type_id = " + BlackboardArtifact.ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID() //NON-NLS + " AND blackboard_attributes.attribute_type_id = " + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ACCOUNT_TYPE.getTypeID() //NON-NLS - + " AND blackboard_attributes.value_text = '" + accountTypeName + "'" //NON-NLS + + " AND blackboard_attributes.value_text = '" + accountType.getTypeName() + "'" //NON-NLS + getRejectedArtifactFilterClause(); //NON-NLS try (SleuthkitCase.CaseDbQuery results = skCase.executeQuery(query); ResultSet rs = results.getResultSet();) { @@ -473,10 +470,10 @@ final public class Accounts implements AutopsyVisitableItem { */ final public class DefaultAccountTypeNode extends DisplayableItemNode { - private DefaultAccountTypeNode(String accountTypeName, String accountTypeDisplayName) { - super(Children.create(new DefaultAccountFactory(accountTypeName), true), Lookups.singleton(accountTypeDisplayName)); - setName(accountTypeDisplayName); - this.setIconBaseWithExtension("org/sleuthkit/autopsy/images/credit-cards.png"); //NON-NLS + private DefaultAccountTypeNode(Account.Type accountType) { + super(Children.create(new DefaultAccountFactory(accountType), true), Lookups.singleton(accountType)); + setName(accountType.getDisplayName()); + this.setIconBaseWithExtension(getIconFilePath(accountType)); //NON-NLS } @Override @@ -1161,13 +1158,13 @@ final public class Accounts implements AutopsyVisitableItem { * take the result of a group_concat SQLite operation and split it into a * set of X using the mapper to to convert from string to X * - * @param the type of elements to return + * @param the type of elements to return * @param groupConcat a string containing the group_concat result ( a comma - * separated list) - * @param mapper a function from String to X + * separated list) + * @param mapper a function from String to X * * @return a Set of X, each element mapped from one element of the original - * comma delimited string + * comma delimited string */ static List unGroupConcat(String groupConcat, Function mapper) { return StringUtils.isBlank(groupConcat) ? Collections.emptyList() @@ -1187,8 +1184,8 @@ final public class Accounts implements AutopsyVisitableItem { /** * Constructor * - * @param key The FileWithCCN that backs this node. - * @param content The Content object the key represents. + * @param key The FileWithCCN that backs this node. + * @param content The Content object the key represents. * @param lookupContents The contents of this Node's lookup. It should * contain the content object and the account artifacts. */ @@ -1465,11 +1462,11 @@ final public class Accounts implements AutopsyVisitableItem { } return sheet; } - + private void updateSheet() { this.setSheet(createSheet()); } - + } /** @@ -1601,7 +1598,7 @@ final public class Accounts implements AutopsyVisitableItem { super(artifact, "org/sleuthkit/autopsy/images/credit-card.png"); //NON-NLS this.artifact = artifact; setName("" + this.artifact.getArtifactID()); - + reviewStatusBus.register(this); } @@ -1628,13 +1625,13 @@ final public class Accounts implements AutopsyVisitableItem { Bundle.Accounts_FileWithCCNNode_statusProperty_displayName(), Bundle.Accounts_FileWithCCNNode_noDescription(), artifact.getReviewStatus().getDisplayName())); - + return sheet; } - + @Subscribe void handleReviewStatusChange(ReviewStatusChangeEvent event) { - + // Update the node if event includes this artifact event.artifacts.stream().filter((art) -> (art.getArtifactID() == this.artifact.getArtifactID())).map((_item) -> { return _item; @@ -1642,11 +1639,11 @@ final public class Accounts implements AutopsyVisitableItem { updateSheet(); }); } - + private void updateSheet() { this.setSheet(createSheet()); } - + } private final class ToggleShowRejected extends AbstractAction { @@ -1755,14 +1752,47 @@ final public class Accounts implements AutopsyVisitableItem { } } - private class ReviewStatusChangeEvent { + static private class ReviewStatusChangeEvent { Collection artifacts; BlackboardArtifact.ReviewStatus newReviewStatus; - public ReviewStatusChangeEvent(Collection artifacts, BlackboardArtifact.ReviewStatus newReviewStatus) { + ReviewStatusChangeEvent(Collection artifacts, BlackboardArtifact.ReviewStatus newReviewStatus) { this.artifacts = artifacts; this.newReviewStatus = newReviewStatus; } } + + /** + * Get the path of the icon for the given Account Type. + * + * @return The path of the icon for the given Account Type. + */ + public static String getIconFilePath(Account.Type type) { + + if (type.equals(Account.Type.CREDIT_CARD)) { + return iconBasePath + "credit-card.png"; + } else if (type.equals(Account.Type.DEVICE)) { + return iconBasePath + "image.png"; + } else if (type.equals(Account.Type.EMAIL)) { + return iconBasePath + "email.png"; + } else if (type.equals(Account.Type.FACEBOOK)) { + return iconBasePath + "facebook.png"; + } else if (type.equals(Account.Type.INSTAGRAM)) { + return iconBasePath + "instagram.png"; + } else if (type.equals(Account.Type.MESSAGING_APP)) { + return iconBasePath + "messaging.png"; + } else if (type.equals(Account.Type.PHONE)) { + return iconBasePath + "phone.png"; + } else if (type.equals(Account.Type.TWITTER)) { + return iconBasePath + "twitter.png"; + } else if (type.equals(Account.Type.WEBSITE)) { + return iconBasePath + "web-file.png"; + } else if (type.equals(Account.Type.WHATSAPP)) { + return iconBasePath + "WhatsApp.png"; + } else { + //there could be a default icon instead... + throw new IllegalArgumentException("Unknown Account.Type: " + type.getTypeName()); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/WhatsApp.png b/Core/src/org/sleuthkit/autopsy/images/WhatsApp.png similarity index 100% rename from Core/src/org/sleuthkit/autopsy/communications/images/WhatsApp.png rename to Core/src/org/sleuthkit/autopsy/images/WhatsApp.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/email.png b/Core/src/org/sleuthkit/autopsy/images/email.png similarity index 100% rename from Core/src/org/sleuthkit/autopsy/communications/images/email.png rename to Core/src/org/sleuthkit/autopsy/images/email.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/facebook.png b/Core/src/org/sleuthkit/autopsy/images/facebook.png similarity index 100% rename from Core/src/org/sleuthkit/autopsy/communications/images/facebook.png rename to Core/src/org/sleuthkit/autopsy/images/facebook.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/instagram.png b/Core/src/org/sleuthkit/autopsy/images/instagram.png similarity index 100% rename from Core/src/org/sleuthkit/autopsy/communications/images/instagram.png rename to Core/src/org/sleuthkit/autopsy/images/instagram.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/messaging.png b/Core/src/org/sleuthkit/autopsy/images/messaging.png similarity index 100% rename from Core/src/org/sleuthkit/autopsy/communications/images/messaging.png rename to Core/src/org/sleuthkit/autopsy/images/messaging.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/phone.png b/Core/src/org/sleuthkit/autopsy/images/phone.png similarity index 100% rename from Core/src/org/sleuthkit/autopsy/communications/images/phone.png rename to Core/src/org/sleuthkit/autopsy/images/phone.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/twitter.png b/Core/src/org/sleuthkit/autopsy/images/twitter.png similarity index 100% rename from Core/src/org/sleuthkit/autopsy/communications/images/twitter.png rename to Core/src/org/sleuthkit/autopsy/images/twitter.png From 9d4f697754338be29c0d9ff0d26eb3be4f2f415b Mon Sep 17 00:00:00 2001 From: Jonathan Millman Date: Tue, 27 Feb 2018 10:43:05 +0100 Subject: [PATCH 058/128] use correct method name --- .../sleuthkit/autopsy/communications/CommunicationsGraph.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index 81ec5d7838..3d51f3c292 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -310,7 +310,7 @@ final class CommunicationsGraph extends mxGraph { Set accountIDs = relatedAccounts.keySet(); - Map relationshipCounts = commsManager.getRelationshipCounts(accountIDs, currentFilter); + Map relationshipCounts = commsManager.getRelationshipCountsBetween(accountIDs, currentFilter); int total = relationshipCounts.size(); int k = 0; From 3f5619933d6dd73f4fe560a9c724e43e195bd1c7 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 27 Feb 2018 15:24:44 +0100 Subject: [PATCH 059/128] reset state of data source filters when case is opened. --- .../communications/CVTTopComponent.java | 2 +- .../communications/CommunicationsGraph.java | 2 +- .../autopsy/communications/FiltersPanel.java | 68 +++++++++++++------ 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 11ae8809e8..539459fdea 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -142,7 +142,7 @@ public final class CVTTopComponent extends TopComponent { * * Re-applying the filters means we will lose the selection... */ - filtersPane.updateAndApplyFilters(); + filtersPane.updateAndApplyFilters(true); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index 81ec5d7838..3d51f3c292 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -310,7 +310,7 @@ final class CommunicationsGraph extends mxGraph { Set accountIDs = relatedAccounts.keySet(); - Map relationshipCounts = commsManager.getRelationshipCounts(accountIDs, currentFilter); + Map relationshipCounts = commsManager.getRelationshipCountsBetween(accountIDs, currentFilter); int total = relationshipCounts.size(); int k = 0; diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 36f9ac4410..f9031af04a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -61,8 +61,15 @@ final public class FiltersPanel extends JPanel { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName()); + /** + * Map from Account.Type to the checkbox for that account type's filter. + */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private final Map accountTypeMap = new HashMap<>(); + + /** + * Map from datasource device id to the checkbox for that datasource. + */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private final Map devicesMap = new HashMap<>(); @@ -70,6 +77,11 @@ final public class FiltersPanel extends JPanel { * Listens to ingest events to enable refresh button */ private final PropertyChangeListener ingestListener; + + /** + * Flag that indicates the UI is not up-sto-date with respect to the case DB + * and it should be refreshed (by reapplying the filters). + */ private boolean needsRefresh; /** @@ -78,10 +90,16 @@ final public class FiltersPanel extends JPanel { * results) */ private final ItemListener validationListener; - private boolean deviceAccountTypeEnabled = false; - @NbBundle.Messages({"refreshText=Refresh Results", - "applyText=Apply"}) + /** + * Is the device account type filter enabled or not. It should be enabled + * when the Table/Brows mode is active and disabled when the visualization + * is active. Initially false since the browse/table mode is active + * initially. + */ + private boolean deviceAccountTypeEnabled; + + @NbBundle.Messages({"refreshText=Refresh Results", "applyText=Apply"}) public FiltersPanel() { initComponents(); deviceRequiredLabel.setVisible(false); @@ -102,8 +120,7 @@ final public class FiltersPanel extends JPanel { updateTimeZone(); validationListener = itemEvent -> validateFilters(); - updateFilters(); - setAllDevicesSelected(true); + updateFilters(true); UserPreferences.addChangeListener(preferenceChangeEvent -> { if (preferenceChangeEvent.getKey().equals(UserPreferences.DISPLAY_TIMES_IN_LOCAL_TIME)) { updateTimeZone(); @@ -118,7 +135,7 @@ final public class FiltersPanel extends JPanel { if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() && eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { - updateFilters(); + updateFilters(false); needsRefresh = true; validateFilters(); } @@ -150,8 +167,8 @@ final public class FiltersPanel extends JPanel { /** * Update the filter widgets, and apply them. */ - void updateAndApplyFilters() { - updateFilters(); + void updateAndApplyFilters(boolean initialState) { + updateFilters(initialState); applyFilters(); } @@ -162,9 +179,9 @@ final public class FiltersPanel extends JPanel { /** * Updates the filter widgets to reflect he data sources/types in the case. */ - private void updateFilters() { + private void updateFilters(boolean initialState) { updateAccountTypeFilter(); - updateDeviceFilter(); + updateDeviceFilter(initialState); } @Override @@ -221,7 +238,7 @@ final public class FiltersPanel extends JPanel { /** * Populate the devices filter widgets */ - private void updateDeviceFilter() { + private void updateDeviceFilter(boolean initialState) { try { final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); @@ -229,13 +246,12 @@ final public class FiltersPanel extends JPanel { String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName(); //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); + final JCheckBox jCheckBox = new JCheckBox(dsName, initialState); jCheckBox.addItemListener(validationListener); devicesPane.add(jCheckBox); return jCheckBox; }); - }; - + } } catch (IllegalStateException ex) { logger.log(Level.WARNING, "Communications Visualization Tool opened with no open case.", ex); } catch (TskCoreException tskCoreException) { @@ -538,6 +554,21 @@ final public class FiltersPanel extends JPanel { return new DateRangeFilter(start, end); } + /** + * Enable or disable the device account type filter. The filter should be + * disabled for the browse/table mode and enabled for the visualization. + * + * @param enable True to enable the device account type filter, False to + * disable it. + */ + void setDeviceAccountTypeEnabled(boolean enable) { + deviceAccountTypeEnabled = enable; + JCheckBox deviceCheckbox = accountTypeMap.get(Account.Type.DEVICE); + if (deviceCheckbox != null) { + deviceCheckbox.setEnabled(deviceAccountTypeEnabled); + } + } + /** * Set the selection state of all the account type check boxes * @@ -569,6 +600,7 @@ final public class FiltersPanel extends JPanel { private void setAllSelected(Map map, boolean selected) { map.values().forEach(box -> box.setSelected(selected)); } + private void unCheckAllAccountTypesButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_unCheckAllAccountTypesButtonActionPerformed setAllAccountTypesSelected(false); }//GEN-LAST:event_unCheckAllAccountTypesButtonActionPerformed @@ -620,12 +652,4 @@ final public class FiltersPanel extends JPanel { private final javax.swing.JButton unCheckAllAccountTypesButton = new javax.swing.JButton(); private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton(); // End of variables declaration//GEN-END:variables - - void setDeviceAccountTypeEnabled(boolean enableDeviceAccountType) { - deviceAccountTypeEnabled = enableDeviceAccountType; - JCheckBox deviceCheckbox = accountTypeMap.get(Account.Type.DEVICE); - if (deviceCheckbox != null) { - deviceCheckbox.setEnabled(deviceAccountTypeEnabled); - } - } } From 0f4e58569be87a3f1bf963b6b0f6c9b04641b84e Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 27 Feb 2018 15:36:27 +0100 Subject: [PATCH 060/128] resolve codacy issues --- .../communications/CVTTopComponent.form | 4 +- .../communications/CVTTopComponent.java | 68 +++++++++++-------- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form index 75898c859e..055719c488 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form @@ -4,7 +4,7 @@ - + @@ -92,4 +92,4 @@
- \ No newline at end of file + diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 539459fdea..47a478ec2d 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -19,8 +19,14 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.Subscribe; +import java.awt.Dimension; +import java.awt.Font; import java.util.List; import java.util.stream.Collectors; +import javax.swing.GroupLayout; +import javax.swing.ImageIcon; +import javax.swing.JTabbedPane; +import javax.swing.LayoutStyle; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; @@ -83,48 +89,46 @@ public final class CVTTopComponent extends TopComponent { // //GEN-BEGIN:initComponents private void initComponents() { - browseVisualizeTabPane = new javax.swing.JTabbedPane(); - accountsBrowser = new org.sleuthkit.autopsy.communications.AccountsBrowser(); - vizPanel = new org.sleuthkit.autopsy.communications.VisualizationPanel(); - filtersPane = new org.sleuthkit.autopsy.communications.FiltersPanel(); + browseVisualizeTabPane = new JTabbedPane(); + accountsBrowser = new AccountsBrowser(); + vizPanel = new VisualizationPanel(); + filtersPane = new FiltersPanel(); - browseVisualizeTabPane.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N - browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N - browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle_1"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N + browseVisualizeTabPane.setFont(new Font("Tahoma", 0, 18)); // NOI18N + browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N + browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N - filtersPane.setMinimumSize(new java.awt.Dimension(256, 495)); + filtersPane.setMinimumSize(new Dimension(256, 495)); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + GroupLayout layout = new GroupLayout(this); this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(6, 6, 6) - .addComponent(filtersPane, javax.swing.GroupLayout.PREFERRED_SIZE, 265, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(browseVisualizeTabPane, javax.swing.GroupLayout.DEFAULT_SIZE, 786, Short.MAX_VALUE) + .addComponent(filtersPane, GroupLayout.PREFERRED_SIZE, 265, GroupLayout.PREFERRED_SIZE) + .addPreferredGap(LayoutStyle.ComponentPlacement.RELATED) + .addComponent(browseVisualizeTabPane, GroupLayout.PREFERRED_SIZE, 786, Short.MAX_VALUE) .addContainerGap()) ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(6, 6, 6) - .addComponent(filtersPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(filtersPane, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGap(5, 5, 5)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addComponent(browseVisualizeTabPane) .addContainerGap()) ); - browseVisualizeTabPane.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName")); // NOI18N + browseVisualizeTabPane.getAccessibleContext().setAccessibleName(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName")); // NOI18N }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private org.sleuthkit.autopsy.communications.AccountsBrowser accountsBrowser; - private javax.swing.JTabbedPane browseVisualizeTabPane; - private org.sleuthkit.autopsy.communications.FiltersPanel filtersPane; - private org.sleuthkit.autopsy.communications.VisualizationPanel vizPanel; + private AccountsBrowser accountsBrowser; + private JTabbedPane browseVisualizeTabPane; + private FiltersPanel filtersPane; + private VisualizationPanel vizPanel; // End of variables declaration//GEN-END:variables @Override @@ -156,14 +160,24 @@ public final class CVTTopComponent extends TopComponent { .collect(Collectors.toList()); } + /** + * Extension of ProxyLookup that exposes the ability to change the Lookups + * delegated to. + * + */ final private static class ProxyLookupImpl extends ProxyLookup { - ProxyLookupImpl(Lookup... l) { - super(l); + ProxyLookupImpl(Lookup... lookups) { + super(lookups); } - protected void changeLookups(Lookup... l) { - setLookups(l); + /** + * Set the Lookups delegated to by this lookup. + * + * @param lookups The new Lookups to delegate to. + */ + protected void changeLookups(Lookup... lookups) { + setLookups(lookups); } } } From 4d594948e86d843ed3e147f7aaf38a79438a9a0c Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Tue, 27 Feb 2018 17:42:17 -0500 Subject: [PATCH 061/128] Tidy up IngestJobSettings --- .../autopsy/ingest/IngestJobSettings.java | 369 +++++++++--------- 1 file changed, 177 insertions(+), 192 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java index ad93b012c3..5624f43183 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettings.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,12 +26,14 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.logging.Level; +import java.util.stream.Collectors; import org.openide.util.NbBundle; import org.openide.util.io.NbObjectInputStream; import org.openide.util.io.NbObjectOutputStream; @@ -43,113 +45,93 @@ import org.sleuthkit.autopsy.modules.interestingitems.FilesSet; import org.sleuthkit.autopsy.modules.interestingitems.FilesSetsManager; /** - * Encapsulates the ingest job settings for a particular execution context. - * Examples of execution contexts include the add data source wizard and the run - * ingest modules dialog. Different execution contexts may have different ingest - * job settings. + * The settings for an ingest job. */ public class IngestJobSettings { - private static final String ENABLED_MODULES_KEY = "Enabled_Ingest_Modules"; //NON-NLS - private static final String DISABLED_MODULES_KEY = "Disabled_Ingest_Modules"; //NON-NLS - private static final String LAST_FILE_INGEST_FILTER_KEY = "Last_File_Ingest_Filter"; //NON-NLS + private static final String ENABLED_MODULES_PROPERTY = "Enabled_Ingest_Modules"; //NON-NLS + private static final String DISABLED_MODULES_PROPERTY = "Disabled_Ingest_Modules"; //NON-NLS + private static final String LAST_FILE_INGEST_FILTER_PROPERTY = "Last_File_Ingest_Filter"; //NON-NLS private static final String MODULE_SETTINGS_FOLDER = "IngestModuleSettings"; //NON-NLS private static final String MODULE_SETTINGS_FOLDER_PATH = Paths.get(PlatformUtil.getUserConfigDirectory(), IngestJobSettings.MODULE_SETTINGS_FOLDER).toAbsolutePath().toString(); private static final String MODULE_SETTINGS_FILE_EXT = ".settings"; //NON-NLS - private static final Logger LOGGER = Logger.getLogger(IngestJobSettings.class.getName()); - private FilesSet fileIngestFilter; - private String executionContext; + private static final CharSequence PYTHON_CLASS_PROXY_PREFIX = "org.python.proxies.".subSequence(0, "org.python.proxies.".length() - 1); //NON-NLS + private static final Logger logger = Logger.getLogger(IngestJobSettings.class.getName()); private final IngestType ingestType; - private String moduleSettingsFolderPath; - private static final CharSequence pythonModuleSettingsPrefixCS = "org.python.proxies.".subSequence(0, "org.python.proxies.".length() - 1); //NON-NLS private final List moduleTemplates; private final List warnings; + private String executionContext; + private FilesSet fileIngestFilter; + private String moduleSettingsFolderPath; /** - * Gets the last selected FileIngestFilter saved in settings which is - * represented by a FilesSet, if the last selected filter is null the - * default filter will be returned. + * Gets the path to the module settings folder for a given execution + * context. * - * @return FilesSet which represents the FileIngestFilter + * Some examples of execution contexts include the Add Data Source wizard, + * the Run Ingest Modules dialog, and auto ingest. Different execution + * contexts may have different ingest job settings. + * + * @return The execution context identifier. + * + * @return The path to the module settings folder */ - FilesSet getFileIngestFilter() { - if (fileIngestFilter == null) { - fileIngestFilter = FilesSetsManager.getDefaultFilter(); - } - return fileIngestFilter; + static Path getSavedModuleSettingsFolder(String executionContext) { + return Paths.get(IngestJobSettings.MODULE_SETTINGS_FOLDER_PATH, executionContext); } /** - * Sets the FileIngestFilter which is currently being used by ingest. + * Loads the ingest job settings for a given execution context. If settings + * for the context have not been previously saved, default settings are + * used. * - * @param fileIngestFilter the FilesSet which represents the - * FileIngestFilter + * Some examples of execution contexts include the Add Data Source wizard, + * the Run Ingest Modules dialog, and auto ingest. Different execution + * contexts may have different ingest job settings. + * + * @param executionContext The execution context identifier. */ - void setFileIngestFilter(FilesSet fileIngestFilter) { - this.fileIngestFilter = fileIngestFilter; + public IngestJobSettings(final String executionContext) { + this(executionContext, IngestType.ALL_MODULES); } /** - * The type of ingest modules to run. - */ - public enum IngestType { - - /** - * Run both data source level and file-level ingest modules. - */ - ALL_MODULES, - /** - * Run only data source level ingest modules. - */ - DATA_SOURCE_ONLY, - /** - * Run only file level ingest modules. - */ - FILES_ONLY - } - - /** - * Constructs an ingest job settings object for a given execution context. - * Examples of execution contexts include the add data source wizard and the - * run ingest modules dialog. Different execution contexts may have - * different ingest job settings. + * Loads the ingest job settings for a given execution context. If settings + * for the context have not been previously saved, default settings are + * used. * - * @param executionContext The ingest execution context identifier. - */ - public IngestJobSettings(String executionContext) { - this.executionContext = executionContext; - this.ingestType = IngestType.ALL_MODULES; - this.moduleTemplates = new ArrayList<>(); - this.warnings = new ArrayList<>(); - this.createSavedModuleSettingsFolder(); - this.load(); - } - - /** - * Constructs an ingest job settings object for a given context. Examples of - * execution contexts include the add data source wizard and the run ingest - * modules dialog. Different execution contexts may have different ingest - * job settings. + * Some examples of execution contexts include the Add Data Source wizard, + * the Run Ingest Modules dialog, and auto ingest. Different execution + * contexts may have different ingest job settings. * - * @param context The context identifier string. - * @param ingestType The type of modules ingest is running. + * @param executionContext The execution context identifier. + * @param ingestType Whether to run all ingest modules, data source + * level ingest modules only, or file level ingest + * modules only. */ - public IngestJobSettings(String context, IngestType ingestType) { + public IngestJobSettings(String executionContext, IngestType ingestType) { this.ingestType = ingestType; - - if (this.ingestType.equals(IngestType.ALL_MODULES)) { - this.executionContext = context; - } else { - this.executionContext = context + "." + this.ingestType.name(); - } - this.moduleTemplates = new ArrayList<>(); - this.warnings = new ArrayList<>(); + if (this.ingestType.equals(IngestType.ALL_MODULES)) { + this.executionContext = executionContext; + } else { + this.executionContext = executionContext + "." + this.ingestType.name(); + } this.createSavedModuleSettingsFolder(); this.load(); } + /** + * Gets the path to the module settings folder for these ingest job + * settings. + * + * @return The path to the ingest module settings folder. + */ + public Path getSavedModuleSettingsFolder() { + return Paths.get(IngestJobSettings.MODULE_SETTINGS_FOLDER_PATH, executionContext); + } + /** * Saves these ingest job settings. */ @@ -158,11 +140,13 @@ public class IngestJobSettings { } /** - * Saves the settings with a new context name removing the old profile - * folder + * Saves these ingest job settings for use in a different execution context. * - * @param executionContext will be used to name the new folder for storing - * the settings + * Some examples of execution contexts include the Add Data Source wizard, + * the Run Ingest Modules dialog, and auto ingest. Different execution + * contexts may have different ingest job settings. + * + * @param executionContext The new execution context. */ void saveAs(String executionContext) { this.executionContext = executionContext; @@ -171,20 +155,8 @@ public class IngestJobSettings { } /** - * Gets the ingest execution context identifier. Examples of execution - * contexts include the add data source wizard and the run ingest modules - * dialog. Different execution contexts may have different ingest job - * settings. - * - * @return The execution context identifier. - */ - String getExecutionContext() { - return this.executionContext; - } - - /** - * Gets and clears any accumulated warnings associated with these ingest job - * settings. + * Gets and clears any accumulated warnings associated with the loading or + * saving of these ingest job settings. * * @return A list of warning messages, possibly empty. */ @@ -195,7 +167,41 @@ public class IngestJobSettings { } /** - * Gets the ingest module templates part of these ingest job settings. + * Gets the execution context identifier. + * + * Some examples of execution contexts include the Add Data Source wizard, + * the Run Ingest Modules dialog, and auto ingest. Different execution + * contexts may have different ingest job settings. + * + * @return The execution context identifier. + */ + String getExecutionContext() { + return this.executionContext; + } + + /** + * Gets the file ingest filter for the ingest job. + * + * @return FilesSet The filter as a files set. + */ + FilesSet getFileIngestFilter() { + if (fileIngestFilter == null) { + fileIngestFilter = FilesSetsManager.getDefaultFilter(); + } + return fileIngestFilter; + } + + /** + * Sets the file ingest filter for the ingest job. + * + * @param fileIngestFilter The filter as a files set. + */ + void setFileIngestFilter(FilesSet fileIngestFilter) { + this.fileIngestFilter = fileIngestFilter; + } + + /** + * Gets the enabled ingest module templates for the ingest job. * * @return The list of ingest module templates. */ @@ -204,8 +210,17 @@ public class IngestJobSettings { } /** - * Gets the enabled ingest module templates part of these ingest job - * settings. + * Sets the enabled ingest module templates for the ingest job. + * + * @param moduleTemplates The ingest module templates. + */ + void setIngestModuleTemplates(List moduleTemplates) { + this.moduleTemplates.clear(); + this.moduleTemplates.addAll(moduleTemplates); + } + + /** + * Gets the enabled ingest module templates for this ingest job. * * @return The list of enabled ingest module templates. */ @@ -220,29 +235,12 @@ public class IngestJobSettings { } /** - * Sets the ingest module templates part of these ingest job settings. - * - * @param moduleTemplates The ingest module templates. - */ - void setIngestModuleTemplates(List moduleTemplates) { - this.moduleTemplates.clear(); - this.moduleTemplates.addAll(moduleTemplates); - } - - /** - * Gets the process unallocated space flag part of these ingest job - * settings. + * Gets the process unallocated space flag for this ingest job. * * @return True or false. * */ boolean getProcessUnallocatedSpace() { - /* - * Used to be a simple flag but the processUnallocated checkbox was - * changed to a skip unallocated. This was due to the FileIngestFilters - * needing a default value which did not skip unallocated files. This - * method exists to maintain existing functionality. - */ boolean processUnallocated = true; if (!Objects.isNull(this.fileIngestFilter)) { processUnallocated = (this.fileIngestFilter.ingoresUnallocatedSpace() == false); @@ -251,29 +249,7 @@ public class IngestJobSettings { } /** - * Returns the path to the ingest module settings folder. - * - * @return path to the module settings folder - */ - public Path getSavedModuleSettingsFolder() { - return Paths.get(IngestJobSettings.MODULE_SETTINGS_FOLDER_PATH, executionContext); - } - - /** - * Returns the path to the ingest module settings folder from a static - * manner. - * - * @param context specify the context of the folder you wish to get - * - * @return path to the module settings folder - */ - static Path getSavedModuleSettingsFolder(String context) { - return Paths.get(IngestJobSettings.MODULE_SETTINGS_FOLDER_PATH, context); - } - - /** - * Creates the folder for saving the individual ingest module settings part - * of these ingest job settings. + * Creates the module folder for these ingest job settings. */ private void createSavedModuleSettingsFolder() { try { @@ -281,13 +257,14 @@ public class IngestJobSettings { Files.createDirectories(folder); this.moduleSettingsFolderPath = folder.toAbsolutePath().toString(); } catch (IOException | SecurityException ex) { - LOGGER.log(Level.SEVERE, "Failed to create ingest module settings directory " + this.moduleSettingsFolderPath, ex); //NON-NLS + logger.log(Level.SEVERE, "Failed to create ingest module settings directory " + this.moduleSettingsFolderPath, ex); //NON-NLS this.warnings.add(NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.createModuleSettingsFolder.warning")); //NON-NLS } } /** - * Loads the saved or default ingest job settings context into memory. + * Loads the saved or default ingest job settings for the execution context + * into memory. */ private void load() { /** @@ -317,8 +294,8 @@ public class IngestJobSettings { * Get the enabled/disabled ingest modules settings for this context. By * default, all loaded modules are enabled. */ - HashSet enabledModuleNames = getModulesNamesFromSetting(executionContext, IngestJobSettings.ENABLED_MODULES_KEY, makeCommaSeparatedValuesList(loadedModuleNames)); - HashSet disabledModuleNames = getModulesNamesFromSetting(executionContext, IngestJobSettings.DISABLED_MODULES_KEY, ""); //NON-NLS + HashSet enabledModuleNames = getModulesNames(executionContext, IngestJobSettings.ENABLED_MODULES_PROPERTY, makeCsvList(loadedModuleNames)); + HashSet disabledModuleNames = getModulesNames(executionContext, IngestJobSettings.DISABLED_MODULES_PROPERTY, ""); //NON-NLS /** * Check for missing modules and create warnings if any are found. @@ -338,7 +315,7 @@ public class IngestJobSettings { enabledModuleNames.remove(moduleName); disabledModuleNames.remove(moduleName); String warning = NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.missingModule.warning", moduleName); //NON-NLS - LOGGER.log(Level.WARNING, warning); + logger.log(Level.WARNING, warning); this.warnings.add(warning); } @@ -367,14 +344,14 @@ public class IngestJobSettings { * Update the enabled/disabled ingest module settings for this context * to reflect any missing modules or newly discovered modules. */ - ModuleSettings.setConfigSetting(this.executionContext, IngestJobSettings.ENABLED_MODULES_KEY, makeCommaSeparatedValuesList(enabledModuleNames)); - ModuleSettings.setConfigSetting(this.executionContext, IngestJobSettings.DISABLED_MODULES_KEY, makeCommaSeparatedValuesList(disabledModuleNames)); + ModuleSettings.setConfigSetting(this.executionContext, IngestJobSettings.ENABLED_MODULES_PROPERTY, makeCsvList(enabledModuleNames)); + ModuleSettings.setConfigSetting(this.executionContext, IngestJobSettings.DISABLED_MODULES_PROPERTY, makeCsvList(disabledModuleNames)); /** * Restore the last used File Ingest Filter */ - if (ModuleSettings.settingExists(this.executionContext, IngestJobSettings.LAST_FILE_INGEST_FILTER_KEY) == false) { - ModuleSettings.setConfigSetting(this.executionContext, IngestJobSettings.LAST_FILE_INGEST_FILTER_KEY, FilesSetsManager.getDefaultFilter().getName()); + if (ModuleSettings.settingExists(this.executionContext, IngestJobSettings.LAST_FILE_INGEST_FILTER_PROPERTY) == false) { + ModuleSettings.setConfigSetting(this.executionContext, IngestJobSettings.LAST_FILE_INGEST_FILTER_PROPERTY, FilesSetsManager.getDefaultFilter().getName()); } try { Map fileIngestFilters = FilesSetsManager.getInstance() @@ -382,35 +359,37 @@ public class IngestJobSettings { for (FilesSet fSet : FilesSetsManager.getStandardFileIngestFilters()) { fileIngestFilters.put(fSet.getName(), fSet); } - this.fileIngestFilter = fileIngestFilters.get(ModuleSettings.getConfigSetting( - this.executionContext, IngestJobSettings.LAST_FILE_INGEST_FILTER_KEY)); + this.fileIngestFilter = fileIngestFilters.get(ModuleSettings.getConfigSetting(this.executionContext, IngestJobSettings.LAST_FILE_INGEST_FILTER_PROPERTY)); } catch (FilesSetsManager.FilesSetsManagerException ex) { this.fileIngestFilter = FilesSetsManager.getDefaultFilter(); - LOGGER.log(Level.SEVERE, "Failed to get file ingest filter from .properties file, default filter being used", ex); //NON-NLS + logger.log(Level.SEVERE, "Failed to get file ingest filter from .properties file, default filter being used", ex); //NON-NLS } } /** - * Gets the module names for a given key within these ingest job settings. + * Gets a list of enabled module names from the properties file for the + * execution context of these ingest job settings. * - * @param context The identifier for the context for which to get the - * module names, e.g., the Add Data Source wizard or - * Run Ingest Modules context. - * @param key The key string. - * @param defaultSetting The default list of module names. + * @param executionContext The execution context identifier. + * @param propertyName The property name. + * @param defaultSetting The default list of module names to se and return + * if the property does not exist. * - * @return The list of module names associated with the key. + * @return */ - private static HashSet getModulesNamesFromSetting(String context, String key, String defaultSetting) { - if (ModuleSettings.settingExists(context, key) == false) { - ModuleSettings.setConfigSetting(context, key, defaultSetting); + private static HashSet getModulesNames(String executionContext, String propertyName, String defaultSetting) { + if (ModuleSettings.settingExists(executionContext, propertyName) == false) { + ModuleSettings.setConfigSetting(executionContext, propertyName, defaultSetting); } HashSet moduleNames = new HashSet<>(); - String modulesSetting = ModuleSettings.getConfigSetting(context, key); + String modulesSetting = ModuleSettings.getConfigSetting(executionContext, propertyName); if (!modulesSetting.isEmpty()) { String[] settingNames = modulesSetting.split(", "); for (String name : settingNames) { - // Map some old core module names to the current core module names. + /* + * Map some obsolete core ingest module names to the current + * core ingest module names. + */ switch (name) { case "Thunderbird Parser": //NON-NLS case "MBox Parser": //NON-NLS @@ -435,7 +414,7 @@ public class IngestJobSettings { } /** - * Get a set which contains all the names of enabled modules for the + * Gets a set which contains all the names of enabled modules for the * specified context. * * @param context -the execution context (profile name) to check @@ -443,7 +422,7 @@ public class IngestJobSettings { * @return the names of the enabled modules */ static List getEnabledModules(String context) { - return new ArrayList<>(getModulesNamesFromSetting(context, ENABLED_MODULES_KEY, "")); + return new ArrayList<>(getModulesNames(context, ENABLED_MODULES_PROPERTY, "")); } /** @@ -457,14 +436,13 @@ public class IngestJobSettings { * @return True or false */ private boolean isPythonModuleSettingsFile(String moduleSettingsFilePath) { - return moduleSettingsFilePath.contains(pythonModuleSettingsPrefixCS); + return moduleSettingsFilePath.contains(PYTHON_CLASS_PROXY_PREFIX); } /** - * Gets the saved or default ingest job settings for a given ingest module - * for these ingest job settings. + * Gets the saved or default ingest job settings for a given ingest module. * - * @param factory The ingest module factory for an ingest module. + * @param factory The ingest module factory. * * @return The ingest module settings. */ @@ -478,7 +456,7 @@ public class IngestJobSettings { settings = (IngestModuleIngestJobSettings) in.readObject(); } catch (IOException | ClassNotFoundException ex) { String warning = NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.moduleSettingsLoad.warning", factory.getModuleDisplayName(), this.executionContext); //NON-NLS - LOGGER.log(Level.WARNING, warning, ex); + logger.log(Level.WARNING, warning, ex); this.warnings.add(warning); } } else { @@ -486,7 +464,7 @@ public class IngestJobSettings { settings = (IngestModuleIngestJobSettings) in.readObject(); } catch (IOException | ClassNotFoundException exception) { String warning = NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.moduleSettingsLoad.warning", factory.getModuleDisplayName(), this.executionContext); //NON-NLS - LOGGER.log(Level.WARNING, warning, exception); + logger.log(Level.WARNING, warning, exception); this.warnings.add(warning); } } @@ -529,18 +507,17 @@ public class IngestJobSettings { disabledModuleNames.add(moduleName); } } - ModuleSettings.setConfigSetting(this.executionContext, IngestJobSettings.ENABLED_MODULES_KEY, makeCommaSeparatedValuesList(enabledModuleNames)); - ModuleSettings.setConfigSetting(this.executionContext, IngestJobSettings.DISABLED_MODULES_KEY, makeCommaSeparatedValuesList(disabledModuleNames)); + ModuleSettings.setConfigSetting(this.executionContext, IngestJobSettings.ENABLED_MODULES_PROPERTY, makeCsvList(enabledModuleNames)); + ModuleSettings.setConfigSetting(this.executionContext, IngestJobSettings.DISABLED_MODULES_PROPERTY, makeCsvList(disabledModuleNames)); /** * Save the last used File Ingest Filter setting for this context. */ - ModuleSettings.setConfigSetting(this.executionContext, LAST_FILE_INGEST_FILTER_KEY, fileIngestFilter.getName()); + ModuleSettings.setConfigSetting(this.executionContext, LAST_FILE_INGEST_FILTER_PROPERTY, fileIngestFilter.getName()); } /** - * Serializes the ingest job settings for this context for a given ingest - * module. + * Serializes the ingest job settings for a given ingest module. * * @param factory The ingest module factory for the module. * @param settings The ingest job settings for the ingest module @@ -551,32 +528,40 @@ public class IngestJobSettings { out.writeObject(settings); } catch (IOException ex) { String warning = NbBundle.getMessage(IngestJobSettings.class, "IngestJobSettings.moduleSettingsSave.warning", factory.getModuleDisplayName(), this.executionContext); //NON-NLS - LOGGER.log(Level.SEVERE, warning, ex); + logger.log(Level.SEVERE, warning, ex); this.warnings.add(warning); } } /** - * Makes a comma-separated values list from a hash set of strings. + * Makes a comma-separated values list from a collection of strings. * - * @param input A hash set of strings. + * @param collection A collection of strings. * - * @return The contents of the hash set as a single string of + * @return The contents of the collection as a single string of * comma-separated values. */ - private static String makeCommaSeparatedValuesList(HashSet input) { - if (input == null || input.isEmpty()) { - return ""; - } + private static String makeCsvList(Collection collection) { + return collection.stream().collect(Collectors.joining(",")); + } - ArrayList list = new ArrayList<>(); - list.addAll(input); - StringBuilder csvList = new StringBuilder(); - for (int i = 0; i < list.size() - 1; ++i) { - csvList.append(list.get(i)).append(", "); - } - csvList.append(list.get(list.size() - 1)); - return csvList.toString(); + /** + * The type of ingest modules to run. + */ + public enum IngestType { + + /** + * Run both data source level and file-level ingest modules. + */ + ALL_MODULES, + /** + * Run only data source level ingest modules. + */ + DATA_SOURCE_ONLY, + /** + * Run only file level ingest modules. + */ + FILES_ONLY } } From 5e2ed956157b1aa7def5925cf39400e767a9148c Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Wed, 28 Feb 2018 13:28:56 -0500 Subject: [PATCH 062/128] 2229: Part 4: Use getOpenCase() instead of getCurrentCase() in casemodule. --- .../autopsy/casemodule/AddImageTask.java | 8 ++++- .../autopsy/casemodule/CaseDeleteAction.java | 4 +-- .../autopsy/casemodule/ImageFilePanel.java | 14 ++++++-- .../autopsy/casemodule/LocalFilesPanel.java | 18 ++++++---- .../casemodule/LogicalEvidenceFilePanel.java | 14 ++++++-- .../OptionalCasePropertiesPanel.java | 34 ++++++++++++------- .../casemodule/services/TagsManager.java | 23 +++++++------ 7 files changed, 77 insertions(+), 38 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java index f6458e6dd0..21a7bca103 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageTask.java @@ -107,9 +107,15 @@ class AddImageTask implements Runnable { */ @Override public void run() { + Case currentCase; + try { + currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); + return; + } progressMonitor.setIndeterminate(true); progressMonitor.setProgress(0); - Case currentCase = Case.getCurrentCase(); String imageWriterPath = ""; if (imageWriterSettings != null) { imageWriterPath = imageWriterSettings.getPath(); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java index 357b1f4704..2b333e2bdc 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseDeleteAction.java @@ -66,7 +66,7 @@ final class CaseDeleteAction extends CallableSystemAction { "# {0} - exception message", "Case.deleteCaseFailureMessageBox.message=Error deleting case: {0}",}) public void actionPerformed(ActionEvent e) { try { - Case currentCase = Case.getCurrentCase(); + Case currentCase = Case.getOpenCase(); String caseName = currentCase.getName(); String caseDirectory = currentCase.getCaseDirectory(); @@ -110,7 +110,7 @@ final class CaseDeleteAction extends CallableSystemAction { } }.execute(); } - } catch (IllegalStateException ex) { + } catch (NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Case delete action called with no current case", ex); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java index 29ad64e672..6fa73b7ff7 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java @@ -31,6 +31,7 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.filechooser.FileFilter; import org.apache.commons.lang3.StringUtils; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import static org.sleuthkit.autopsy.casemodule.Bundle.*; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; @@ -306,7 +307,9 @@ public class ImageFilePanel extends JPanel implements DocumentListener { * * @return true if a proper image has been selected, false otherwise */ - @NbBundle.Messages("ImageFilePanel.pathValidation.dataSourceOnCDriveError=Warning: Path to multi-user data source is on \"C:\" drive") + @NbBundle.Messages({"ImageFilePanel.pathValidation.dataSourceOnCDriveError=Warning: Path to multi-user data source is on \"C:\" drive", + "ImageFilePanel.pathValidation.getOpenCase.Error=Warning: Exception while getting open case." + }) public boolean validatePanel() { pathErrorLabel.setVisible(false); String path = getContentPaths(); @@ -315,9 +318,14 @@ public class ImageFilePanel extends JPanel implements DocumentListener { } // Display warning if there is one (but don't disable "next" button) - if (false == PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) { + try { + if (false == PathValidator.isValid(path, Case.getOpenCase().getCaseType())) { + pathErrorLabel.setVisible(true); + pathErrorLabel.setText(Bundle.ImageFilePanel_pathValidation_dataSourceOnCDriveError()); + } + } catch (NoCurrentCaseException ex) { pathErrorLabel.setVisible(true); - pathErrorLabel.setText(Bundle.ImageFilePanel_pathValidation_dataSourceOnCDriveError()); + pathErrorLabel.setText(Bundle.ImageFilePanel_pathValidation_getOpenCase_Error()); } return new File(path).isFile() diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java index c4fedf6cac..d73125e068 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LocalFilesPanel.java @@ -275,17 +275,23 @@ final class LocalFilesPanel extends javax.swing.JPanel { * * @param paths Absolute paths to the selected data source */ + @NbBundle.Messages("LocalFilesPanel.pathValidation.error=WARNING: Exception while gettting opon case.") private void warnIfPathIsInvalid(final List pathsList) { errorLabel.setVisible(false); - final Case.CaseType currentCaseType = Case.getCurrentCase().getCaseType(); + try { + final Case.CaseType currentCaseType = Case.getOpenCase().getCaseType(); - for (String currentPath : pathsList) { - if (!PathValidator.isValid(currentPath, currentCaseType)) { - errorLabel.setVisible(true); - errorLabel.setText(NbBundle.getMessage(this.getClass(), "DataSourceOnCDriveError.text")); - return; + for (String currentPath : pathsList) { + if (!PathValidator.isValid(currentPath, currentCaseType)) { + errorLabel.setVisible(true); + errorLabel.setText(NbBundle.getMessage(this.getClass(), "DataSourceOnCDriveError.text")); + return; + } } + } catch (NoCurrentCaseException ex) { + errorLabel.setVisible(true); + errorLabel.setText(Bundle.LocalFilesPanel_pathValidation_error()); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java index 106c5165f0..4e70d4b248 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java @@ -32,6 +32,7 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; +import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PathValidator; @@ -178,7 +179,8 @@ final class LogicalEvidenceFilePanel extends javax.swing.JPanel implements Docum */ @Messages({ "LogicalEvidenceFilePanel.validatePanel.nonL01Error.text=Only files with the .l01 file extension are supported here.", - "LogicalEvidenceFilePanel.pathValidation.dataSourceOnCDriveError=Warning: Path to multi-user data source is on \"C:\" drive" + "LogicalEvidenceFilePanel.pathValidation.dataSourceOnCDriveError=Warning: Path to multi-user data source is on \"C:\" drive", + "LogicalEvidenceFilePanel.pathValidation.getOpenCase.Error=Warning: Exception while getting open case." }) boolean validatePanel() { errorLabel.setVisible(false); @@ -188,9 +190,15 @@ final class LogicalEvidenceFilePanel extends javax.swing.JPanel implements Docum return false; } // display warning if there is one (but don't disable "next" button) - if (!PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) { + try { + if (!PathValidator.isValid(path, Case.getOpenCase().getCaseType())) { + errorLabel.setVisible(true); + errorLabel.setText(Bundle.LogicalEvidenceFilePanel_pathValidation_dataSourceOnCDriveError()); + return false; + } + } catch (NoCurrentCaseException ex) { errorLabel.setVisible(true); - errorLabel.setText(Bundle.LogicalEvidenceFilePanel_pathValidation_dataSourceOnCDriveError()); + errorLabel.setText(Bundle.LogicalEvidenceFilePanel_pathValidation_getOpenCase_Error()); return false; } //check the extension incase the path was manually entered diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java index a5b96595fd..3840c9ed1d 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -62,12 +62,19 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { OptionalCasePropertiesPanel(boolean editCurrentCase) { initComponents(); if (editCurrentCase) { - caseDisplayNameTextField.setText(Case.getCurrentCase().getDisplayName()); - caseNumberTextField.setText(Case.getCurrentCase().getNumber()); - examinerTextField.setText(Case.getCurrentCase().getExaminer()); - tfExaminerEmailText.setText(Case.getCurrentCase().getExaminerEmail()); - tfExaminerPhoneText.setText(Case.getCurrentCase().getExaminerPhone()); - taNotesText.setText(Case.getCurrentCase().getCaseNotes()); + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); + return; + } + caseDisplayNameTextField.setText(openCase.getDisplayName()); + caseNumberTextField.setText(openCase.getNumber()); + examinerTextField.setText(openCase.getExaminer()); + tfExaminerEmailText.setText(openCase.getExaminerEmail()); + tfExaminerPhoneText.setText(openCase.getExaminerPhone()); + taNotesText.setText(openCase.getCaseNotes()); setUpCaseDetailsFields(); setUpOrganizationData(); } else { @@ -86,15 +93,18 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { private void setUpOrganizationData() { if (EamDb.isEnabled()) { - Case currentCase = Case.getCurrentCase(); - if (currentCase != null) { - try { + try { + Case currentCase = Case.getOpenCase(); + if (currentCase != null) { EamDb dbManager = EamDb.getInstance(); selectedOrg = dbManager.getCase(currentCase).getOrg(); - } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Unable to get Organization associated with the case from Central Repo", ex); } + } catch (EamDbException ex) { + LOGGER.log(Level.SEVERE, "Unable to get Organization associated with the case from Central Repo", ex); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); } + if (selectedOrg != null) { setCurrentlySelectedOrganization(selectedOrg.getName()); } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java index 8d7eda2ec4..b38165ef5c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagsManager.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ import java.util.Set; import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifactTag; @@ -98,11 +99,11 @@ public class TagsManager implements Closeable { tagDisplayNames.add(tagType.getDisplayName()); }); try { - TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + TagsManager tagsManager = Case.getOpenCase().getServices().getTagsManager(); for (TagName tagName : tagsManager.getAllTagNames()) { tagDisplayNames.add(tagName.getDisplayName()); } - } catch (IllegalStateException ignored) { + } catch (NoCurrentCaseException ignored) { /* * No current case, nothing more to add to the set. */ @@ -339,8 +340,8 @@ public class TagsManager implements Closeable { ContentTag tag; tag = caseDb.addContentTag(content, tagName, comment, beginByteOffset, endByteOffset); try { - Case.getCurrentCase().notifyContentTagAdded(tag); - } catch (IllegalStateException ex) { + Case.getOpenCase().notifyContentTagAdded(tag); + } catch (NoCurrentCaseException ex) { throw new TskCoreException("Added a tag to a closed case", ex); } return tag; @@ -357,8 +358,8 @@ public class TagsManager implements Closeable { public void deleteContentTag(ContentTag tag) throws TskCoreException { caseDb.deleteContentTag(tag); try { - Case.getCurrentCase().notifyContentTagDeleted(tag); - } catch (IllegalStateException ex) { + Case.getOpenCase().notifyContentTagDeleted(tag); + } catch (NoCurrentCaseException ex) { throw new TskCoreException("Deleted a tag from a closed case", ex); } } @@ -469,8 +470,8 @@ public class TagsManager implements Closeable { public BlackboardArtifactTag addBlackboardArtifactTag(BlackboardArtifact artifact, TagName tagName, String comment) throws TskCoreException { BlackboardArtifactTag tag = caseDb.addBlackboardArtifactTag(artifact, tagName, comment); try { - Case.getCurrentCase().notifyBlackBoardArtifactTagAdded(tag); - } catch (IllegalStateException ex) { + Case.getOpenCase().notifyBlackBoardArtifactTagAdded(tag); + } catch (NoCurrentCaseException ex) { throw new TskCoreException("Added a tag to a closed case", ex); } return tag; @@ -487,8 +488,8 @@ public class TagsManager implements Closeable { public void deleteBlackboardArtifactTag(BlackboardArtifactTag tag) throws TskCoreException { caseDb.deleteBlackboardArtifactTag(tag); try { - Case.getCurrentCase().notifyBlackBoardArtifactTagDeleted(tag); - } catch (IllegalStateException ex) { + Case.getOpenCase().notifyBlackBoardArtifactTagDeleted(tag); + } catch (NoCurrentCaseException ex) { throw new TskCoreException("Deleted a tag from a closed case", ex); } } From 58a04287e31934870b7ce513ab7aeb53b5e60a06 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Wed, 28 Feb 2018 13:51:41 -0500 Subject: [PATCH 063/128] 2229: Part 5: Use getOpenCase() instead of getCurrentCase() in casemodule. --- .../datamodel/CorrelationDataSource.java | 7 +++--- .../datamodel/EamArtifactUtil.java | 22 +++++++++++-------- .../autopsy/datamodel/DeletedContent.java | 11 +++++----- .../autopsy/datamodel/EmailExtracted.java | 11 +++++----- .../autopsy/datamodel/ExtractedContent.java | 19 ++++++++-------- .../sleuthkit/autopsy/datamodel/FileSize.java | 11 +++++----- .../sleuthkit/autopsy/datamodel/Reports.java | 13 ++++++----- 7 files changed, 52 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java index 9aa9fada32..cba529954e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/CorrelationDataSource.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2015-2017 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.centralrepository.datamodel; import java.io.Serializable; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskDataException; @@ -73,8 +74,8 @@ public class CorrelationDataSource implements Serializable { public static CorrelationDataSource fromTSKDataSource(CorrelationCase correlationCase, Content dataSource) throws EamDbException { Case curCase; try { - curCase = Case.getCurrentCase(); - } catch (IllegalStateException ex) { + curCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { throw new EamDbException("Autopsy case is closed"); } String deviceId; diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java index c0810b52d3..43812eeebf 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/datamodel/EamArtifactUtil.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2015-2017 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,6 +23,7 @@ import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -89,7 +90,7 @@ public class EamArtifactUtil { // if they asked for it, add the instance details associated with this occurance. if (!eamArtifacts.isEmpty() && addInstanceDetails) { try { - Case currentCase = Case.getCurrentCase(); + Case currentCase = Case.getOpenCase(); AbstractFile bbSourceFile = currentCase.getSleuthkitCase().getAbstractFileById(bbArtifact.getObjectID()); if (null == bbSourceFile) { //@@@ Log this @@ -97,9 +98,9 @@ public class EamArtifactUtil { } // make an instance for the BB source file - CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCase()); + CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getOpenCase()); if (null == correlationCase) { - correlationCase = EamDb.getInstance().newCase(Case.getCurrentCase()); + correlationCase = EamDb.getInstance().newCase(Case.getOpenCase()); } CorrelationAttributeInstance eamInstance = new CorrelationAttributeInstance( correlationCase, @@ -116,7 +117,7 @@ public class EamArtifactUtil { } catch (TskCoreException | EamDbException ex) { LOGGER.log(Level.SEVERE, "Error creating artifact instance.", ex); // NON-NLS return eamArtifacts; - } catch (IllegalStateException ex) { + } catch (NoCurrentCaseException ex) { LOGGER.log(Level.SEVERE, "Case is closed.", ex); // NON-NLS return eamArtifacts; } @@ -145,7 +146,7 @@ public class EamArtifactUtil { // Get the associated artifact BlackboardAttribute attribute = bbArtifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); if (attribute != null) { - BlackboardArtifact associatedArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong()); + BlackboardArtifact associatedArtifact = Case.getOpenCase().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong()); return EamArtifactUtil.getCorrelationAttributeFromBlackboardArtifact(correlationType, associatedArtifact); } @@ -203,6 +204,9 @@ public class EamArtifactUtil { } catch (TskCoreException ex) { LOGGER.log(Level.SEVERE, "Error getting attribute while getting type from BlackboardArtifact.", ex); // NON-NLS return null; + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS + return null; } if (null != value) { @@ -250,9 +254,9 @@ public class EamArtifactUtil { try { CorrelationAttribute.Type filesType = EamDb.getInstance().getCorrelationTypeById(CorrelationAttribute.FILES_TYPE_ID); eamArtifact = new CorrelationAttribute(filesType, af.getMd5Hash()); - CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getCurrentCase()); + CorrelationCase correlationCase = EamDb.getInstance().getCase(Case.getOpenCase()); if (null == correlationCase) { - correlationCase = EamDb.getInstance().newCase(Case.getCurrentCase()); + correlationCase = EamDb.getInstance().newCase(Case.getOpenCase()); } CorrelationAttributeInstance cei = new CorrelationAttributeInstance( correlationCase, @@ -263,7 +267,7 @@ public class EamArtifactUtil { ); eamArtifact.addInstance(cei); return eamArtifact; - } catch (TskCoreException | EamDbException ex) { + } catch (TskCoreException | EamDbException | NoCurrentCaseException ex) { LOGGER.log(Level.SEVERE, "Error making correlation attribute.", ex); return null; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java index 9b6f36a56b..816105d080 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,6 +39,7 @@ import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import static org.sleuthkit.autopsy.datamodel.Bundle.*; @@ -206,11 +207,11 @@ public class DeletedContent implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); // new file was added // @@@ COULD CHECK If the new file is deleted before notifying... update(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ @@ -225,9 +226,9 @@ public class DeletedContent implements AutopsyVisitableItem { * received for a case that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); update(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java index 7ffcfc077c..935aec9c62 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012-2017 Basis Technology Corp. + * Copyright 2012-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,6 +39,7 @@ 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.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; @@ -240,7 +241,7 @@ public class EmailExtracted implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); /** * Even with the check above, it is still possible that * the case will be closed in a different thread before @@ -251,7 +252,7 @@ public class EmailExtracted implements AutopsyVisitableItem { if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()) { emailResults.update(); } - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ @@ -265,9 +266,9 @@ public class EmailExtracted implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); emailResults.update(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index 9a7334f423..3e001b097d 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,6 +34,7 @@ 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.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; @@ -210,7 +211,7 @@ public class ExtractedContent implements AutopsyVisitableItem { * may be received for a case that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); /** * Due to some unresolved issues with how cases are closed, * it is possible for the event to have a null oldValue if @@ -220,7 +221,7 @@ public class ExtractedContent implements AutopsyVisitableItem { if (null != event && !(this.doNotShow.contains(event.getBlackboardArtifactType()))) { refresh(true); } - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ @@ -233,9 +234,9 @@ public class ExtractedContent implements AutopsyVisitableItem { * may be received for a case that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); refresh(true); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ @@ -401,7 +402,7 @@ public class ExtractedContent implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); /** * Even with the check above, it is still possible that * the case will be closed in a different thread before @@ -412,7 +413,7 @@ public class ExtractedContent implements AutopsyVisitableItem { if (null != event && event.getBlackboardArtifactType().equals(type)) { refresh(true); } - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ @@ -426,9 +427,9 @@ public class ExtractedContent implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); refresh(true); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java index 007c59ad41..0a9321e4d5 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2017 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,6 +36,7 @@ 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.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -201,9 +202,9 @@ public class FileSize implements AutopsyVisitableItem { try { // new file was added // @@@ could check the size here and only fire off updates if we know the file meets the min size criteria - Case.getCurrentCase(); + Case.getOpenCase(); update(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ @@ -218,9 +219,9 @@ public class FileSize implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); update(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java b/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java index a09f518780..9c6d9f1829 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Reports.java @@ -40,6 +40,7 @@ import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.directorytree.ExternalViewerAction; @@ -114,9 +115,9 @@ public final class Reports implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); ReportNodeFactory.this.refresh(true); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ @@ -128,8 +129,8 @@ public final class Reports implements AutopsyVisitableItem { @Override protected boolean createKeys(List keys) { try { - keys.addAll(Case.getCurrentCase().getAllReports()); - } catch (TskCoreException ex) { + keys.addAll(Case.getOpenCase().getAllReports()); + } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(Reports.ReportNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get reports", ex); //NON-NLS } return true; @@ -265,8 +266,8 @@ public final class Reports implements AutopsyVisitableItem { NbBundle.getMessage(Reports.class, "DeleteReportAction.actionPerformed.showConfirmDialog.title"), JOptionPane.YES_NO_OPTION)) { try { - Case.getCurrentCase().deleteReports(selectedReportsCollection); - } catch (TskCoreException | IllegalStateException ex) { + Case.getOpenCase().deleteReports(selectedReportsCollection); + } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(DeleteReportAction.class.getName()).log(Level.SEVERE, "Error deleting reports", ex); // NON-NLS MessageNotifyUtil.Message.error(Bundle.DeleteReportAction_showConfirmDialog_errorMsg()); } From 43a92df4cf93186c6b5b6e8fa28226f813899aa3 Mon Sep 17 00:00:00 2001 From: Raman Date: Wed, 28 Feb 2018 15:11:10 -0500 Subject: [PATCH 064/128] 3568: improve error handling in FileTypeViewers --- .../autopsy/contentviewers/PListViewer.java | 68 +++++++---- .../autopsy/contentviewers/SQLiteViewer.java | 113 ++++++++++++------ 2 files changed, 120 insertions(+), 61 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java index c488c93be8..7d76f3deba 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java @@ -35,11 +35,13 @@ import java.io.File; import java.io.IOException; import java.text.ParseException; import java.util.ArrayList; +import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.TableCellRenderer; @@ -50,6 +52,7 @@ import org.openide.explorer.ExplorerManager; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.util.NbBundle; +import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.TskCoreException; @@ -261,35 +264,50 @@ public class PListViewer extends javax.swing.JPanel implements FileTypeViewer, E * * @return none */ + @NbBundle.Messages({"PListViewer.processPlist.interruptedMessage=Interrupted while parsing/displaying plist file.", + "PListViewer.processPlist.errorMessage=Error while parsing/displaying plist file."}) private void processPlist(final AbstractFile plistFile) { - final byte[] plistFileBuf = new byte[(int) plistFile.getSize()]; - try { - plistFile.read(plistFileBuf, 0, plistFile.getSize()); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Error reading bytes of plist file.", ex); - } - - final List plist; - try { - plist = parsePList(plistFileBuf); - new SwingWorker() { - @Override - protected Void doInBackground() { + new SwingWorker, Void>() { + @Override + protected List doInBackground() throws TskCoreException, IOException, PropertyListFormatException, ParseException, ParserConfigurationException, SAXException { + // Read in and parse the file + final byte[] plistFileBuf = new byte[(int) plistFile.getSize()]; + plistFile.read(plistFileBuf, 0, plistFile.getSize()); + final List plist = parsePList(plistFileBuf); + + return plist; + } + + @Override + protected void done() { + super.done(); + List plist; + try { + plist = get(); setupTable(plist); - return null; + + SwingUtilities.invokeLater(() -> { + setColumnWidths(); + }); + } catch (InterruptedException ex) { + LOGGER.log(Level.SEVERE, "Interruption while parsing/dislaying plist file " + plistFile.getName(), ex); + + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getMessage(), + Bundle.PListViewer_processPlist_interruptedMessage(), + JOptionPane.ERROR_MESSAGE); + + } catch (ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Exception while parsing/dislaying plist file " + plistFile.getName(), ex); + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getCause().getMessage(), + Bundle.PListViewer_processPlist_errorMessage(), + JOptionPane.ERROR_MESSAGE); } - - @Override - protected void done() { - super.done(); - setColumnWidths(); - } - }.execute(); - - } catch (IOException | PropertyListFormatException | ParseException | ParserConfigurationException | SAXException ex) { - LOGGER.log(Level.SEVERE, String.format("Error parsing plist for file (obj_id = %d)", plistFile.getId()), ex); - } + + } + }.execute(); } /** diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 49d6231435..76d86f3d43 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -39,8 +39,10 @@ import java.util.TreeMap; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JComboBox; +import javax.swing.JOptionPane; import javax.swing.SwingWorker; import org.openide.util.NbBundle; +import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.casemodule.services.Services; @@ -313,13 +315,16 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { * * @return none */ + @NbBundle.Messages({"# {0} - fileName", + "SQLiteViewer.processSQLiteFile.errorMessage=Error opening SQLite file {0}" + }) private void processSQLiteFile(AbstractFile sqliteFile) { tablesDropdownList.removeAllItems(); - new SwingWorker() { + new SwingWorker() { @Override - protected Boolean doInBackground() throws Exception { + protected Void doInBackground() throws Exception { try { // Copy the file to temp folder @@ -336,23 +341,27 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS // Read all table names and schema - return getTables(); + getTables(); } catch (IOException ex) { - LOGGER.log(Level.SEVERE, "Failed to copy DB file.", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Failed to copy DB file " + sqliteFile.getName(), ex); //NON-NLS + throw ex; } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Failed to Open DB.", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Failed to open DB file " + sqliteFile.getName(), ex); //NON-NLS + throw ex; } catch (ClassNotFoundException ex) { - LOGGER.log(Level.SEVERE, "Failed to initialize JDBC Sqlite.", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Failed to initialize JDBC SQLite.", ex); //NON-NLS + throw ex; } - return false; + return null; } @Override protected void done() { super.done(); try { - boolean status = get(); - if ((status == true) && (dbTablesMap.size() > 0)) { + + get(); + if (dbTablesMap.size() > 0) { dbTablesMap.keySet().forEach((tableName) -> { tablesDropdownList.addItem(tableName); }); @@ -361,8 +370,20 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { tablesDropdownList.addItem("No tables found"); tablesDropdownList.setEnabled(false); } - } catch (InterruptedException | ExecutionException ex) { - LOGGER.log(Level.SEVERE, "Unexpected exception while opening DB file", ex); //NON-NLS + } catch (InterruptedException ex) { + + LOGGER.log(Level.SEVERE, "Interrupted while opening DB file " + sqliteFile.getName(), ex); //NON-NLS + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getMessage(), + Bundle.SQLiteViewer_processSQLiteFile_errorMessage(sqliteFile.getName()), + JOptionPane.ERROR_MESSAGE); + } + catch (ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while opening DB file " + sqliteFile.getName(), ex); //NON-NLS + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getCause().getMessage(), + Bundle.SQLiteViewer_processSQLiteFile_errorMessage(sqliteFile.getName()), + JOptionPane.ERROR_MESSAGE); } } }.execute(); @@ -413,28 +434,26 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { * * @return true if success, false otherwise */ - private boolean getTables() { + private void getTables() throws SQLException { - try { - Statement statement = connection.createStatement(); + + Statement statement = connection.createStatement(); + ResultSet resultSet = statement.executeQuery( + "SELECT name, sql FROM sqlite_master " + + " WHERE type= 'table' " + + " ORDER BY name;"); //NON-NLS - ResultSet resultSet = statement.executeQuery( - "SELECT name, sql FROM sqlite_master " - + " WHERE type= 'table' " - + " ORDER BY name;"); //NON-NLS + while (resultSet.next()) { + String tableName = resultSet.getString("name"); //NON-NLS + String tableSQL = resultSet.getString("sql"); //NON-NLS - while (resultSet.next()) { - String tableName = resultSet.getString("name"); //NON-NLS - String tableSQL = resultSet.getString("sql"); //NON-NLS - - dbTablesMap.put(tableName, tableSQL); - } - } catch (SQLException e) { - LOGGER.log(Level.SEVERE, "Error getting table names from the DB", e); //NON-NLS + dbTablesMap.put(tableName, tableSQL); } - return true; } + @NbBundle.Messages({"# {0} - tableName", + "SQLiteViewer.selectTable.errorText=Error getting row count for table: {0}" + }) private void selectTable(String tableName) { if (worker != null && !worker.isDone()) { worker.cancel(false); @@ -452,10 +471,9 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { return resultSet.getInt("count"); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Failed to get data for table.", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Failed to get row count for table " + tableName, ex); //NON-NLS + throw ex; } - //NON-NLS - return 0; } @Override @@ -481,14 +499,27 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { selectedTableView.setupTable(Collections.emptyList()); } - } catch (InterruptedException | ExecutionException ex) { - LOGGER.log(Level.SEVERE, "Unexpected exception while reading table.", ex); //NON-NLS + } catch (InterruptedException ex ) { + LOGGER.log(Level.SEVERE, "Interrupted while getting row count from table " + tableName, ex); //NON-NLS + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getMessage(), + Bundle.SQLiteViewer_selectTable_errorText(tableName), + JOptionPane.ERROR_MESSAGE); + } + catch (ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception while getting row count from table " + tableName, ex); //NON-NLS + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getCause().getMessage(), + Bundle.SQLiteViewer_selectTable_errorText(tableName), + JOptionPane.ERROR_MESSAGE); } } }; worker.execute(); } + @NbBundle.Messages({"# {0} - tableName", + "SQLiteViewer.readTable.errorText=Error getting rows for table: {0}"}) private void readTable(String tableName, int startRow, int numRowsToRead) { if (worker != null && !worker.isDone()) { @@ -506,13 +537,12 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { + " LIMIT " + Integer.toString(numRowsToRead) + " OFFSET " + Integer.toString(startRow - 1) ); //NON-NLS - + return resultSetToArrayList(resultSet); } catch (SQLException ex) { LOGGER.log(Level.SEVERE, "Failed to get data for table " + tableName, ex); //NON-NLS + throw ex; } - //NON-NLS - return null; } @Override @@ -530,8 +560,19 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { }else{ selectedTableView.setupTable(Collections.emptyList()); } - } catch (InterruptedException | ExecutionException ex) { + } catch (InterruptedException ex) { + LOGGER.log(Level.SEVERE, "Interrupted while reading table " + tableName, ex); //NON-NLS + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getMessage(), + Bundle.SQLiteViewer_readTable_errorText(tableName), + JOptionPane.ERROR_MESSAGE); + } + catch (ExecutionException ex) { LOGGER.log(Level.SEVERE, "Unexpected exception while reading table " + tableName, ex); //NON-NLS + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getCause().getMessage(), + Bundle.SQLiteViewer_readTable_errorText(tableName), + JOptionPane.ERROR_MESSAGE); } } }; From 564d47d84d1b883f758684f41641372cc060c16e Mon Sep 17 00:00:00 2001 From: Raman Date: Wed, 28 Feb 2018 15:47:04 -0500 Subject: [PATCH 065/128] Address Codacy review comments. --- .../autopsy/contentviewers/SQLiteViewer.java | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index d60797c8b6..e67623039e 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -362,14 +362,14 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { try { get(); - if (dbTablesMap.size() > 0) { - dbTablesMap.keySet().forEach((tableName) -> { - tablesDropdownList.addItem(tableName); - }); - } else { + if (dbTablesMap.isEmpty()) { // Populate error message tablesDropdownList.addItem("No tables found"); tablesDropdownList.setEnabled(false); + } else { + dbTablesMap.keySet().forEach((tableName) -> { + tablesDropdownList.addItem(tableName); + }); } } catch (InterruptedException ex) { @@ -450,6 +450,8 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { dbTablesMap.put(tableName, tableSQL); } + resultSet.close(); + statement.close(); } @NbBundle.Messages({"# {0} - tableName", @@ -465,9 +467,11 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { @Override protected Integer doInBackground() throws Exception { + Statement statement = null; + ResultSet resultSet = null; try { - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery( + statement = connection.createStatement(); + resultSet = statement.executeQuery( "SELECT count (*) as count FROM " + tableName); //NON-NLS return resultSet.getInt("count"); @@ -475,6 +479,14 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { LOGGER.log(Level.SEVERE, "Failed to get row count for table " + tableName, ex); //NON-NLS throw ex; } + finally { + if (null != resultSet) { + resultSet.close(); + } + if (null != statement) { + statement.close(); + } + } } @Override @@ -531,9 +543,12 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { worker = new SwingWorker>, Void>() { @Override protected ArrayList> doInBackground() throws Exception { + + Statement statement = null; + ResultSet resultSet = null; try { - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery( + statement = connection.createStatement(); + resultSet = statement.executeQuery( "SELECT * FROM " + tableName + " LIMIT " + Integer.toString(numRowsToRead) + " OFFSET " + Integer.toString(startRow - 1) @@ -544,6 +559,14 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { LOGGER.log(Level.SEVERE, "Failed to get data for table " + tableName, ex); //NON-NLS throw ex; } + finally { + if (null != resultSet) { + resultSet.close(); + } + if (null != statement) { + statement.close(); + } + } } @Override From 63f4d5d388170902bae79689fded89b6f240f356 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Wed, 28 Feb 2018 17:08:57 -0500 Subject: [PATCH 066/128] 2229: Part 6: Use getOpenCase() instead of getCurrentCase() in datamodule. --- .../datamodel/AbstractAbstractFileNode.java | 7 ++++--- .../autopsy/datamodel/AbstractContentNode.java | 7 ++++--- .../datamodel/BlackboardArtifactNode.java | 17 +++++++++-------- .../autopsy/datamodel/FileTypesByExtension.java | 7 ++++--- .../autopsy/datamodel/FileTypesByMimeType.java | 7 ++++--- .../sleuthkit/autopsy/datamodel/ImageNode.java | 7 ++++--- .../autopsy/datamodel/VirtualDirectoryNode.java | 11 ++++++----- 7 files changed, 35 insertions(+), 28 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index df2d3dfdc4..1404ca4950 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,6 +33,7 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.WeakListeners; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.coreutils.Logger; @@ -263,8 +264,8 @@ public abstract class AbstractAbstractFileNode extends A protected void addTagProperty(Sheet.Set ss) { List tags = new ArrayList<>(); try { - tags.addAll(Case.getCurrentCase().getServices().getTagsManager().getContentTagsByContent(content)); - } catch (TskCoreException ex) { + tags.addAll(Case.getOpenCase().getServices().getTagsManager().getContentTagsByContent(content)); + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to get tags for content " + content.getName(), ex); } ss.put(new NodeProperty<>("Tags", AbstractAbstractFileNode_tagsProperty_displayName(), diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index 66355c2661..8750d57f30 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,6 +26,7 @@ import java.util.logging.Level; import org.openide.util.lookup.Lookups; import org.openide.util.Lookup; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.Content; @@ -119,12 +120,12 @@ public abstract class AbstractContentNode extends ContentNode + " AND type = " + TskData.ObjectType.ABSTRACTFILE.getObjectType() + ") AS OBJECT_IDS"; //NON-NLS; - try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCase().getSleuthkitCase().executeQuery(query)) { + try (SleuthkitCase.CaseDbQuery dbQuery = Case.getOpenCase().getSleuthkitCase().executeQuery(query)) { ResultSet resultSet = dbQuery.getResultSet(); if(resultSet.next()){ return (0 < resultSet.getInt("count")); } - } catch (TskCoreException | SQLException ex) { + } catch (TskCoreException | SQLException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Error checking if the node has children, for content: " + c, ex); //NON-NLS } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 056d87bfbd..5f54adfed2 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,6 +41,7 @@ import org.openide.util.NbBundle; import org.openide.util.WeakListeners; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; @@ -259,7 +260,7 @@ public class BlackboardArtifactNode extends AbstractContentNode(NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.name"), NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.createSheet.artifactType.displayName"), NO_DESCR, @@ -336,7 +337,7 @@ public class BlackboardArtifactNode extends AbstractContentNode tags = new ArrayList<>(); try { - tags.addAll(Case.getCurrentCase().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact)); - tags.addAll(Case.getCurrentCase().getServices().getTagsManager().getContentTagsByContent(associated)); - } catch (TskCoreException ex) { + tags.addAll(Case.getOpenCase().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact)); + tags.addAll(Case.getOpenCase().getServices().getTagsManager().getContentTagsByContent(associated)); + } catch (TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.SEVERE, "Failed to get tags for artifact " + artifact.getDisplayName(), ex); } ss.put(new NodeProperty<>("Tags", NbBundle.getMessage(AbstractAbstractFileNode.class, "BlackboardArtifactNode.createSheet.tags.displayName"), diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index 2341fff12c..2d8cc564d2 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,6 +37,7 @@ import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.FileTypes.FileTypesKey; @@ -93,10 +94,10 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); typesRoot.updateShowCounts(); update(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 9fe183a8a8..e7af90fab7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,6 +41,7 @@ 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.casemodule.NoCurrentCaseException; import static org.sleuthkit.autopsy.core.UserPreferences.hideKnownFilesInViewsTree; import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree; import org.sleuthkit.autopsy.coreutils.Logger; @@ -163,10 +164,10 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi * already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); typesRoot.updateShowCounts(); populateHashMap(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index 4efe1db364..7c06fb7afb 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,6 +33,7 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor; import org.sleuthkit.autopsy.directorytree.FileSearchAction; @@ -170,7 +171,7 @@ public class ImageNode extends AbstractContentNode { Bundle.ImageNode_createSheet_timezone_desc(), this.content.getTimeZone())); - try (CaseDbQuery query = Case.getCurrentCase().getSleuthkitCase().executeQuery("SELECT device_id FROM data_source_info WHERE obj_id = " + this.content.getId());) { + try (CaseDbQuery query = Case.getOpenCase().getSleuthkitCase().executeQuery("SELECT device_id FROM data_source_info WHERE obj_id = " + this.content.getId());) { ResultSet deviceIdSet = query.getResultSet(); if (deviceIdSet.next()) { ss.put(new NodeProperty<>(Bundle.ImageNode_createSheet_deviceId_name(), @@ -178,7 +179,7 @@ public class ImageNode extends AbstractContentNode { Bundle.ImageNode_createSheet_deviceId_desc(), deviceIdSet.getString("device_id"))); } - } catch (SQLException | TskCoreException ex) { + } catch (SQLException | TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to get device id for the following image: " + this.content.getId(), ex); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java index 3d78d3f13d..312e6a8da0 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VirtualDirectoryNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,6 +26,7 @@ import java.util.logging.Level; import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -105,7 +106,7 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode { Bundle.VirtualDirectoryNode_createSheet_size_displayName(), Bundle.VirtualDirectoryNode_createSheet_size_desc(), this.content.getSize())); - try (SleuthkitCase.CaseDbQuery query = Case.getCurrentCase().getSleuthkitCase().executeQuery("SELECT time_zone FROM data_source_info WHERE obj_id = " + this.content.getId())) { + try (SleuthkitCase.CaseDbQuery query = Case.getOpenCase().getSleuthkitCase().executeQuery("SELECT time_zone FROM data_source_info WHERE obj_id = " + this.content.getId())) { ResultSet timeZoneSet = query.getResultSet(); if (timeZoneSet.next()) { ss.put(new NodeProperty<>(Bundle.VirtualDirectoryNode_createSheet_timezone_name(), @@ -113,10 +114,10 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode { Bundle.VirtualDirectoryNode_createSheet_timezone_desc(), timeZoneSet.getString("time_zone"))); } - } catch (SQLException | TskCoreException ex) { + } catch (SQLException | TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to get time zone for the following image: " + this.content.getId(), ex); } - try (SleuthkitCase.CaseDbQuery query = Case.getCurrentCase().getSleuthkitCase().executeQuery("SELECT device_id FROM data_source_info WHERE obj_id = " + this.content.getId());) { + try (SleuthkitCase.CaseDbQuery query = Case.getOpenCase().getSleuthkitCase().executeQuery("SELECT device_id FROM data_source_info WHERE obj_id = " + this.content.getId());) { ResultSet deviceIdSet = query.getResultSet(); if (deviceIdSet.next()) { ss.put(new NodeProperty<>(Bundle.VirtualDirectoryNode_createSheet_deviceId_name(), @@ -124,7 +125,7 @@ public class VirtualDirectoryNode extends SpecialDirectoryNode { Bundle.VirtualDirectoryNode_createSheet_deviceId_desc(), deviceIdSet.getString("device_id"))); } - } catch (SQLException | TskCoreException ex) { + } catch (SQLException | TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to get device id for the following image: " + this.content.getId(), ex); } From 037c820f6a0a93d7264f6dbce20da4a9585cf804 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Thu, 1 Mar 2018 11:47:29 -0500 Subject: [PATCH 067/128] 3532: Move the html file to diff_output folder and update the tag contents --- test/script/regression.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/script/regression.py b/test/script/regression.py index df8173da88..889365ad3e 100644 --- a/test/script/regression.py +++ b/test/script/regression.py @@ -108,9 +108,6 @@ def main(): if not parse_result: Errors.print_error("The arguments were given wrong") exit(1) - # Remove the output_link_file - if file_exists(OUTPUT_DIR_LINK_FILE): - os.remove(OUTPUT_DIR_LINK_FILE) test_config = TestConfiguration(args) case_type = test_config.userCaseType.lower() @@ -883,7 +880,7 @@ class TestConfiguration(object): #write the output_dir to a html file - linkFile = open(OUTPUT_DIR_LINK_FILE, "a") + linkFile = open(os.path.join(self.args.diff_files_output_folder, OUTPUT_DIR_LINK_FILE), "a") index = self.output_dir.find("\\") linkStr = "Multi test output" + linkStr = linkStr + "\">Enterprise Viking Tests" else: - linkStr = linkStr + "\">Single test output" + linkStr = linkStr + "\">Standalone Viking Tests" linkFile.write(linkStr + "\n") linkFile.close() From 4000a6318d15a6260e6d94a71d147a2efda39547 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Thu, 1 Mar 2018 13:04:11 -0500 Subject: [PATCH 068/128] 2229: Part 7: Use getOpenCase() instead of getCurrentCase() in centrolrepo, communications and contentviewers --- .../eventlisteners/CaseEventListener.java | 47 +++++++++++++------ .../eventlisteners/IngestEventsListener.java | 7 +-- .../ingestmodule/IngestModule.java | 22 +++++++-- .../communications/AccountsRootChildren.java | 7 +-- .../autopsy/communications/FiltersPanel.java | 13 +++-- .../autopsy/contentviewers/PListViewer.java | 16 ++++++- .../autopsy/contentviewers/SQLiteViewer.java | 17 +++++-- 7 files changed, 94 insertions(+), 35 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java index b053d9df17..da11671a08 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/CaseEventListener.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2015-2017 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +28,7 @@ import java.util.logging.Level; import java.util.stream.Collectors; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; @@ -162,8 +163,8 @@ final class CaseEventListener implements PropertyChangeListener { try { // Get the remaining tags on the content object - Content content = Case.getCurrentCase().getSleuthkitCase().getContentById(contentID); - TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + Content content = Case.getOpenCase().getSleuthkitCase().getContentById(contentID); + TagsManager tagsManager = Case.getOpenCase().getServices().getTagsManager(); List tags = tagsManager.getContentTagsByContent(content); if (tags.stream() @@ -185,7 +186,7 @@ final class CaseEventListener implements PropertyChangeListener { // There's still at least one bad tag, so leave the known status as is return; } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.SEVERE, "Failed to find content", ex); return; } @@ -241,6 +242,13 @@ final class CaseEventListener implements PropertyChangeListener { return; } } else { //BLACKBOARD_ARTIFACT_TAG_DELETED + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); + return; + } // For deleted tags, we want to set the file status to UNKNOWN if: // - The tag that was just removed is notable in central repo // - There are no remaining tags that are notable @@ -256,9 +264,9 @@ final class CaseEventListener implements PropertyChangeListener { try { // Get the remaining tags on the artifact - content = Case.getCurrentCase().getSleuthkitCase().getContentById(contentID); - bbArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(artifactID); - TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + content = openCase.getSleuthkitCase().getContentById(contentID); + bbArtifact = openCase.getSleuthkitCase().getBlackboardArtifact(artifactID); + TagsManager tagsManager = openCase.getServices().getTagsManager(); List tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbArtifact); if (tags.stream() @@ -319,10 +327,10 @@ final class CaseEventListener implements PropertyChangeListener { * that are tagged with the given tag name. */ try { - TagName tagName = Case.getCurrentCase().getServices().getTagsManager().getDisplayNamesToTagNamesMap().get(modifiedTagName); + TagName tagName = Case.getOpenCase().getServices().getTagsManager().getDisplayNamesToTagNamesMap().get(modifiedTagName); //First update the artifacts //Get all BlackboardArtifactTags with this tag name - List artifactTags = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifactTagsByTagName(tagName); + List artifactTags = Case.getOpenCase().getSleuthkitCase().getBlackboardArtifactTagsByTagName(tagName); for (BlackboardArtifactTag bbTag : artifactTags) { //start with assumption that none of the other tags applied to this Correlation Attribute will prevent it's status from being changed boolean hasTagWithConflictingKnownStatus = false; @@ -338,7 +346,7 @@ final class CaseEventListener implements PropertyChangeListener { } //Get the BlackboardArtifact which this BlackboardArtifactTag has been applied to. BlackboardArtifact bbArtifact = bbTag.getArtifact(); - TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + TagsManager tagsManager = Case.getOpenCase().getServices().getTagsManager(); List tags = tagsManager.getBlackboardArtifactTagsByArtifact(bbArtifact); //get all tags which are on this blackboard artifact for (BlackboardArtifactTag t : tags) { @@ -366,7 +374,7 @@ final class CaseEventListener implements PropertyChangeListener { } // Next update the files - List fileTags = Case.getCurrentCase().getSleuthkitCase().getContentTagsByTagName(tagName); + List fileTags = Case.getOpenCase().getSleuthkitCase().getContentTagsByTagName(tagName); //Get all ContentTags with this tag name for (ContentTag contentTag : fileTags) { //start with assumption that none of the other tags applied to this ContentTag will prevent it's status from being changed @@ -376,7 +384,7 @@ final class CaseEventListener implements PropertyChangeListener { // the status of the file in the central repository if (tagName.getKnownStatus() == TskData.FileKnown.UNKNOWN) { Content content = contentTag.getContent(); - TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + TagsManager tagsManager = Case.getOpenCase().getServices().getTagsManager(); List tags = tagsManager.getContentTagsByContent(content); //get all tags which are on this file for (ContentTag t : tags) { @@ -405,6 +413,8 @@ final class CaseEventListener implements PropertyChangeListener { LOGGER.log(Level.SEVERE, "Cannot update known status in central repository for tag: " + modifiedTagName, ex); //NON-NLS } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Cannot get central repository for tag: " + modifiedTagName, ex); //NON-NLS + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS } } //TAG_STATUS_CHANGED } @@ -424,15 +434,22 @@ final class CaseEventListener implements PropertyChangeListener { if (!EamDb.isEnabled()) { return; } + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); + return; + } final DataSourceAddedEvent dataSourceAddedEvent = (DataSourceAddedEvent) event; Content newDataSource = dataSourceAddedEvent.getDataSource(); try { - String deviceId = Case.getCurrentCase().getSleuthkitCase().getDataSource(newDataSource.getId()).getDeviceId(); - CorrelationCase correlationCase = dbManager.getCase(Case.getCurrentCase()); + String deviceId = openCase.getSleuthkitCase().getDataSource(newDataSource.getId()).getDeviceId(); + CorrelationCase correlationCase = dbManager.getCase(openCase); if (null == correlationCase) { - correlationCase = dbManager.newCase(Case.getCurrentCase()); + correlationCase = dbManager.newCase(openCase); } if (null == dbManager.getDataSource(correlationCase, deviceId)) { dbManager.newDataSource(CorrelationDataSource.fromTSKDataSource(correlationCase, newDataSource)); diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java index 0877bc1685..258cb13dcf 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/eventlisteners/IngestEventsListener.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2015-2017 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,6 +32,7 @@ import java.util.logging.Level; import java.util.stream.Collectors; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -144,9 +145,9 @@ public class IngestEventsListener { tifArtifact.addAttributes(attributes); try { // index the artifact for keyword search - Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); + Blackboard blackboard = Case.getOpenCase().getServices().getBlackboard(); blackboard.indexArtifact(tifArtifact); - } catch (Blackboard.BlackboardException ex) { + } catch (Blackboard.BlackboardException | NoCurrentCaseException ex) { LOGGER.log(Level.SEVERE, "Unable to index blackboard artifact " + tifArtifact.getArtifactID(), ex); //NON-NLS } diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java index 09f3c63449..9657cdcd19 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/ingestmodule/IngestModule.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,6 +26,7 @@ import java.util.stream.Collectors; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -79,7 +80,12 @@ class IngestModule implements FileIngestModule { return ProcessResult.OK; } - blackboard = Case.getCurrentCase().getServices().getBlackboard(); + try { + blackboard = Case.getOpenCase().getServices().getBlackboard(); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); + return ProcessResult.ERROR; + } if (!EamArtifactUtil.isValidCentralRepoFile(af)) { return ProcessResult.OK; @@ -190,8 +196,16 @@ class IngestModule implements FileIngestModule { } return; } + Case autopsyCase; + try { + autopsyCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); + throw new IngestModuleException("Exception while getting open case.", ex); + } + // Don't allow sqlite central repo databases to be used for multi user cases - if ((Case.getCurrentCase().getCaseType() == Case.CaseType.MULTI_USER_CASE) + if ((autopsyCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) && (EamDbPlatformEnum.getSelectedPlatform() == EamDbPlatformEnum.SQLITE)) { LOGGER.log(Level.SEVERE, "Cannot run correlation engine on a multi-user case with a SQLite central repository."); throw new IngestModuleException("Cannot run on a multi-user case with a SQLite central repository."); // NON-NLS @@ -212,7 +226,7 @@ class IngestModule implements FileIngestModule { LOGGER.log(Level.SEVERE, "Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS throw new IngestModuleException("Error getting correlation type FILES in ingest module start up.", ex); // NON-NLS } - Case autopsyCase = Case.getCurrentCase(); + try { eamCase = centralRepoDb.getCase(autopsyCase); } catch (EamDbException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java index 63e1e17d82..d15feb83d6 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +30,7 @@ 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.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.datamodel.Account; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -76,13 +77,13 @@ class AccountsRootChildren extends ChildFactory { private String getDataSourceName(AccountDeviceInstance accountDeviceInstance) { try { - final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); + final SleuthkitCase sleuthkitCase = Case.getOpenCase().getSleuthkitCase(); for (DataSource dataSource : sleuthkitCase.getDataSources()) { if (dataSource.getDeviceId().equals(accountDeviceInstance.getDeviceId())) { return sleuthkitCase.getContentById(dataSource.getId()).getName(); } } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Error getting datasource name, falling back on device ID.", ex); } return accountDeviceInstance.getDeviceId(); diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 84db631a86..cd78566325 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -36,6 +36,7 @@ import org.openide.nodes.Children; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; @@ -198,7 +199,7 @@ final public class FiltersPanel extends javax.swing.JPanel { //TODO: something like this commented code could be used to show only //the account types that are found: - //final CommunicationsManager communicationsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); + //final CommunicationsManager communicationsManager = Case.getOpenCase().getSleuthkitCase().getCommunicationsManager(); //List accountTypesInUse = communicationsManager.getAccountTypesInUse(); //accountTypesInUSe.forEach(...) Account.Type.PREDEFINED_ACCOUNT_TYPES.forEach(type -> { @@ -229,7 +230,7 @@ final public class FiltersPanel extends javax.swing.JPanel { */ private void updateDeviceFilter() { try { - final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); + final SleuthkitCase sleuthkitCase = Case.getOpenCase().getSleuthkitCase(); for (DataSource dataSource : sleuthkitCase.getDataSources()) { String dsName = sleuthkitCase.getContentById(dataSource.getId()).getName(); @@ -242,7 +243,7 @@ final public class FiltersPanel extends javax.swing.JPanel { }); }; - } catch (IllegalStateException ex) { + } catch (NoCurrentCaseException ex) { logger.log(Level.WARNING, "Communications Visualization Tool opened with no open case.", ex); } catch (TskCoreException tskCoreException) { logger.log(Level.SEVERE, "There was a error loading the datasources for the case.", tskCoreException); @@ -498,10 +499,12 @@ final public class FiltersPanel extends javax.swing.JPanel { ImmutableSet.of(CALL_LOG, MESSAGE))); try { - final CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); + final CommunicationsManager commsManager = Case.getOpenCase().getSleuthkitCase().getCommunicationsManager(); em.setRootContext(new AbstractNode(Children.create(new AccountsRootChildren(commsManager, commsFilter), true))); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "There was an error getting the CommunicationsManager for the current case.", ex); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); } needsRefresh = false; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java index c488c93be8..ede7f0a738 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/PListViewer.java @@ -51,6 +51,7 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.TskCoreException; import org.xml.sax.SAXException; @@ -185,8 +186,21 @@ public class PListViewer extends javax.swing.JPanel implements FileTypeViewer, E */ private void exportButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportButtonActionPerformed + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + JOptionPane.showMessageDialog(this, + "Failed to export plist file.", + Bundle.PListViewer_ExportFailed_message(), + JOptionPane.ERROR_MESSAGE); + + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); + return; + } + final JFileChooser fileChooser = new JFileChooser(); - fileChooser.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory())); + fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); fileChooser.setFileFilter(new FileNameExtensionFilter("XML file", "xml")); final int returnVal = fileChooser.showSaveDialog(this); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 49d6231435..af8674d7b7 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -42,6 +42,7 @@ import javax.swing.JComboBox; import javax.swing.SwingWorker; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.coreutils.Logger; @@ -323,7 +324,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { try { // Copy the file to temp folder - tmpDBPathName = Case.getCurrentCase().getTempDirectory() + File.separator + sqliteFile.getName(); + tmpDBPathName = Case.getOpenCase().getTempDirectory() + File.separator + sqliteFile.getName(); tmpDBFile = new File(tmpDBPathName); ContentUtils.writeToFile(sqliteFile, tmpDBFile); @@ -337,6 +338,8 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { // Read all table names and schema return getTables(); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS } catch (IOException ex) { LOGGER.log(Level.SEVERE, "Failed to copy DB file.", ex); //NON-NLS } catch (SQLException ex) { @@ -379,8 +382,14 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { * @return true if the meta file is found and copied successfully, false otherwise */ private boolean findAndCopySQLiteMetaFile(AbstractFile sqliteFile, String metaFileName ) { - - SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return false; + } + SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase(); Services services = new Services(sleuthkitCase); FileManager fileManager = services.getFileManager(); @@ -394,7 +403,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { if (metaFiles != null) { for (AbstractFile metaFile: metaFiles) { - String tmpMetafilePathName = Case.getCurrentCase().getTempDirectory() + File.separator + metaFile.getName(); + String tmpMetafilePathName = openCase.getTempDirectory() + File.separator + metaFile.getName(); File tmpMetafile = new File(tmpMetafilePathName); try { From 23f5b3c3dab0162e55a13977ba805926e18d353e Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 10:55:38 -0500 Subject: [PATCH 069/128] 2229: Part 8: Use getOpenCase() instead of getCurrentCase() in datamodule, casemodule, hashdatabase and report. --- .../events/ContentTagAddedEvent.java | 9 ++-- .../casemodule/events/TagAddedEvent.java | 9 ++-- .../autopsy/datamodel/accounts/Accounts.java | 43 ++++++++++--------- .../hashdatabase/HashDbSearchAction.java | 11 +++++ .../hashdatabase/HashDbSearchPanel.java | 22 +++++++--- .../modules/hashdatabase/HashDbSearcher.java | 34 +++++++++------ .../autopsy/report/FileReportText.java | 5 ++- 7 files changed, 85 insertions(+), 48 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/ContentTagAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/ContentTagAddedEvent.java index 966138434a..c724dfa104 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/ContentTagAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/ContentTagAddedEvent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.casemodule.events; import java.io.Serializable; import javax.annotation.concurrent.Immutable; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.TskCoreException; @@ -41,10 +42,10 @@ public class ContentTagAddedEvent extends TagAddedEvent implements S * * @return ContentTag that was added * - * @throws IllegalStateException + * @throws NoCurrentCaseException * @throws TskCoreException */ - ContentTag getTagByID() throws IllegalStateException, TskCoreException { - return Case.getCurrentCase().getServices().getTagsManager().getContentTagByTagID(getTagID()); + ContentTag getTagByID() throws NoCurrentCaseException, TskCoreException { + return Case.getOpenCase().getServices().getTagsManager().getContentTagByTagID(getTagID()); } } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/TagAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/TagAddedEvent.java index 1f679c47a3..6d7ebb4f9c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/TagAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/TagAddedEvent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.casemodule.events; import java.io.Serializable; import java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.datamodel.Tag; @@ -84,7 +85,7 @@ abstract class TagAddedEvent extends AutopsyEvent implements Seri try { tag = getTagByID(); return tag; - } catch (IllegalStateException | TskCoreException ex) { + } catch (NoCurrentCaseException | TskCoreException ex) { Logger.getLogger(TagAddedEvent.class.getName()).log(Level.SEVERE, "Error doing lazy load for remote event", ex); //NON-NLS return null; } @@ -98,8 +99,8 @@ abstract class TagAddedEvent extends AutopsyEvent implements Seri * * @return the Tag based on the saved tag id * - * @throws IllegalStateException + * @throws NoCurrentCaseException * @throws TskCoreException */ - abstract T getTagByID() throws IllegalStateException, TskCoreException; + abstract T getTagByID() throws NoCurrentCaseException, TskCoreException; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java index b3e8a9a1b1..422a0cff0a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -59,6 +59,7 @@ import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; import org.sleuthkit.autopsy.datamodel.AutopsyItemVisitor; import org.sleuthkit.autopsy.datamodel.AutopsyVisitableItem; @@ -240,7 +241,7 @@ final public class Accounts implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); /** * Even with the check above, it is still possible that * the case will be closed in a different thread before @@ -252,7 +253,7 @@ final public class Accounts implements AutopsyVisitableItem { && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) { reviewStatusBus.post(eventData); } - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) @@ -264,9 +265,9 @@ final public class Accounts implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); refresh(true); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { @@ -368,7 +369,7 @@ final public class Accounts implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); /** * Even with the check above, it is still possible that * the case will be closed in a different thread before @@ -380,7 +381,7 @@ final public class Accounts implements AutopsyVisitableItem { && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) { reviewStatusBus.post(eventData); } - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) @@ -392,10 +393,10 @@ final public class Accounts implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); refresh(true); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { @@ -517,7 +518,7 @@ final public class Accounts implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); /** * Even with the check above, it is still possible that * the case will be closed in a different thread before @@ -529,7 +530,7 @@ final public class Accounts implements AutopsyVisitableItem { && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) { reviewStatusBus.post(eventData); } - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) @@ -541,10 +542,10 @@ final public class Accounts implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); refresh(true); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { @@ -651,7 +652,7 @@ final public class Accounts implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); /** * Even with the check above, it is still possible that * the case will be closed in a different thread before @@ -663,7 +664,7 @@ final public class Accounts implements AutopsyVisitableItem { && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) { reviewStatusBus.post(eventData); } - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) @@ -675,10 +676,10 @@ final public class Accounts implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); refresh(true); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { @@ -862,7 +863,7 @@ final public class Accounts implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); /** * Even with the check above, it is still possible that * the case will be closed in a different thread before @@ -874,7 +875,7 @@ final public class Accounts implements AutopsyVisitableItem { && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_ACCOUNT.getTypeID()) { reviewStatusBus.post(eventData); } - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) @@ -886,10 +887,10 @@ final public class Accounts implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); refresh(true); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(Case.Events.CURRENT_CASE.toString())) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchAction.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchAction.java index 61c2fe699e..7d4ec611b6 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchAction.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchAction.java @@ -24,6 +24,7 @@ import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.directorytree.HashSearchProvider; import org.sleuthkit.datamodel.AbstractFile; @@ -118,8 +119,12 @@ public class HashDbSearchAction extends CallableSystemAction implements HashSear * performAction. */ @Override + @NbBundle.Messages ({ + "HashDbSearchAction.noOpenCase.errMsg=No open case available." + }) public void performAction() { // Make sure at least 1 file has an md5 hash + try { if (file != null && HashDbSearcher.countFilesMd5Hashed() > 0) { doSearch(); } else { @@ -129,6 +134,12 @@ public class HashDbSearchAction extends CallableSystemAction implements HashSear NbBundle.getMessage(this.getClass(), "HashDbSearchAction.dlgMsg.title"), JOptionPane.ERROR_MESSAGE); } + } catch (NoCurrentCaseException ex) { + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + Bundle.HashDbSearchAction_noOpenCase_errMsg(), + NbBundle.getMessage(this.getClass(), "HashDbSearchAction.dlgMsg.title"), + JOptionPane.ERROR_MESSAGE); + } } private void doSearch() { diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchPanel.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchPanel.java index 9f7642c69f..4dac4864c7 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearchPanel.java @@ -32,6 +32,7 @@ import javax.swing.table.DefaultTableModel; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.PlainDocument; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.ingest.IngestManager; /** @@ -290,16 +291,27 @@ class HashDbSearchPanel extends javax.swing.JPanel implements ActionListener { * Search through all tsk_files to find ones with the same hashes as the * hashes given. */ + @NbBundle.Messages ({ + "HashDbSearchPanel.noOpenCase.errMsg=No open case available." + }) boolean search() { // Check if any hashed have been entered if (hashTable.getRowCount() != 0) { // Make sure at least 1 file has an md5 hash - if (HashDbSearcher.countFilesMd5Hashed() > 0) { - return doSearch(); - } else { + try { + if (HashDbSearcher.countFilesMd5Hashed() > 0) { + return doSearch(); + } else { + JOptionPane.showMessageDialog(this, + NbBundle.getMessage(this.getClass(), + "HashDbSearchPanel.noFilesHaveMD5HashMsg"), + NbBundle.getMessage(this.getClass(), "HashDbSearchPanel.dlgMsg.title"), + JOptionPane.ERROR_MESSAGE); + return false; + } + } catch (NoCurrentCaseException ex) { JOptionPane.showMessageDialog(this, - NbBundle.getMessage(this.getClass(), - "HashDbSearchPanel.noFilesHaveMD5HashMsg"), + Bundle.HashDbSearchPanel_noOpenCase_errMsg(), NbBundle.getMessage(this.getClass(), "HashDbSearchPanel.dlgMsg.title"), JOptionPane.ERROR_MESSAGE); return false; diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearcher.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearcher.java index 628f99f78e..213400925f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearcher.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbSearcher.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,9 +22,12 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.logging.Level; import javax.swing.SwingWorker; import org.netbeans.api.progress.ProgressHandle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.FsContent; import org.sleuthkit.datamodel.SleuthkitCase; @@ -34,7 +37,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; * the same content. */ class HashDbSearcher { - + private static final Logger logger = Logger.getLogger(HashDbSearcher.class.getName()); /** * Given a string hash value, find all files with that hash. * @@ -42,8 +45,8 @@ class HashDbSearcher { * * @return a List of all FsContent with the given hash */ - static List findFilesByMd5(String md5Hash) { - final Case currentCase = Case.getCurrentCase(); + static List findFilesByMd5(String md5Hash) throws NoCurrentCaseException { + final Case currentCase = Case.getOpenCase(); final SleuthkitCase skCase = currentCase.getSleuthkitCase(); return skCase.findFilesByMd5(md5Hash); } @@ -56,7 +59,7 @@ class HashDbSearcher { * * @return a Map of md5 hashes mapped to the list of files hit */ - static Map> findFilesBymd5(List md5Hash) { + static Map> findFilesBymd5(List md5Hash) throws NoCurrentCaseException { Map> map = new LinkedHashMap>(); for (String md5 : md5Hash) { List files = findFilesByMd5(md5); @@ -69,7 +72,7 @@ class HashDbSearcher { // Same as above, but with a given ProgressHandle to accumulate and StringWorker to check if cancelled - static Map> findFilesBymd5(List md5Hash, ProgressHandle progress, SwingWorker worker) { + static Map> findFilesBymd5(List md5Hash, ProgressHandle progress, SwingWorker worker) throws NoCurrentCaseException { Map> map = new LinkedHashMap>(); if (!worker.isCancelled()) { progress.switchToDeterminate(md5Hash.size()); @@ -101,9 +104,14 @@ class HashDbSearcher { */ static List findFiles(FsContent file) { String md5; - if ((md5 = file.getMd5Hash()) != null) { - return findFilesByMd5(md5); - } else { + try { + if ((md5 = file.getMd5Hash()) != null) { + return findFilesByMd5(md5); + } else { + return Collections.emptyList(); + } + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); return Collections.emptyList(); } } @@ -114,8 +122,8 @@ class HashDbSearcher { * * @return true if the search feature is ready. */ - static boolean allFilesMd5Hashed() { - final Case currentCase = Case.getCurrentCase(); + static boolean allFilesMd5Hashed() throws NoCurrentCaseException { + final Case currentCase = Case.getOpenCase(); final SleuthkitCase skCase = currentCase.getSleuthkitCase(); return skCase.allFilesMd5Hashed(); } @@ -125,8 +133,8 @@ class HashDbSearcher { * * @return the number of files with an MD5 */ - static int countFilesMd5Hashed() { - final Case currentCase = Case.getCurrentCase(); + static int countFilesMd5Hashed() throws NoCurrentCaseException { + final Case currentCase = Case.getOpenCase(); final SleuthkitCase skCase = currentCase.getSleuthkitCase(); return skCase.countFilesMd5Hashed(); } diff --git a/Core/src/org/sleuthkit/autopsy/report/FileReportText.java b/Core/src/org/sleuthkit/autopsy/report/FileReportText.java index c9a239cf0a..71cccf96ed 100644 --- a/Core/src/org/sleuthkit/autopsy/report/FileReportText.java +++ b/Core/src/org/sleuthkit/autopsy/report/FileReportText.java @@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; @@ -71,13 +72,15 @@ class FileReportText implements FileReportModule { if (out != null) { try { out.close(); - Case.getCurrentCase().addReport(reportPath, NbBundle.getMessage(this.getClass(), + Case.getOpenCase().addReport(reportPath, NbBundle.getMessage(this.getClass(), "FileReportText.getName.text"), ""); } catch (IOException ex) { logger.log(Level.WARNING, "Could not close output writer when ending report.", ex); //NON-NLS } catch (TskCoreException ex) { String errorMessage = String.format("Error adding %s to case as a report", reportPath); //NON-NLS logger.log(Level.SEVERE, errorMessage, ex); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); } } } From e589b6506d14aaa313f890dbba5310ec6f81b578 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 11:47:53 -0500 Subject: [PATCH 070/128] 2229: Part 9: Use getOpenCase() instead of getCurrentCase() in datamodule, report, imagewriter, ingest, stix and vmextractor. --- .../org/sleuthkit/autopsy/datamodel/Tags.java | 35 ++++---- .../autopsy/imagewriter/ImageWriter.java | 15 ++-- .../ingest/IngestJobSettingsPanel.java | 5 +- .../ingest/events/BlackboardPostEvent.java | 7 +- .../autopsy/modules/stix/EvalAccountObj.java | 7 +- .../vmextractor/VMExtractorIngestModule.java | 31 ++++--- .../autopsy/report/TableReportGenerator.java | 80 ++++++++++++------- 7 files changed, 107 insertions(+), 73 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java index 64b77ac245..57fd35beba 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,6 +34,7 @@ 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.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -141,10 +142,10 @@ public class Tags implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); refresh(true); tagResults.update(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ @@ -158,10 +159,10 @@ public class Tags implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); refresh(true); tagResults.update(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ @@ -195,10 +196,10 @@ public class Tags implements AutopsyVisitableItem { @Override protected boolean createKeys(List keys) { try { - List tagNamesInUse = Case.getCurrentCase().getServices().getTagsManager().getTagNamesInUse(); + List tagNamesInUse = Case.getOpenCase().getServices().getTagsManager().getTagNamesInUse(); Collections.sort(tagNamesInUse); keys.addAll(tagNamesInUse); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(TagNameNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS } return true; @@ -242,10 +243,10 @@ public class Tags implements AutopsyVisitableItem { private void updateDisplayName() { long tagsCount = 0; try { - TagsManager tm = Case.getCurrentCase().getServices().getTagsManager(); + TagsManager tm = Case.getOpenCase().getServices().getTagsManager(); tagsCount = tm.getContentTagsCountByTagName(tagName); tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(TagNameNode.class.getName()).log(Level.SEVERE, "Failed to get tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS } setDisplayName(tagName.getDisplayName() + " \u200E(\u200E" + tagsCount + ")\u200E"); @@ -347,8 +348,8 @@ public class Tags implements AutopsyVisitableItem { private void updateDisplayName() { long tagsCount = 0; try { - tagsCount = Case.getCurrentCase().getServices().getTagsManager().getContentTagsCountByTagName(tagName); - } catch (TskCoreException ex) { + tagsCount = Case.getOpenCase().getServices().getTagsManager().getContentTagsCountByTagName(tagName); + } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(ContentTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get content tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS } super.setDisplayName(CONTENT_DISPLAY_NAME + " (" + tagsCount + ")"); @@ -402,8 +403,8 @@ public class Tags implements AutopsyVisitableItem { protected boolean createKeys(List keys) { // Use the content tags bearing the specified tag name as the keys. try { - keys.addAll(Case.getCurrentCase().getServices().getTagsManager().getContentTagsByTagName(tagName)); - } catch (TskCoreException ex) { + keys.addAll(Case.getOpenCase().getServices().getTagsManager().getContentTagsByTagName(tagName)); + } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(ContentTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS } return true; @@ -446,8 +447,8 @@ public class Tags implements AutopsyVisitableItem { private void updateDisplayName() { long tagsCount = 0; try { - tagsCount = Case.getCurrentCase().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName); - } catch (TskCoreException ex) { + tagsCount = Case.getOpenCase().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName); + } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(BlackboardArtifactTagTypeNode.class.getName()).log(Level.SEVERE, "Failed to get blackboard artifact tags count for " + tagName.getDisplayName() + " tag name", ex); //NON-NLS } super.setDisplayName(ARTIFACT_DISPLAY_NAME + " (" + tagsCount + ")"); @@ -501,8 +502,8 @@ public class Tags implements AutopsyVisitableItem { protected boolean createKeys(List keys) { try { // Use the blackboard artifact tags bearing the specified tag name as the keys. - keys.addAll(Case.getCurrentCase().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName)); - } catch (TskCoreException ex) { + keys.addAll(Case.getOpenCase().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName)); + } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(BlackboardArtifactTagNodeFactory.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS } return true; diff --git a/Core/src/org/sleuthkit/autopsy/imagewriter/ImageWriter.java b/Core/src/org/sleuthkit/autopsy/imagewriter/ImageWriter.java index 3cd166a1b5..dcdbca0379 100644 --- a/Core/src/org/sleuthkit/autopsy/imagewriter/ImageWriter.java +++ b/Core/src/org/sleuthkit/autopsy/imagewriter/ImageWriter.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,6 +32,7 @@ import java.util.logging.Level; import org.netbeans.api.progress.ProgressHandle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.Image; @@ -78,12 +79,12 @@ class ImageWriter implements PropertyChangeListener{ this.settings = settings; doUI = RuntimeProperties.runningWithGUI(); - // We save the reference to the sleuthkit case here in case getCurrentCase() is set to + // We save the reference to the sleuthkit case here in case getOpenCase() is set to // null before Image Writer finishes. The user can still elect to wait for image writer // (in ImageWriterService.closeCaseResources) even though the case is closing. try{ - caseDb = Case.getCurrentCase().getSleuthkitCase(); - } catch (IllegalStateException ex){ + caseDb = Case.getOpenCase().getSleuthkitCase(); + } catch (NoCurrentCaseException ex){ logger.log(Level.SEVERE, "Unable to load case. Image writer will be cancelled."); this.isCancelled = true; } @@ -151,10 +152,10 @@ class ImageWriter implements PropertyChangeListener{ Image image; try{ - image = Case.getCurrentCase().getSleuthkitCase().getImageById(dataSourceId); + image = Case.getOpenCase().getSleuthkitCase().getImageById(dataSourceId); imageHandle = image.getImageHandle(); - } catch (IllegalStateException ex){ - // This exception means that getCurrentCase() failed because no case was open. + } catch (NoCurrentCaseException ex){ + // This exception means that getOpenCase() failed because no case was open. // This can happen when the user closes the case while ingest is ongoing - canceling // ingest fires off the DataSourceAnalysisCompletedEvent while the case is in the // process of closing. diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java index 8feb3d0bb2..8439a288e8 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobSettingsPanel.java @@ -43,6 +43,7 @@ import javax.swing.table.TableColumn; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponents.AdvancedConfigurationDialog; import org.sleuthkit.autopsy.modules.interestingitems.FilesSet; import org.sleuthkit.autopsy.modules.interestingitems.FilesSetDefsPanel; @@ -97,9 +98,9 @@ public final class IngestJobSettingsPanel extends javax.swing.JPanel { this.settings = settings; this.dataSources.addAll(dataSources); try { - SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase skCase = Case.getOpenCase().getSleuthkitCase(); ingestJobs.addAll(skCase.getIngestJobs()); - } catch (IllegalStateException ex) { + } catch (NoCurrentCaseException ex) { logger.log(Level.SEVERE, "No open case", ex); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Failed to load ingest job information", ex); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/events/BlackboardPostEvent.java b/Core/src/org/sleuthkit/autopsy/ingest/events/BlackboardPostEvent.java index 7846f122f3..9e05d538de 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/events/BlackboardPostEvent.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/events/BlackboardPostEvent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,6 +26,7 @@ import java.util.logging.Level; import java.util.stream.Collectors; import javax.annotation.concurrent.Immutable; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -93,11 +94,11 @@ public final class BlackboardPostEvent extends AutopsyEvent implements Serializa SerializableEventData data = (SerializableEventData) super.getOldValue(); Collection artifacts = new ArrayList<>(); for (Long id : data.artifactIds) { - artifacts.add(Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(id)); + artifacts.add(Case.getOpenCase().getSleuthkitCase().getBlackboardArtifact(id)); } eventData = new ModuleDataEvent(data.moduleName, data.artifactTypeId, !artifacts.isEmpty() ? artifacts : null); return eventData; - } catch (IllegalStateException | TskCoreException ex) { + } catch (NoCurrentCaseException | TskCoreException ex) { logger.log(Level.SEVERE, "Error doing lazy load for remote event", ex); //NON-NLS return null; } diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalAccountObj.java b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalAccountObj.java index d2a6b19551..5174e479eb 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalAccountObj.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalAccountObj.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +30,7 @@ import java.util.ArrayList; import org.mitre.cybox.objects.AccountObjectType; import org.mitre.cybox.objects.UserAccountObjectType; import org.mitre.cybox.objects.WindowsUserAccount; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * @@ -104,7 +105,7 @@ class EvalAccountObj extends EvaluatableObject { try { List finalHits = new ArrayList(); - Case case1 = Case.getCurrentCase(); + Case case1 = Case.getOpenCase(); SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); List artList = sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_OS_ACCOUNT); @@ -150,7 +151,7 @@ class EvalAccountObj extends EvaluatableObject { // Didn't find any matches return new ObservableResult(id, "AccountObject: No matches found for " + searchString, //NON-NLS spacing, ObservableResult.ObservableState.FALSE, null); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { return new ObservableResult(id, "AccountObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS spacing, ObservableResult.ObservableState.INDETERMINATE, null); } diff --git a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java index 35e66647d8..2a719e5709 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/vmextractor/VMExtractorIngestModule.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2012-2016 Basis Technology Corp. + * Copyright 2012-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,6 +35,7 @@ import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.GeneralFilter; import org.sleuthkit.autopsy.casemodule.ImageDSProcessor; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorCallback; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessorProgressMonitor; import org.sleuthkit.autopsy.coreutils.Logger; @@ -72,13 +73,14 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { private final HashMap imageFolderToOutputFolder = new HashMap<>(); private int folderId = 0; - @Messages({"# {0} - data source name", "deviceIdQueryErrMsg=Data source {0} missing Device ID"}) + @Messages({"# {0} - data source name", "deviceIdQueryErrMsg=Data source {0} missing Device ID", + "VMExtractorIngestModule.noOpenCase.errMsg=No open case available."}) @Override public void startUp(IngestJobContext context) throws IngestModuleException { this.context = context; long dataSourceObjId = context.getDataSource().getId(); try { - Case currentCase = Case.getCurrentCase(); + Case currentCase = Case.getOpenCase(); SleuthkitCase caseDb = currentCase.getSleuthkitCase(); DataSource dataSource = caseDb.getDataSource(dataSourceObjId); parentDeviceId = dataSource.getDeviceId(); @@ -87,13 +89,15 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { String timeStamp = dateFormat.format(Calendar.getInstance().getTime()); String ingestJobOutputDirName = context.getDataSource().getName() + "_" + context.getDataSource().getId() + "_" + timeStamp; ingestJobOutputDirName = ingestJobOutputDirName.replace(':', '_'); - ingestJobOutputDir = Paths.get(Case.getCurrentCase().getModuleDirectory(), VMExtractorIngestModuleFactory.getModuleName(), ingestJobOutputDirName); + ingestJobOutputDir = Paths.get(currentCase.getModuleDirectory(), VMExtractorIngestModuleFactory.getModuleName(), ingestJobOutputDirName); // create module output folder to write extracted virtual machine files to Files.createDirectories(ingestJobOutputDir); } catch (IOException | SecurityException | UnsupportedOperationException ex) { throw new IngestModule.IngestModuleException(Bundle.VMExtractorIngestModule_cannotCreateOutputDir_message(ex.getLocalizedMessage()), ex); } catch (TskDataException | TskCoreException ex) { throw new IngestModule.IngestModuleException(Bundle.deviceIdQueryErrMsg(context.getDataSource().getName()), ex); + } catch (NoCurrentCaseException ex) { + throw new IngestModule.IngestModuleException(Bundle.VMExtractorIngestModule_noOpenCase_errMsg(), ex); } } @@ -116,6 +120,9 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error querying case database", ex); //NON-NLS return ProcessResult.ERROR; + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return ProcessResult.ERROR; } if (vmFiles.isEmpty()) { @@ -191,6 +198,10 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { logger.log(Level.SEVERE, "Failed to ingest virtual machine file " + file + " in folder " + folder, ex); //NON-NLS MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "VMExtractorIngestModule.msgNotify.failedIngestVM.title.txt"), NbBundle.getMessage(this.getClass(), "VMExtractorIngestModule.msgNotify.failedIngestVM.msg.txt", file)); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "VMExtractorIngestModule.msgNotify.failedIngestVM.title.txt"), + Bundle.VMExtractorIngestModule_noOpenCase_errMsg()); } } // Update progress bar @@ -212,11 +223,11 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { * @throws TskCoreException if there is a problem querying the case * database. */ - private static List findVirtualMachineFiles(Content dataSource) throws TskCoreException { + private static List findVirtualMachineFiles(Content dataSource) throws TskCoreException, NoCurrentCaseException { List vmFiles = new ArrayList<>(); for (String vmExtension : GeneralFilter.VIRTUAL_MACHINE_EXTS) { String searchString = "%" + vmExtension; // want a search string that looks like this "%.vmdk" - vmFiles.addAll(Case.getCurrentCase().getServices().getFileManager().findFiles(dataSource, searchString)); + vmFiles.addAll(Case.getOpenCase().getServices().getFileManager().findFiles(dataSource, searchString)); } return vmFiles; } @@ -250,13 +261,13 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { * * @param vmFile A virtual machine file. */ - private void ingestVirtualMachineImage(Path vmFile) throws InterruptedException, IOException { + private void ingestVirtualMachineImage(Path vmFile) throws InterruptedException, IOException, NoCurrentCaseException { /* * Try to add the virtual machine file to the case as a data source. */ UUID taskId = UUID.randomUUID(); - Case.getCurrentCase().notifyAddingDataSource(taskId); + Case.getOpenCase().notifyAddingDataSource(taskId); ImageDSProcessor dataSourceProcessor = new ImageDSProcessor(); AddDataSourceCallback dspCallback = new AddDataSourceCallback(vmFile); synchronized (this) { @@ -272,7 +283,7 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { * ingest context. */ if (!dspCallback.vmDataSources.isEmpty()) { - Case.getCurrentCase().notifyDataSourceAdded(dspCallback.vmDataSources.get(0), taskId); + Case.getOpenCase().notifyDataSourceAdded(dspCallback.vmDataSources.get(0), taskId); List dataSourceContent = new ArrayList<>(dspCallback.vmDataSources); IngestJobSettings ingestJobSettings = new IngestJobSettings(context.getExecutionContext()); for (String warning : ingestJobSettings.getWarnings()) { @@ -283,7 +294,7 @@ final class VMExtractorIngestModule extends DataSourceIngestModuleAdapter { NbBundle.getMessage(this.getClass(), "VMExtractorIngestModule.addedVirtualMachineImage.message", vmFile.toString()))); IngestManager.getInstance().queueIngestJob(dataSourceContent, ingestJobSettings); } else { - Case.getCurrentCase().notifyFailedAddingDataSource(taskId); + Case.getOpenCase().notifyFailedAddingDataSource(taskId); } } diff --git a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java index 499f848d54..673d5fa3d4 100644 --- a/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java @@ -39,6 +39,7 @@ import java.util.TreeSet; import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; @@ -181,11 +182,11 @@ class TableReportGenerator { String accountDisplayname = accountTypeStr; if (accountTypeStr != null) { try { - Account.Type acctType = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().getAccountType(accountTypeStr); + Account.Type acctType = Case.getOpenCase().getSleuthkitCase().getCommunicationsManager().getAccountType(accountTypeStr); if (acctType != null) { accountDisplayname = acctType.getDisplayName(); } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Unable to get display name for account type " + accountTypeStr, ex); } } @@ -267,8 +268,8 @@ class TableReportGenerator { // Get the content tags. List tags; try { - tags = Case.getCurrentCase().getServices().getTagsManager().getAllContentTags(); - } catch (TskCoreException ex) { + tags = Case.getOpenCase().getServices().getTagsManager().getAllContentTags(); + } catch (TskCoreException | NoCurrentCaseException ex) { errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedGetContentTags")); logger.log(Level.SEVERE, "failed to get content tags", ex); //NON-NLS return; @@ -360,8 +361,8 @@ class TableReportGenerator { List tags; try { - tags = Case.getCurrentCase().getServices().getTagsManager().getAllBlackboardArtifactTags(); - } catch (TskCoreException ex) { + tags = Case.getOpenCase().getServices().getTagsManager().getAllBlackboardArtifactTags(); + } catch (TskCoreException | NoCurrentCaseException ex) { errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedGetBBArtifactTags")); logger.log(Level.SEVERE, "failed to get blackboard artifact tags", ex); //NON-NLS return; @@ -452,8 +453,8 @@ class TableReportGenerator { private void checkIfTagHasImage(BlackboardArtifactTag artifactTag) { AbstractFile file; try { - file = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(artifactTag.getArtifact().getObjectID()); - } catch (TskCoreException ex) { + file = Case.getOpenCase().getSleuthkitCase().getAbstractFileById(artifactTag.getArtifact().getObjectID()); + } catch (TskCoreException | NoCurrentCaseException ex) { errorList.add( NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.errGetContentFromBBArtifact")); logger.log(Level.WARNING, "Error while getting content from a blackboard artifact to report on.", ex); //NON-NLS @@ -520,6 +521,7 @@ class TableReportGenerator { * @param tableModule module to report on */ @SuppressWarnings("deprecation") + @NbBundle.Messages ({"ReportGenerator.errList.noOpenCase=No open case available."}) private void writeKeywordHits(TableReportModule tableModule, String comment, HashSet tagNamesFilter) { // Query for keyword lists-only so that we can tell modules what lists @@ -528,7 +530,15 @@ class TableReportGenerator { // so that we only report the lists that we will later provide with real // hits. If no keyord hits are tagged, then we make the page for nothing. String orderByClause; - if (Case.getCurrentCase().getCaseType() == Case.CaseType.MULTI_USER_CASE) { + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + errorList.add(Bundle.ReportGenerator_errList_noOpenCase()); + logger.log(Level.SEVERE, "Exception while getting open case: ", ex); //NON-NLS + return; + } + if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) { orderByClause = "ORDER BY convert_to(att.value_text, 'SQL_ASCII') ASC NULLS FIRST"; //NON-NLS } else { orderByClause = "ORDER BY list ASC"; //NON-NLS @@ -546,7 +556,7 @@ class TableReportGenerator { + //NON-NLS "GROUP BY list " + orderByClause; //NON-NLS - try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCase().getSleuthkitCase().executeQuery(keywordListQuery)) { + try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(keywordListQuery)) { ResultSet listsRs = dbQuery.getResultSet(); List lists = new ArrayList<>(); while (listsRs.next()) { @@ -569,7 +579,7 @@ class TableReportGenerator { return; } - if (Case.getCurrentCase().getCaseType() == Case.CaseType.MULTI_USER_CASE) { + if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) { orderByClause = "ORDER BY convert_to(att3.value_text, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS + "convert_to(att1.value_text, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS + "convert_to(f.parent_path, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS @@ -602,7 +612,7 @@ class TableReportGenerator { + //NON-NLS orderByClause; //NON-NLS - try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCase().getSleuthkitCase().executeQuery(keywordsQuery)) { + try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(keywordsQuery)) { ResultSet resultSet = dbQuery.getResultSet(); String currentKeyword = ""; @@ -627,9 +637,9 @@ class TableReportGenerator { String uniquePath = ""; try { - AbstractFile f = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(objId); + AbstractFile f = openCase.getSleuthkitCase().getAbstractFileById(objId); if (f != null) { - uniquePath = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(objId).getUniquePath(); + uniquePath = openCase.getSleuthkitCase().getAbstractFileById(objId).getUniquePath(); } } catch (TskCoreException ex) { errorList.add( @@ -685,7 +695,15 @@ class TableReportGenerator { @SuppressWarnings("deprecation") private void writeHashsetHits(TableReportModule tableModule, String comment, HashSet tagNamesFilter) { String orderByClause; - if (Case.getCurrentCase().getCaseType() == Case.CaseType.MULTI_USER_CASE) { + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + errorList.add(Bundle.ReportGenerator_errList_noOpenCase()); + logger.log(Level.SEVERE, "Exception while getting open case: ", ex); //NON-NLS + return; + } + if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) { orderByClause = "ORDER BY convert_to(att.value_text, 'SQL_ASCII') ASC NULLS FIRST"; //NON-NLS } else { orderByClause = "ORDER BY att.value_text ASC"; //NON-NLS @@ -703,7 +721,7 @@ class TableReportGenerator { + //NON-NLS "GROUP BY list " + orderByClause; //NON-NLS - try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCase().getSleuthkitCase().executeQuery(hashsetsQuery)) { + try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(hashsetsQuery)) { // Query for hashsets ResultSet listsRs = dbQuery.getResultSet(); List lists = new ArrayList<>(); @@ -722,7 +740,7 @@ class TableReportGenerator { return; } - if (Case.getCurrentCase().getCaseType() == Case.CaseType.MULTI_USER_CASE) { + if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) { orderByClause = "ORDER BY convert_to(att.value_text, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS + "convert_to(f.parent_path, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS + "convert_to(f.name, 'SQL_ASCII') ASC NULLS FIRST, " //NON-NLS @@ -745,7 +763,7 @@ class TableReportGenerator { + //NON-NLS orderByClause; //NON-NLS - try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCase().getSleuthkitCase().executeQuery(hashsetHitsQuery)) { + try (SleuthkitCase.CaseDbQuery dbQuery = openCase.getSleuthkitCase().executeQuery(hashsetHitsQuery)) { // Query for hashset hits ResultSet resultSet = dbQuery.getResultSet(); String currentSet = ""; @@ -768,9 +786,9 @@ class TableReportGenerator { String uniquePath = ""; try { - AbstractFile f = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(objId); + AbstractFile f = openCase.getSleuthkitCase().getAbstractFileById(objId); if (f != null) { - uniquePath = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(objId).getUniquePath(); + uniquePath = openCase.getSleuthkitCase().getAbstractFileById(objId).getUniquePath(); } } catch (TskCoreException ex) { errorList.add( @@ -834,9 +852,9 @@ class TableReportGenerator { this.attributes = attrs; this.tags = tags; try { - this.content = Case.getCurrentCase().getSleuthkitCase().getContentById(artifact.getObjectID()); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Could not get content from database"); + this.content = Case.getOpenCase().getSleuthkitCase().getContentById(artifact.getObjectID()); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Could not get content from database", ex); } } @@ -972,12 +990,12 @@ class TableReportGenerator { HashSet allTags = getTags(); try { - List contentTags = Case.getCurrentCase().getServices().getTagsManager().getContentTagsByContent(content); + List contentTags = Case.getOpenCase().getServices().getTagsManager().getContentTagsByContent(content); for (ContentTag ct : contentTags) { String notableString = ct.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : ""; allTags.add(ct.getName().getDisplayName() + notableString); } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedGetContentTags")); logger.log(Level.SEVERE, "Failed to get content tags", ex); //NON-NLS } @@ -1008,8 +1026,8 @@ class TableReportGenerator { private List getFilteredArtifacts(BlackboardArtifact.Type type, HashSet tagNamesFilter) { List artifacts = new ArrayList<>(); try { - for (BlackboardArtifact artifact : Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifacts(type.getTypeID())) { - List tags = Case.getCurrentCase().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact); + for (BlackboardArtifact artifact : Case.getOpenCase().getSleuthkitCase().getBlackboardArtifacts(type.getTypeID())) { + List tags = Case.getOpenCase().getServices().getTagsManager().getBlackboardArtifactTagsByArtifact(artifact); HashSet uniqueTagNames = new HashSet<>(); for (BlackboardArtifactTag tag : tags) { String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : ""; @@ -1019,13 +1037,13 @@ class TableReportGenerator { continue; } try { - artifacts.add(new ArtifactData(artifact, Case.getCurrentCase().getSleuthkitCase().getBlackboardAttributes(artifact), uniqueTagNames)); + artifacts.add(new ArtifactData(artifact, Case.getOpenCase().getSleuthkitCase().getBlackboardAttributes(artifact), uniqueTagNames)); } catch (TskCoreException ex) { errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedGetBBAttribs")); logger.log(Level.SEVERE, "Failed to get Blackboard Attributes when generating report.", ex); //NON-NLS } } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { errorList.add(NbBundle.getMessage(this.getClass(), "ReportGenerator.errList.failedGetBBArtifacts")); logger.log(Level.SEVERE, "Failed to get Blackboard Artifacts when generating report.", ex); //NON-NLS } @@ -1609,12 +1627,12 @@ class TableReportGenerator { + //NON-NLS "WHERE tn.tag_name_id = bat.tag_name_id AND bat.artifact_id = " + artifactId; //NON-NLS - try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCase().getSleuthkitCase().executeQuery(query)) { + try (SleuthkitCase.CaseDbQuery dbQuery = Case.getOpenCase().getSleuthkitCase().executeQuery(query)) { ResultSet tagNameRows = dbQuery.getResultSet(); while (tagNameRows.next()) { uniqueTagNames.add(tagNameRows.getString("display_name")); //NON-NLS } - } catch (TskCoreException | SQLException ex) { + } catch (TskCoreException | SQLException | NoCurrentCaseException ex) { throw new TskCoreException("Error getting tag names for artifact: ", ex); } From c314babd8ed5a1d098315b075f0f9b3afdbe1787 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 12:11:15 -0500 Subject: [PATCH 071/128] 2229: Part 9: Use getOpenCase() instead of getCurrentCase() in datamodule, casemodule, menuactions... --- .../casemodule/events/DataSourceAddedEvent.java | 7 ++++--- .../sleuthkit/autopsy/datamodel/KeywordHits.java | 11 ++++++----- .../autopsy/diagnostics/PerformancePanel.java | 8 ++++---- .../menuactions/DataContentDynamicMenu.java | 7 ++++--- .../menuactions/DataExplorerDynamicMenu.java | 7 ++++--- .../autopsy/modules/iOS/TextMessageAnalyzer.java | 16 ++++++++++++---- .../autopsy/timeline/TimeLineController.java | 7 ++++--- 7 files changed, 38 insertions(+), 25 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceAddedEvent.java index aa02c07846..dcf575a5dc 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/DataSourceAddedEvent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,7 @@ import java.io.Serializable; import java.util.UUID; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.datamodel.Content; @@ -78,9 +79,9 @@ public final class DataSourceAddedEvent extends AutopsyEvent implements Serializ } try { long id = (Long) super.getNewValue(); - dataSource = Case.getCurrentCase().getSleuthkitCase().getContentById(id); + dataSource = Case.getOpenCase().getSleuthkitCase().getContentById(id); return dataSource; - } catch (IllegalStateException | TskCoreException ex) { + } catch (NoCurrentCaseException | TskCoreException ex) { logger.log(Level.SEVERE, "Error doing lazy load for remote event", ex); //NON-NLS return null; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index 75873d135e..dbe3dc371b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -43,6 +43,7 @@ import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import static org.sleuthkit.autopsy.datamodel.Bundle.*; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -411,7 +412,7 @@ public class KeywordHits implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); /** * Even with the check above, it is still possible that * the case will be closed in a different thread before @@ -422,7 +423,7 @@ public class KeywordHits implements AutopsyVisitableItem { if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) { keywordResults.update(); } - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(IngestManager.IngestJobEvent.COMPLETED.toString()) @@ -434,9 +435,9 @@ public class KeywordHits implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); keywordResults.update(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. } } else if (eventType.equals(Case.Events.CURRENT_CASE.toString()) diff --git a/Core/src/org/sleuthkit/autopsy/diagnostics/PerformancePanel.java b/Core/src/org/sleuthkit/autopsy/diagnostics/PerformancePanel.java index 69a80462d8..58efd747b6 100644 --- a/Core/src/org/sleuthkit/autopsy/diagnostics/PerformancePanel.java +++ b/Core/src/org/sleuthkit/autopsy/diagnostics/PerformancePanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -299,7 +299,7 @@ public class PerformancePanel extends javax.swing.JDialog { Case curCase; try { - curCase = Case.getCurrentCase(); + curCase = Case.getOpenCase(); } catch (Exception e) { setImgLabel(NbBundle.getMessage(this.getClass(), "PerformancePanel.label.caseNotOpen.text")); setStatusMsg(""); @@ -380,7 +380,7 @@ public class PerformancePanel extends javax.swing.JDialog { Case curCase; try { - curCase = Case.getCurrentCase(); + curCase = Case.getOpenCase(); } catch (Exception e) { setFileReadLabel( NbBundle.getMessage(this.getClass(), "PerformancePanel.label.caseNotOpen.text")); @@ -472,7 +472,7 @@ public class PerformancePanel extends javax.swing.JDialog { Case curCase; try { - curCase = Case.getCurrentCase(); + curCase = Case.getOpenCase(); } catch (Exception e) { setDbLabel(NbBundle.getMessage(this.getClass(), "PerformancePanel.label.caseNotOpen.text")); return; diff --git a/Core/src/org/sleuthkit/autopsy/menuactions/DataContentDynamicMenu.java b/Core/src/org/sleuthkit/autopsy/menuactions/DataContentDynamicMenu.java index 40ae79064c..a677f8042a 100644 --- a/Core/src/org/sleuthkit/autopsy/menuactions/DataContentDynamicMenu.java +++ b/Core/src/org/sleuthkit/autopsy/menuactions/DataContentDynamicMenu.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,6 +27,7 @@ import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.windows.TopComponent; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; import org.sleuthkit.autopsy.corecomponents.DataContentTopComponent; @@ -51,9 +52,9 @@ class DataContentDynamicMenu extends JMenuItem implements DynamicMenuContent { defaultItem.addActionListener(new OpenTopComponentAction(contentWin)); try { - Case currentCase = Case.getCurrentCase(); + Case currentCase = Case.getOpenCase(); defaultItem.setEnabled(currentCase.hasData()); - } catch (IllegalStateException ex) { + } catch (NoCurrentCaseException ex) { defaultItem.setEnabled(false); // disable the menu when no case is opened } diff --git a/Core/src/org/sleuthkit/autopsy/menuactions/DataExplorerDynamicMenu.java b/Core/src/org/sleuthkit/autopsy/menuactions/DataExplorerDynamicMenu.java index 16776ecdf4..38377d0f28 100644 --- a/Core/src/org/sleuthkit/autopsy/menuactions/DataExplorerDynamicMenu.java +++ b/Core/src/org/sleuthkit/autopsy/menuactions/DataExplorerDynamicMenu.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,7 @@ import org.openide.awt.DynamicMenuContent; import org.openide.util.Lookup; import org.openide.windows.TopComponent; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataExplorer; /** @@ -54,9 +55,9 @@ class DataExplorerDynamicMenu extends JMenuItem implements DynamicMenuContent { item.addActionListener(new OpenTopComponentAction(explorerWin)); try { - Case currentCase = Case.getCurrentCase(); + Case currentCase = Case.getOpenCase(); item.setEnabled(currentCase.hasData()); - } catch (IllegalStateException ex) { + } catch (NoCurrentCaseException ex) { item.setEnabled(false); // disable the menu when no case is opened } diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java index d1a81663fe..eae41cd814 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +30,7 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -55,16 +56,23 @@ class TextMessageAnalyzer { private Blackboard blackboard; void findTexts(IngestJobContext context) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); + Case openCase; try { - SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } + blackboard = openCase.getServices().getBlackboard(); + try { + SleuthkitCase skCase = openCase.getSleuthkitCase(); absFiles = skCase.findAllFilesWhere("name ='mmssms.db'"); //NON-NLS //get exact file name if (absFiles.isEmpty()) { return; } for (AbstractFile AF : absFiles) { try { - jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), AF.getName().replaceAll("[<>%|\"/:*\\\\]", "")); + jFile = new java.io.File(openCase.getTempDirectory(), AF.getName().replaceAll("[<>%|\"/:*\\\\]", "")); ContentUtils.writeToFile(AF, jFile, context::dataSourceIngestIsCancelled); dbPath = jFile.toString(); //path of file as string fileId = AF.getId(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 3c8062355b..b382814f0d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014-2016 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -67,6 +67,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_ADDED; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; @@ -949,8 +950,8 @@ public class TimeLineController { * already closed. */ try { - Case.getCurrentCase(); - } catch (IllegalStateException notUsed) { + Case.getOpenCase(); + } catch (NoCurrentCaseException notUsed) { // Case is closed, do nothing. return; } From 342976d9c47dbe4af43208d01592f0048630260e Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 13:17:01 -0500 Subject: [PATCH 072/128] 2229: Part 11: Use getOpenCase() instead of getCurrentCase() in directorytree, report, timeline... --- .../ViewSourceArtifactAction.java | 7 +- .../autopsy/ingest/IngestManager.java | 107 +++++++++--------- .../autopsy/modules/iOS/CallLogAnalyzer.java | 24 +++- .../sleuthkit/autopsy/report/ReportExcel.java | 14 ++- .../autopsy/report/ReportGenerator.java | 14 ++- ...nterestingArtifactCreatorIngestModule.java | 11 +- .../timeline/TimeLineTopComponent.java | 5 +- .../timeline/explorernodes/EventNode.java | 7 +- .../timeline/explorernodes/EventRootNode.java | 3 +- .../timeline/ui/listvew/ListTimeline.java | 3 +- 10 files changed, 116 insertions(+), 79 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ViewSourceArtifactAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ViewSourceArtifactAction.java index a9d23a14de..04a2cccc2c 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ViewSourceArtifactAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ViewSourceArtifactAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,6 +23,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.TskCoreException; @@ -48,7 +49,7 @@ class ViewSourceArtifactAction extends AbstractAction { try { for (BlackboardAttribute attribute : artifact.getAttributes()) { if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT.getTypeID()) { - BlackboardArtifact associatedArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong()); + BlackboardArtifact associatedArtifact = Case.getOpenCase().getSleuthkitCase().getBlackboardArtifact(attribute.getValueLong()); if (associatedArtifact != null) { dirTree.viewArtifact(associatedArtifact); break; @@ -56,7 +57,7 @@ class ViewSourceArtifactAction extends AbstractAction { } } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.WARNING, "Unable to perform view artifact on an associated artifact of " + artifact.getDisplayName(), ex); //NON-NLS } } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java index 089ebfc53e..13d67f9e9c 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestManager.java @@ -50,6 +50,7 @@ import org.openide.util.Cancellable; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.ServicesMonitor; import org.sleuthkit.autopsy.core.UserPreferences; @@ -190,10 +191,10 @@ public class IngestManager { * only necessary for multi-user cases. */ try { - if (Case.getCurrentCase().getCaseType() != Case.CaseType.MULTI_USER_CASE) { + if (Case.getOpenCase().getCaseType() != Case.CaseType.MULTI_USER_CASE) { return; } - } catch (IllegalStateException noCaseOpenException) { + } catch (NoCurrentCaseException noCaseOpenException) { return; } @@ -251,13 +252,13 @@ public class IngestManager { caseIsOpen = true; clearIngestMessageBox(); try { - Case openedCase = Case.getCurrentCase(); + Case openedCase = Case.getOpenCase(); String channelPrefix = openedCase.getName(); if (Case.CaseType.MULTI_USER_CASE == openedCase.getCaseType()) { jobEventPublisher.openRemoteEventChannel(String.format(INGEST_JOB_EVENT_CHANNEL_NAME, channelPrefix)); moduleEventPublisher.openRemoteEventChannel(String.format(INGEST_MODULE_EVENT_CHANNEL_NAME, channelPrefix)); } - } catch (IllegalStateException | AutopsyEventException ex) { + } catch (NoCurrentCaseException | AutopsyEventException ex) { logger.log(Level.SEVERE, "Failed to open remote events channel", ex); //NON-NLS MessageNotifyUtil.Notify.error(NbBundle.getMessage(IngestManager.class, "IngestManager.OpenEventChannel.Fail.Title"), NbBundle.getMessage(IngestManager.class, "IngestManager.OpenEventChannel.Fail.ErrMsg")); @@ -347,61 +348,65 @@ public class IngestManager { }) private IngestJobStartResult startIngestJob(IngestJob job) { List errors = null; - if (caseIsOpen) { - if (Case.getCurrentCase().getCaseType() == Case.CaseType.MULTI_USER_CASE) { - try { - if (!servicesMonitor.getServiceStatus(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString()).equals(ServicesMonitor.ServiceStatus.UP.toString())) { - if (RuntimeProperties.runningWithGUI()) { - EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - String serviceDisplayName = ServicesMonitor.Service.REMOTE_CASE_DATABASE.getDisplayName(); - JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), - NbBundle.getMessage(this.getClass(), "IngestManager.cancellingIngest.msgDlg.text"), - NbBundle.getMessage(this.getClass(), "IngestManager.serviceIsDown.msgDlg.text", serviceDisplayName), - JOptionPane.ERROR_MESSAGE); - } - }); - } - return new IngestJobStartResult(null, new IngestManagerException("Ingest aborted. Remote database is down"), Collections.emptyList()); //NON-NLS + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + return new IngestJobStartResult(null, new IngestManagerException("Exception while getting open case.", ex), Collections.emptyList()); //NON-NLS + } + if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) { + try { + if (!servicesMonitor.getServiceStatus(ServicesMonitor.Service.REMOTE_CASE_DATABASE.toString()).equals(ServicesMonitor.ServiceStatus.UP.toString())) { + if (RuntimeProperties.runningWithGUI()) { + EventQueue.invokeLater(new Runnable() { + @Override + public void run() { + String serviceDisplayName = ServicesMonitor.Service.REMOTE_CASE_DATABASE.getDisplayName(); + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + NbBundle.getMessage(this.getClass(), "IngestManager.cancellingIngest.msgDlg.text"), + NbBundle.getMessage(this.getClass(), "IngestManager.serviceIsDown.msgDlg.text", serviceDisplayName), + JOptionPane.ERROR_MESSAGE); + } + }); } - } catch (ServicesMonitor.ServicesMonitorException ex) { - return new IngestJobStartResult(null, new IngestManagerException("Database server is down", ex), Collections.emptyList()); //NON-NLS + return new IngestJobStartResult(null, new IngestManagerException("Ingest aborted. Remote database is down"), Collections.emptyList()); //NON-NLS } + } catch (ServicesMonitor.ServicesMonitorException ex) { + return new IngestJobStartResult(null, new IngestManagerException("Database server is down", ex), Collections.emptyList()); //NON-NLS } + } - if (!ingestMonitor.isRunning()) { - ingestMonitor.start(); + if (!ingestMonitor.isRunning()) { + ingestMonitor.start(); + } + + ingestJobsById.put(job.getId(), job); + errors = job.start(); + if (errors.isEmpty()) { + this.fireIngestJobStarted(job.getId()); + IngestManager.logger.log(Level.INFO, "Ingest job {0} started", job.getId()); //NON-NLS + } else { + this.ingestJobsById.remove(job.getId()); + for (IngestModuleError error : errors) { + logger.log(Level.SEVERE, String.format("Error starting %s ingest module for job %d", error.getModuleDisplayName(), job.getId()), error.getThrowable()); //NON-NLS } - - ingestJobsById.put(job.getId(), job); - errors = job.start(); - if (errors.isEmpty()) { - this.fireIngestJobStarted(job.getId()); - IngestManager.logger.log(Level.INFO, "Ingest job {0} started", job.getId()); //NON-NLS - } else { - this.ingestJobsById.remove(job.getId()); + IngestManager.logger.log(Level.SEVERE, "Ingest job {0} could not be started", job.getId()); //NON-NLS + if (RuntimeProperties.runningWithGUI()) { + final StringBuilder message = new StringBuilder(1024); + message.append(Bundle.IngestManager_startupErr_dlgMsg()).append("\n"); //NON-NLS + message.append(Bundle.IngestManager_startupErr_dlgSolution()).append("\n\n"); //NON-NLS + message.append(Bundle.IngestManager_startupErr_dlgErrorList()).append("\n"); //NON-NLS for (IngestModuleError error : errors) { - logger.log(Level.SEVERE, String.format("Error starting %s ingest module for job %d", error.getModuleDisplayName(), job.getId()), error.getThrowable()); //NON-NLS + String moduleName = error.getModuleDisplayName(); + String errorMessage = error.getThrowable().getLocalizedMessage(); + message.append(moduleName).append(": ").append(errorMessage).append("\n"); //NON-NLS } - IngestManager.logger.log(Level.SEVERE, "Ingest job {0} could not be started", job.getId()); //NON-NLS - if (RuntimeProperties.runningWithGUI()) { - final StringBuilder message = new StringBuilder(1024); - message.append(Bundle.IngestManager_startupErr_dlgMsg()).append("\n"); //NON-NLS - message.append(Bundle.IngestManager_startupErr_dlgSolution()).append("\n\n"); //NON-NLS - message.append(Bundle.IngestManager_startupErr_dlgErrorList()).append("\n"); //NON-NLS - for (IngestModuleError error : errors) { - String moduleName = error.getModuleDisplayName(); - String errorMessage = error.getThrowable().getLocalizedMessage(); - message.append(moduleName).append(": ").append(errorMessage).append("\n"); //NON-NLS - } - message.append("\n\n"); - EventQueue.invokeLater(() -> { - JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), message, Bundle.IngestManager_startupErr_dlgTitle(), JOptionPane.ERROR_MESSAGE); - }); - } - return new IngestJobStartResult(null, new IngestManagerException("Errors occurred while starting ingest"), errors); //NON-NLS + message.append("\n\n"); + EventQueue.invokeLater(() -> { + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), message, Bundle.IngestManager_startupErr_dlgTitle(), JOptionPane.ERROR_MESSAGE); + }); } + return new IngestJobStartResult(null, new IngestManagerException("Errors occurred while starting ingest"), errors); //NON-NLS } return new IngestJobStartResult(job, null, errors); diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java index cfc7272315..e503158543 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -53,17 +54,24 @@ class CallLogAnalyzer { private Blackboard blackboard; public void findCallLogs(IngestJobContext context) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } + blackboard = openCase.getServices().getBlackboard(); List absFiles; try { - SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase skCase = openCase.getSleuthkitCase(); absFiles = skCase.findAllFilesWhere("name ='contacts2.db' OR name ='contacts.db'"); //NON-NLS //get exact file names if (absFiles.isEmpty()) { return; } for (AbstractFile AF : absFiles) { try { - jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), AF.getName().replaceAll("[<>%|\"/:*\\\\]", "")); + jFile = new java.io.File(openCase.getTempDirectory(), AF.getName().replaceAll("[<>%|\"/:*\\\\]", "")); ContentUtils.writeToFile(AF, jFile, context::dataSourceIngestIsCancelled); dbPath = jFile.toString(); //path of file as string fileId = AF.getId(); @@ -90,7 +98,13 @@ class CallLogAnalyzer { logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS } - Case currentCase = Case.getCurrentCase(); + Case currentCase; + try { + currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } SleuthkitCase skCase = currentCase.getSleuthkitCase(); try { AbstractFile f = skCase.getAbstractFileById(fId); diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java b/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java index 9158790ad6..b28ac5ade2 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportExcel.java @@ -27,6 +27,7 @@ import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.TskCoreException; @@ -112,13 +113,15 @@ class ReportExcel implements TableReportModule { try { out = new FileOutputStream(reportPath); wb.write(out); - Case.getCurrentCase().addReport(reportPath, NbBundle.getMessage(this.getClass(), + Case.getOpenCase().addReport(reportPath, NbBundle.getMessage(this.getClass(), "ReportExcel.endReport.srcModuleName.text"), ""); } catch (IOException ex) { logger.log(Level.SEVERE, "Failed to write Excel report.", ex); //NON-NLS } catch (TskCoreException ex) { String errorMessage = String.format("Error adding %s to case as a report", reportPath); //NON-NLS logger.log(Level.SEVERE, errorMessage, ex); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS } finally { if (out != null) { try { @@ -300,6 +303,13 @@ class ReportExcel implements TableReportModule { } private void writeSummaryWorksheet() { + Case currentCase; + try { + currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } sheet = wb.createSheet(NbBundle.getMessage(this.getClass(), "ReportExcel.sheetName.text")); rowIndex = 0; @@ -311,8 +321,6 @@ class ReportExcel implements TableReportModule { sheet.createRow(rowIndex); ++rowIndex; - Case currentCase = Case.getCurrentCase(); - row = sheet.createRow(rowIndex); row.setRowStyle(setStyle); row.createCell(0).setCellValue(NbBundle.getMessage(this.getClass(), "ReportExcel.cellVal.caseName")); diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java index ab373e679a..127f537df4 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportGenerator.java @@ -41,6 +41,7 @@ import org.openide.filesystems.FileUtil; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus; @@ -54,8 +55,6 @@ class ReportGenerator { private static final Logger logger = Logger.getLogger(ReportGenerator.class.getName()); - private Case currentCase = Case.getCurrentCase(); - /** * Progress reportGenerationPanel that can be used to check for cancellation. */ @@ -229,10 +228,10 @@ class ReportGenerator { private List getFiles() { List absFiles; try { - SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase skCase = Case.getOpenCase().getSleuthkitCase(); absFiles = skCase.findAllFilesWhere("meta_type != " + TskData.TSK_FS_META_TYPE_ENUM.TSK_FS_META_TYPE_DIR.getValue()); //NON-NLS return absFiles; - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { MessageNotifyUtil.Notify.show( NbBundle.getMessage(this.getClass(), "ReportGenerator.errors.reportErrorTitle"), NbBundle.getMessage(this.getClass(), "ReportGenerator.errors.reportErrorText") + ex.getLocalizedMessage(), @@ -252,7 +251,12 @@ class ReportGenerator { } private static String createReportDirectory(ReportModule module) throws IOException { - Case currentCase = Case.getCurrentCase(); + Case currentCase; + try { + currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + throw new IOException("Exception while getting open case.", ex); + } // Create the root reports directory path of the form: /Reports/ / DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss"); Date date = new Date(); diff --git a/Core/src/org/sleuthkit/autopsy/test/InterestingArtifactCreatorIngestModule.java b/Core/src/org/sleuthkit/autopsy/test/InterestingArtifactCreatorIngestModule.java index a57623f02e..f56d4cb043 100644 --- a/Core/src/org/sleuthkit/autopsy/test/InterestingArtifactCreatorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/test/InterestingArtifactCreatorIngestModule.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,7 @@ import java.util.logging.Level; import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter; @@ -53,10 +54,10 @@ final class InterestingArtifactCreatorIngestModule extends FileIngestModuleAdapt @Override public void startUp(IngestJobContext context) throws IngestModuleException { - Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); try { + Blackboard blackboard = Case.getOpenCase().getServices().getBlackboard(); artifactType = blackboard.getOrAddArtifactType(INT_ARTIFACT_TYPE_NAME, INT_ARTIFACT_DISPLAY_NAME); - } catch (Blackboard.BlackboardException ex) { + } catch (Blackboard.BlackboardException | NoCurrentCaseException ex) { throw new IngestModuleException(Bundle.InterestingArtifactCreatorIngestModule_exceptionMessage_errorCreatingCustomType(), ex); } } @@ -76,7 +77,7 @@ final class InterestingArtifactCreatorIngestModule extends FileIngestModuleAdapt * type. */ int randomArtIndex = (int) (Math.random() * 3); - Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); + Blackboard blackboard = Case.getOpenCase().getServices().getBlackboard(); BlackboardArtifact.Type artifactTypeBase = blackboard.getOrAddArtifactType(ARTIFACT_TYPE_NAMES[randomArtIndex], ARTIFACT_DISPLAY_NAMES[randomArtIndex]); BlackboardArtifact artifactBase = file.newArtifact(artifactTypeBase.getTypeID()); Collection baseAttributes = new ArrayList<>(); @@ -123,7 +124,7 @@ final class InterestingArtifactCreatorIngestModule extends FileIngestModuleAdapt attributes.add(att3); attributes.add(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT, MODULE_NAME, artifactBase.getArtifactID())); artifact.addAttributes(attributes); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, String.format("Failed to process file (obj_id = %d)", file.getId()), ex); return ProcessResult.ERROR; } catch (Blackboard.BlackboardException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index 70933143c2..2b0bf5d383 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -51,6 +51,7 @@ import org.openide.windows.RetainLocation; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.actions.AddBookmarkTagAction; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponents.DataContentPanel; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; @@ -132,7 +133,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer contentViewerPanel.setNode(null); } }); - } catch (IllegalStateException ex) { + } catch (NoCurrentCaseException ex) { //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS } catch (TskCoreException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java index cf0524a893..d8f0bf9ac3 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,6 +33,7 @@ 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.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory; @@ -215,14 +216,14 @@ public class EventNode extends DisplayableItemNode { * @return An EventNode with the file (and artifact) backing this event in * its lookup. */ - public static EventNode createEventNode(final Long eventID, FilteredEventsModel eventsModel) throws TskCoreException, IllegalStateException { + public static EventNode createEventNode(final Long eventID, FilteredEventsModel eventsModel) throws TskCoreException, NoCurrentCaseException { /* * Look up the event by id and creata an EventNode with the appropriate * data in the lookup. */ final SingleEvent eventById = eventsModel.getEventById(eventID); - SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase sleuthkitCase = Case.getOpenCase().getSleuthkitCase(); AbstractFile file = sleuthkitCase.getAbstractFileById(eventById.getFileID()); if (eventById.getArtifactID().isPresent()) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java index 1b35d90188..3924918d06 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java @@ -29,6 +29,7 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; @@ -130,7 +131,7 @@ public class EventRootNode extends DisplayableItemNode { } else { try { return EventNode.createEventNode(eventID, filteredEvents); - } catch (IllegalStateException ex) { + } catch (NoCurrentCaseException ex) { //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS return null; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java index f7bfba4501..7515891356 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -78,6 +78,7 @@ import org.controlsfx.control.action.ActionUtils; import org.openide.awt.Actions; import org.openide.util.NbBundle; import org.openide.util.actions.Presenter; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; @@ -668,7 +669,7 @@ class ListTimeline extends BorderPane { //show new context menu. new ContextMenu(menuItems.toArray(new MenuItem[menuItems.size()])) .show(this, contextMenuEvent.getScreenX(), contextMenuEvent.getScreenY()); - } catch (IllegalStateException ex) { + } catch (NoCurrentCaseException ex) { //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); //NON-NLS } catch (TskCoreException ex) { From 9b9d366a97a47dd0a67427f2dab064087fc39b1a Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 13:54:24 -0500 Subject: [PATCH 073/128] 2229: Part 12: Use getOpenCase() instead of getCurrentCase() in KeywordSearch. --- .../keywordsearch/ArtifactTextExtractor.java | 9 +++++---- .../autopsy/keywordsearch/KeywordHit.java | 10 ++++++++-- .../keywordsearch/KeywordSearchIngestModule.java | 14 +++++++++++--- .../keywordsearch/KeywordSearchResultFactory.java | 5 +++-- .../autopsy/keywordsearch/QueryResults.java | 7 ++++--- .../autopsy/keywordsearch/RegexQuery.java | 7 ++++--- .../autopsy/keywordsearch/TermsComponentQuery.java | 8 ++++---- 7 files changed, 39 insertions(+), 21 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ArtifactTextExtractor.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ArtifactTextExtractor.java index 962e5ba245..61b9bb3b20 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ArtifactTextExtractor.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ArtifactTextExtractor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,7 @@ import java.nio.charset.StandardCharsets; import java.util.logging.Level; import org.apache.commons.io.IOUtils; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; @@ -57,9 +58,9 @@ class ArtifactTextExtractor implements TextExtractor { Case currentCase; try { - currentCase = Case.getCurrentCase(); - } catch (IllegalStateException ignore) { - // thorown by Case.getCurrentCase() if currentCase is null + currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ignore) { + // thorown by Case.getOpenCase() if currentCase is null return null; } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java index f9284e81d9..c45b70c863 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordHit.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +24,7 @@ import java.util.Comparator; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; @@ -116,7 +117,12 @@ class KeywordHit implements Comparable { long getContentID() throws TskCoreException { if (isArtifactHit()) { // If the hit was in an artifact, look up the source content for the artifact. - SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase caseDb; + try { + caseDb = Case.getOpenCase().getSleuthkitCase(); + } catch (NoCurrentCaseException ex) { + throw new TskCoreException("Exception while getting open case.", ex); + } try (SleuthkitCase.CaseDbQuery executeQuery = caseDb.executeQuery(GET_CONTENT_ID_FROM_ARTIFACT_ID + this.solrObjectId); ResultSet resultSet = executeQuery.getResultSet();) { diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index 81ef0e614a..83cdafb88a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -27,6 +27,7 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -144,7 +145,8 @@ public final class KeywordSearchIngestModule implements FileIngestModule { @Messages({ "KeywordSearchIngestModule.startupMessage.failedToGetIndexSchema=Failed to get schema version for text index.", "# {0} - Solr version number", "KeywordSearchIngestModule.startupException.indexSolrVersionNotSupported=Adding text no longer supported for Solr version {0} of the text index.", - "# {0} - schema version number", "KeywordSearchIngestModule.startupException.indexSchemaNotSupported=Adding text no longer supported for schema version {0} of the text index." + "# {0} - schema version number", "KeywordSearchIngestModule.startupException.indexSchemaNotSupported=Adding text no longer supported for schema version {0} of the text index.", + "KeywordSearchIngestModule.noOpenCase.errMsg=No open case available." }) @Override public void startUp(IngestJobContext context) throws IngestModuleException { @@ -180,11 +182,17 @@ public final class KeywordSearchIngestModule implements FileIngestModule { // increment the module reference count // if first instance of this module for this job then check the server and existence of keywords + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + throw new IngestModuleException(Bundle.KeywordSearchIngestModule_noOpenCase_errMsg(), ex); + } if (refCounter.incrementAndGet(jobId) == 1) { - if (Case.getCurrentCase().getCaseType() == Case.CaseType.MULTI_USER_CASE) { + if (openCase.getCaseType() == Case.CaseType.MULTI_USER_CASE) { // for multi-user cases need to verify connection to remore SOLR server KeywordSearchService kwsService = new SolrSearchService(); - Server.IndexingServerProperties properties = Server.getMultiUserServerProperties(Case.getCurrentCase().getCaseDirectory()); + Server.IndexingServerProperties properties = Server.getMultiUserServerProperties(openCase.getCaseDirectory()); int port; try { port = Integer.parseInt(properties.getPort()); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java index fd15fb0cf2..290fe8b086 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchResultFactory.java @@ -39,6 +39,7 @@ import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode; @@ -147,8 +148,8 @@ class KeywordSearchResultFactory extends ChildFactory { } SleuthkitCase tskCase; try { - tskCase = Case.getCurrentCase().getSleuthkitCase(); - } catch (IllegalStateException ex) { + tskCase = Case.getOpenCase().getSleuthkitCase(); + } catch (NoCurrentCaseException ex) { logger.log(Level.SEVERE, "There was no case open.", ex); //NON-NLS return false; } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java index 585424b3ec..b4e07d0d8a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/QueryResults.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,6 +32,7 @@ import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.aggregate.ProgressContributor; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestMessage; @@ -212,9 +213,9 @@ class QueryResults { */ Content content = null; try { - SleuthkitCase tskCase = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase tskCase = Case.getOpenCase().getSleuthkitCase(); content = tskCase.getContentById(hit.getContentID()); - } catch (TskCoreException | IllegalStateException tskCoreException) { + } catch (TskCoreException | NoCurrentCaseException tskCoreException) { logger.log(Level.SEVERE, "Failed to get text source object for ", tskCoreException); //NON-NLS } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java index 306502603c..ad0fc0a678 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/RegexQuery.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,6 +39,7 @@ import org.apache.solr.common.params.CursorMarkParams; import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.datamodel.CreditCards; @@ -590,11 +591,11 @@ final class RegexQuery implements KeywordSearchQuery { * Create an account instance. */ try { - AccountFileInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString() , MODULE_NAME, content); + AccountFileInstance ccAccountInstance = Case.getOpenCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString() , MODULE_NAME, content); ccAccountInstance.addAttributes(attributes); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java index d42f0b5b64..b2d1ef37e1 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TermsComponentQuery.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,6 +34,7 @@ import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.response.TermsResponse.Term; import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.autopsy.datamodel.CreditCards; @@ -494,10 +495,9 @@ final class TermsComponentQuery implements KeywordSearchQuery { * Create an account. */ try { - AccountFileInstance ccAccountInstance = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString(), MODULE_NAME, content); + AccountFileInstance ccAccountInstance = Case.getOpenCase().getSleuthkitCase().getCommunicationsManager().createAccountFileInstance(Account.Type.CREDIT_CARD, ccnAttribute.getValueString(), MODULE_NAME, content); ccAccountInstance.addAttributes(attributes); - //newArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(ccAccountInstance.getArtifactId()); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.SEVERE, "Error creating CCN account instance", ex); //NON-NLS } From fa648a9357504d6b983e1a546197b57f404ace80 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 14:26:06 -0500 Subject: [PATCH 074/128] 2229: Part 13: Use getOpenCase() instead of getCurrentCase() in test, timeline, report... --- .../AddTaggedHashesToHashDbConfigPanel.java | 6 +++++- .../sleuthkit/autopsy/test/CustomArtifactType.java | 7 ++++--- ...ustomArtifactsCreatorDataSourceIngestModule.java | 5 +++-- .../CustomArtifactsCreatorFileIngestModule.java | 5 +++-- .../autopsy/timeline/OpenTimelineAction.java | 13 +++++++------ .../timeline/actions/SaveSnapshotAsReport.java | 7 ++++--- .../datamodel/CentralRepoDatamodelTest.java | 9 +++++---- .../keywordsearch/ExtractedContentViewer.java | 7 ++++--- 8 files changed, 35 insertions(+), 24 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java index be89b1ea96..9bb4b00460 100644 --- a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDbConfigPanel.java @@ -34,6 +34,7 @@ import javax.swing.ListCellRenderer; import javax.swing.ListModel; import javax.swing.event.ListDataListener; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager; @@ -67,10 +68,13 @@ class AddTaggedHashesToHashDbConfigPanel extends javax.swing.JPanel { private void populateTagNameComponents() { // Get the tag names in use for the current case. try { - tagNames = Case.getCurrentCase().getServices().getTagsManager().getTagNamesInUse(); + tagNames = Case.getOpenCase().getServices().getTagsManager().getTagNamesInUse(); } catch (TskCoreException ex) { Logger.getLogger(AddTaggedHashesToHashDbConfigPanel.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); JOptionPane.showMessageDialog(this, "Error getting tag names for case.", "Tag Names Not Found", JOptionPane.ERROR_MESSAGE); + } catch (NoCurrentCaseException ex) { + Logger.getLogger(AddTaggedHashesToHashDbConfigPanel.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); + JOptionPane.showMessageDialog(this, "Error getting tag names for case.", "Exception while getting open case.", JOptionPane.ERROR_MESSAGE); } // Mark the tag names as unselected. Note that tagNameSelections is a diff --git a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java index ed0c216ac8..adb366878c 100644 --- a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java +++ b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.List; import javax.xml.bind.DatatypeConverter; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; @@ -63,8 +64,8 @@ final class CustomArtifactType { * * @throws BlackboardException If there is an error adding any of the types. */ - static void addToCaseDatabase() throws Blackboard.BlackboardException { - Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); + static void addToCaseDatabase() throws Blackboard.BlackboardException, NoCurrentCaseException { + Blackboard blackboard = Case.getOpenCase().getServices().getBlackboard(); artifactType = blackboard.getOrAddArtifactType(ARTIFACT_TYPE_NAME, ARTIFACT_DISPLAY_NAME); intAttrType = blackboard.getOrAddAttributeType(INT_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.INTEGER, INT_ATTR_DISPLAY_NAME); doubleAttrType = blackboard.getOrAddAttributeType(DOUBLE_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DOUBLE, DOUBLE_ATTR_DISPLAY_NAME); diff --git a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactsCreatorDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactsCreatorDataSourceIngestModule.java index a6026357d1..8293ba934e 100644 --- a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactsCreatorDataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactsCreatorDataSourceIngestModule.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.test; import java.util.logging.Level; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleAdapter; @@ -53,7 +54,7 @@ public class CustomArtifactsCreatorDataSourceIngestModule extends DataSourceInge public void startUp(IngestJobContext context) throws IngestModuleException { try { CustomArtifactType.addToCaseDatabase(); - } catch (Blackboard.BlackboardException ex) { + } catch (Blackboard.BlackboardException | NoCurrentCaseException ex) { throw new IngestModuleException(Bundle.CustomArtifactsCreatorDataSourceIngestModule_exceptionMessage_errorCreatingCustomType(), ex); } } diff --git a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactsCreatorFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactsCreatorFileIngestModule.java index 752b007778..abd6f0d00d 100644 --- a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactsCreatorFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactsCreatorFileIngestModule.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.test; import java.util.logging.Level; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter; @@ -52,7 +53,7 @@ final class CustomArtifactsCreatorFileIngestModule extends FileIngestModuleAdapt public void startUp(IngestJobContext context) throws IngestModuleException { try { CustomArtifactType.addToCaseDatabase(); - } catch (Blackboard.BlackboardException ex) { + } catch (Blackboard.BlackboardException | NoCurrentCaseException ex) { throw new IngestModuleException(Bundle.CustomArtifactsCreatorFileIngestModule_exceptionMessage_errorCreatingCustomType(), ex); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java index aca5c4e7f7..c6e995c195 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/OpenTimelineAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014-2017 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,6 +33,7 @@ import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.Installer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -80,7 +81,7 @@ public final class OpenTimelineAction extends CallableSystemAction { @Override public boolean isEnabled() { /** - * We used to also check if Case.getCurrentCase().hasData() was true. We + * We used to also check if Case.getOpenCase().hasData() was true. We * disabled that check because if it is executed while a data source is * being added, it blocks the edt. We still do that in ImageGallery. */ @@ -111,7 +112,7 @@ public final class OpenTimelineAction extends CallableSystemAction { "OpenTimeLineAction.msgdlg.text=Could not create timeline, there are no data sources."}) synchronized private void showTimeline(AbstractFile file, BlackboardArtifact artifact) { try { - Case currentCase = Case.getCurrentCase(); + Case currentCase = Case.getOpenCase(); if (currentCase.hasData() == false) { MessageNotifyUtil.Message.info(Bundle.OpenTimeLineAction_msgdlg_text()); logger.log(Level.INFO, "Could not create timeline, there are no data sources.");// NON-NLS @@ -131,7 +132,7 @@ public final class OpenTimelineAction extends CallableSystemAction { MessageNotifyUtil.Message.error(Bundle.OpenTimelineAction_settingsErrorMessage()); logger.log(Level.SEVERE, "Failed to initialize per case timeline settings.", iOException); } - } catch (IllegalStateException e) { + } catch (NoCurrentCaseException e) { //there is no case... Do nothing. } } @@ -212,8 +213,8 @@ public final class OpenTimelineAction extends CallableSystemAction { private boolean tooManyFiles() { try { - return FILE_LIMIT < Case.getCurrentCase().getSleuthkitCase().countFilesWhere("1 = 1"); - } catch (IllegalStateException ex) { + return FILE_LIMIT < Case.getOpenCase().getSleuthkitCase().countFilesWhere("1 = 1"); + } catch (NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Can not open timeline with no case open.", ex); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error counting files in the DB.", ex); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java index d9226b587a..e0b3a3e333 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java @@ -47,6 +47,7 @@ import org.controlsfx.validation.Validator; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.timeline.PromptDialogManager; @@ -152,12 +153,12 @@ public class SaveSnapshotAsReport extends Action { try { //add main file as report to case - Case.getCurrentCase().addReport(reportMainFilePath.toString(), Bundle.Timeline_ModuleName(), reportName); - } catch (TskCoreException ex) { + Case.getOpenCase().addReport(reportMainFilePath.toString(), Bundle.Timeline_ModuleName(), reportName); + } catch (TskCoreException | NoCurrentCaseException ex) { LOGGER.log(Level.WARNING, "Failed to add " + reportMainFilePath.toString() + " to case as a report", ex); //NON_NLS new Alert(Alert.AlertType.ERROR, Bundle.SaveSnapShotAsReport_FailedToAddReport()).show(); return; - } + } //notify user of report location final Alert alert = new Alert(Alert.AlertType.INFORMATION, null, OPEN, OK); diff --git a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java index cce1e807e1..105f13146a 100644 --- a/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java +++ b/Core/test/qa-functional/src/org/sleuthkit/autopsy/centralrepository/datamodel/CentralRepoDatamodelTest.java @@ -41,6 +41,7 @@ import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.datamodel.TskData; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * @@ -2343,9 +2344,9 @@ public class CentralRepoDatamodelTest extends TestCase { // Test creating a case from an Autopsy case // The case may already be in the database - the result is the same either way try { - caseB = EamDb.getInstance().newCase(Case.getCurrentCase()); + caseB = EamDb.getInstance().newCase(Case.getOpenCase()); assertTrue("Failed to create correlation case from Autopsy case", caseB != null); - } catch (EamDbException ex) { + } catch (EamDbException | NoCurrentCaseException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); return; @@ -2412,9 +2413,9 @@ public class CentralRepoDatamodelTest extends TestCase { // Test getting a case from an Autopsy case try { - CorrelationCase tempCase = EamDb.getInstance().getCase(Case.getCurrentCase()); + CorrelationCase tempCase = EamDb.getInstance().getCase(Case.getOpenCase()); assertTrue("getCase returned null for current Autopsy case", tempCase != null); - } catch (EamDbException ex) { + } catch (EamDbException | NoCurrentCaseException ex) { Exceptions.printStackTrace(ex); Assert.fail(ex); } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java index 08b2d0a7bb..88163b357c 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java @@ -31,6 +31,7 @@ import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.keywordsearch.KeywordSearchResultFactory.AdHocQueryResult; @@ -176,7 +177,7 @@ public class ExtractedContentViewer implements DataContentViewer { if (rawArtifactText != null) { sources.add(rawArtifactText); } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Error creating RawText for " + file, ex); //NON-NLS } @@ -206,7 +207,7 @@ public class ExtractedContentViewer implements DataContentViewer { } - static private IndexedText getRawArtifactText(BlackboardArtifact artifact) throws TskCoreException { + static private IndexedText getRawArtifactText(BlackboardArtifact artifact) throws TskCoreException, NoCurrentCaseException { IndexedText rawArtifactText = null; if (null != artifact) { /* @@ -219,7 +220,7 @@ public class ExtractedContentViewer implements DataContentViewer { BlackboardAttribute attribute = artifact.getAttribute(TSK_ASSOCIATED_ARTIFACT_TYPE); if (attribute != null) { long artifactId = attribute.getValueLong(); - BlackboardArtifact associatedArtifact = Case.getCurrentCase().getSleuthkitCase().getBlackboardArtifact(artifactId); + BlackboardArtifact associatedArtifact = Case.getOpenCase().getSleuthkitCase().getBlackboardArtifact(artifactId); rawArtifactText = new RawText(associatedArtifact, associatedArtifact.getArtifactID()); } From 4480b37e8dcee7f9a03af8d3a096105ee1265ec3 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 15:53:57 -0500 Subject: [PATCH 075/128] 2229: Part 14: Use getOpenCase() instead of getCurrentCase() in report and stix. --- .../modules/stix/StixArtifactData.java | 21 +++++++++++++------ .../report/ArtifactSelectionDialog.java | 5 ++++- .../autopsy/report/ReportBodyFile.java | 13 +++++++++--- .../sleuthkit/autopsy/report/ReportHTML.java | 21 +++++++++++++++---- .../sleuthkit/autopsy/report/ReportKML.java | 8 ++++++- .../autopsy/report/ReportVisualPanel2.java | 12 ++++++----- .../taggedhashes/AddTaggedHashesToHashDb.java | 13 ++++++++++-- 7 files changed, 71 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java b/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java index afc84d87aa..cf4c4a3aea 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.logging.Level; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -49,20 +50,28 @@ class StixArtifactData { } public StixArtifactData(long a_objId, String a_observableId, String a_objType) { - Case case1 = Case.getCurrentCase(); - SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); try { + Case case1 = Case.getOpenCase(); + SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); file = sleuthkitCase.getAbstractFileById(a_objId); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { file = null; } observableId = a_observableId; objType = a_objType; } - @Messages({"StixArtifactData.indexError.message=Failed to index STIX interesting file hit artifact for keyword search."}) + @Messages({"StixArtifactData.indexError.message=Failed to index STIX interesting file hit artifact for keyword search.", + "StixArtifactData.noOpenCase.errMsg=No open case available."}) public void createArtifact(String a_title) throws TskCoreException { - Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); + Blackboard blackboard; + try { + blackboard = Case.getOpenCase().getServices().getBlackboard(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + MessageNotifyUtil.Notify.error(Bundle.StixArtifactData_noOpenCase_errMsg(), ex.getLocalizedMessage()); + return; + } String setName; if (a_title != null) { diff --git a/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java b/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java index 3af9d2112d..300a810def 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java +++ b/Core/src/org/sleuthkit/autopsy/report/ArtifactSelectionDialog.java @@ -36,6 +36,7 @@ import javax.swing.ListModel; import javax.swing.event.ListDataListener; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.TskCoreException; @@ -74,7 +75,7 @@ public class ArtifactSelectionDialog extends javax.swing.JDialog { BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getLabel(), BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getDisplayName())); // output is too unstructured for table review - artifactTypes = Case.getCurrentCase().getSleuthkitCase().getArtifactTypesInUse(); + artifactTypes = Case.getOpenCase().getSleuthkitCase().getArtifactTypesInUse(); artifactTypes.removeAll(doNotReport); Collections.sort(artifactTypes, new Comparator() { @Override @@ -89,6 +90,8 @@ public class ArtifactSelectionDialog extends javax.swing.JDialog { } } catch (TskCoreException ex) { Logger.getLogger(ArtifactSelectionDialog.class.getName()).log(Level.SEVERE, "Error getting list of artifacts in use: {0}", ex.getLocalizedMessage()); //NON-NLS + } catch (NoCurrentCaseException ex) { + Logger.getLogger(ArtifactSelectionDialog.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex.getLocalizedMessage()); //NON-NLS } } diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java b/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java index 33609f7333..3a601fdd7f 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportBodyFile.java @@ -31,6 +31,7 @@ import javax.swing.JPanel; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus; @@ -73,11 +74,17 @@ class ReportBodyFile implements GeneralReportModule { @SuppressWarnings("deprecation") public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) { // Start the progress bar and setup the report + try { + currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); + return; + } progressPanel.setIndeterminate(false); progressPanel.start(); progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportBodyFile.progress.querying")); reportPath = baseReportDir + getRelativeFilePath(); //NON-NLS - currentCase = Case.getCurrentCase(); + skCase = currentCase.getSleuthkitCase(); // Run query to get all files @@ -154,14 +161,14 @@ class ReportBodyFile implements GeneralReportModule { if (out != null) { out.flush(); out.close(); - Case.getCurrentCase().addReport(reportPath, + Case.getOpenCase().addReport(reportPath, NbBundle.getMessage(this.getClass(), "ReportBodyFile.generateReport.srcModuleName.text"), ""); } } catch (IOException ex) { logger.log(Level.WARNING, "Could not flush and close the BufferedWriter.", ex); //NON-NLS - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { String errorMessage = String.format("Error adding %s to case as a report", reportPath); //NON-NLS logger.log(Level.SEVERE, errorMessage, ex); } diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index 01a179a370..fdc0457b1c 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -49,6 +49,7 @@ import javax.imageio.ImageIO; import org.openide.filesystems.FileUtil; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.EscapeUtil; @@ -102,8 +103,8 @@ class ReportHTML implements TableReportModule { } // Refesh the member variables - private void refresh() { - currentCase = Case.getCurrentCase(); + private void refresh() throws NoCurrentCaseException { + currentCase = Case.getOpenCase(); skCase = currentCase.getSleuthkitCase(); dataTypes = new TreeMap<>(); @@ -327,7 +328,12 @@ class ReportHTML implements TableReportModule { @Override public void startReport(String baseReportDir) { // Refresh the HTML report - refresh(); + try { + refresh(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case."); //NON-NLS + return; + } // Setup the path for the HTML report this.path = baseReportDir; //NON-NLS this.subPath = this.path + HTML_SUBDIR + File.separator; @@ -882,6 +888,13 @@ class ReportHTML implements TableReportModule { private void writeIndex() { Writer indexOut = null; String indexFilePath = path + "report.html"; //NON-NLS + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } try { indexOut = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(indexFilePath), "UTF-8")); //NON-NLS StringBuilder index = new StringBuilder(); @@ -909,7 +922,7 @@ class ReportHTML implements TableReportModule { index.append("\n"); //NON-NLS index.append(""); //NON-NLS indexOut.write(index.toString()); - Case.getCurrentCase().addReport(indexFilePath, NbBundle.getMessage(this.getClass(), + openCase.addReport(indexFilePath, NbBundle.getMessage(this.getClass(), "ReportHTML.writeIndex.srcModuleName.text"), ""); } catch (IOException ex) { logger.log(Level.SEVERE, "Error creating Writer for report.html: {0}", ex); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java index f089855e0c..174a5a0367 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java @@ -43,6 +43,7 @@ import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; import org.jdom2.CDATA; import org.openide.filesystems.FileUtil; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * Generates a KML file based on geospatial information from the BlackBoard. @@ -98,7 +99,12 @@ class ReportKML implements GeneralReportModule { */ @Override public void generateReport(String baseReportDir, ReportProgressPanel progressPanel) { - + try { + currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } // Start the progress bar and setup the report progressPanel.setIndeterminate(true); progressPanel.start(); diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java b/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java index 53422c8f0d..0c291f9d15 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportVisualPanel2.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2014 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,6 +41,7 @@ import javax.swing.event.ListDataListener; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -97,8 +98,8 @@ final class ReportVisualPanel2 extends JPanel { private void initTags() { List tagNamesInUse; try { - tagNamesInUse = Case.getCurrentCase().getServices().getTagsManager().getTagNamesInUse(); - } catch (TskCoreException ex) { + tagNamesInUse = Case.getOpenCase().getServices().getTagsManager().getTagNamesInUse(); + } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(ReportVisualPanel2.class.getName()).log(Level.SEVERE, "Failed to get tag names", ex); //NON-NLS return; } @@ -136,6 +137,7 @@ final class ReportVisualPanel2 extends JPanel { private void initArtifactTypes() { try { + Case openCase = Case.getOpenCase(); ArrayList doNotReport = new ArrayList<>(); doNotReport.add(new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO.getTypeID(), BlackboardArtifact.ARTIFACT_TYPE.TSK_GEN_INFO.getLabel(), @@ -144,7 +146,7 @@ final class ReportVisualPanel2 extends JPanel { BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getLabel(), BlackboardArtifact.ARTIFACT_TYPE.TSK_TOOL_OUTPUT.getDisplayName())); // output is too unstructured for table review - artifacts = Case.getCurrentCase().getSleuthkitCase().getArtifactTypesInUse(); + artifacts = openCase.getSleuthkitCase().getArtifactTypesInUse(); artifacts.removeAll(doNotReport); @@ -152,7 +154,7 @@ final class ReportVisualPanel2 extends JPanel { for (BlackboardArtifact.Type type : artifacts) { artifactStates.put(type, Boolean.TRUE); } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(ReportVisualPanel2.class.getName()).log(Level.SEVERE, "Error getting list of artifacts in use: " + ex.getLocalizedMessage(), ex); //NON-NLS } } diff --git a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java index 283425c5fc..f64a14306f 100644 --- a/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java +++ b/Core/src/org/sleuthkit/autopsy/report/taggedhashes/AddTaggedHashesToHashDb.java @@ -27,6 +27,7 @@ import javax.swing.JPanel; import org.openide.util.lookup.ServiceProvider; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; import org.sleuthkit.autopsy.report.GeneralReportModule; @@ -66,6 +67,14 @@ public class AddTaggedHashesToHashDb implements GeneralReportModule { @Override public void generateReport(String reportPath, ReportProgressPanel progressPanel) { + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + Logger.getLogger(AddTaggedHashesToHashDb.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), "No open Case", "Exception while getting open case.", JOptionPane.ERROR_MESSAGE); + return; + } progressPanel.setIndeterminate(true); progressPanel.start(); progressPanel.updateStatusLabel("Adding hashes..."); @@ -74,7 +83,7 @@ public class AddTaggedHashesToHashDb implements GeneralReportModule { if (hashSet != null) { progressPanel.updateStatusLabel("Adding hashes to " + hashSet.getHashSetName() + " hash set..."); - TagsManager tagsManager = Case.getCurrentCase().getServices().getTagsManager(); + TagsManager tagsManager = openCase.getServices().getTagsManager(); List tagNames = configPanel.getSelectedTagNames(); ArrayList failedExports = new ArrayList<>(); for (TagName tagName : tagNames) { @@ -91,7 +100,7 @@ public class AddTaggedHashesToHashDb implements GeneralReportModule { if (content instanceof AbstractFile) { if (null != ((AbstractFile) content).getMd5Hash()) { try { - hashSet.addHashes(tag.getContent(), Case.getCurrentCase().getDisplayName()); + hashSet.addHashes(tag.getContent(), openCase.getDisplayName()); } catch (TskCoreException ex) { Logger.getLogger(AddTaggedHashesToHashDb.class.getName()).log(Level.SEVERE, "Error adding hash for obj_id = " + tag.getContent().getId() + " to hash set " + hashSet.getHashSetName(), ex); failedExports.add(tag.getContent().getName()); From fb32a6c1c03c700766db3e25fb35ca14739c0f39 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 16:22:42 -0500 Subject: [PATCH 076/128] 2229: Part 15: Use getOpenCase() instead of getCurrentCase() in corecomponents, coreutils, ios... --- .../casemodule/services/TagNameDefinition.java | 9 ++++++--- .../corecomponents/DataContentTopComponent.java | 11 +++++++++-- .../autopsy/corecomponents/DataResultPanel.java | 15 +++++++++++---- .../corecomponents/DataResultTopComponent.java | 9 ++++++++- .../sleuthkit/autopsy/coreutils/ImageUtils.java | 7 ++++--- .../datasourceprocessors/AddRawImageTask.java | 16 +++++++++++++--- .../autopsy/modules/iOS/TextMessageAnalyzer.java | 8 +++++++- 7 files changed, 58 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java index dba0d7aac4..5c978f6378 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagNameDefinition.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +28,7 @@ import java.util.logging.Level; import javax.annotation.concurrent.Immutable; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.datamodel.TagName; @@ -295,9 +296,11 @@ final class TagNameDefinition implements Comparable { setting.append(";"); } setting.append(tagName.toSettingsFormat()); - if (Case.isCaseOpen()) { - SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase(); + try { + SleuthkitCase caseDb = Case.getOpenCase().getSleuthkitCase(); tagName.saveToCase(caseDb); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); } } ModuleSettings.setConfigSetting(TAGS_SETTINGS_NAME, TAG_NAMES_SETTING_KEY, setting.toString()); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java index 6f7c87ad0b..c63c79ac40 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +28,7 @@ import org.openide.util.NbBundle; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; import org.sleuthkit.autopsy.coreutils.Logger; @@ -168,7 +169,13 @@ public final class DataContentTopComponent extends TopComponent implements DataC * the main window, only it to be closed when there's no case opened or * no data sources in the open case. */ - return (!this.isDefault) || !Case.isCaseOpen() || Case.getCurrentCase().hasData() == false; + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + return false; + } + return (!this.isDefault) || openCase.hasData() == false; } @Override diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java index e89d677ad8..ef8eb1c5b6 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,6 +37,7 @@ import org.openide.nodes.NodeReorderEvent; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResult; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; @@ -486,7 +487,13 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C * If this is the "main" panel, only allow it to be closed when no case * is open or no there are no data sources in the current case. */ - return (!this.isMain) || !Case.isCaseOpen() || Case.getCurrentCase().hasData() == false; + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + return false; + } + return (!this.isMain) || openCase.hasData() == false; } /** @@ -528,8 +535,8 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C @Override public void propertyChange(PropertyChangeEvent evt) { try { - Case.getCurrentCase(); - } catch (IllegalStateException ex) { + Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { return; } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java index eb9f8dad5b..3beb8a9a33 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java @@ -33,6 +33,7 @@ import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.actions.AddBookmarkTagAction; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResult; import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer; import org.sleuthkit.autopsy.coreutils.Logger; @@ -334,7 +335,13 @@ public class DataResultTopComponent extends TopComponent implements DataResult, * window, only allow it to be closed when there's no case opened or no * data sources in the open case. */ - return (!this.isMain) || !Case.isCaseOpen() || Case.getCurrentCase().hasData() == false; + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + return false; + } + return (!this.isMain) || openCase.hasData() == false; } /** diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java index 206ebb3dea..2e517db0bb 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-17 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * * Copyright 2012 42six Solutions. * Contact: aebadirad 42six com @@ -62,6 +62,7 @@ import org.apache.commons.lang3.concurrent.BasicThreadFactory; import org.opencv.core.Core; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corelibs.ScalrWrapper; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector.FileTypeDetectorInitException; @@ -384,9 +385,9 @@ public class ImageUtils { private static File getCachedThumbnailLocation(long fileID) { return cacheFileMap.computeIfAbsent(fileID, id -> { try { - String cacheDirectory = Case.getCurrentCase().getCacheDirectory(); + String cacheDirectory = Case.getOpenCase().getCacheDirectory(); return Paths.get(cacheDirectory, "thumbnails", fileID + ".png").toFile(); //NON-NLS - } catch (IllegalStateException e) { + } catch (NoCurrentCaseException e) { LOGGER.log(Level.WARNING, "Could not get cached thumbnail location. No case is open."); //NON-NLS return null; } diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java index 3d576eebf0..a65e32c8a6 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/AddRawImageTask.java @@ -3,7 +3,7 @@ package org.sleuthkit.autopsy.datasourceprocessors; /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,6 +35,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskFileRange; import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /* * A runnable that adds a raw data source to a case database. @@ -121,11 +122,20 @@ final class AddRawImageTask implements Runnable { "AddRawImageTask.image.critical.error.adding=Critical error adding ", "AddRawImageTask.for.device=for device ", "AddRawImageTask.image.notExisting=is not existing.", - "AddRawImageTask.image.noncritical.error.adding=Non-critical error adding "}) + "AddRawImageTask.image.noncritical.error.adding=Non-critical error adding ", + "AddRawImageTask.noOpenCase.errMsg=No open case available."}) private void addImageToCase(List dataSources, List errorMessages) { + SleuthkitCase caseDatabase; + try { + caseDatabase = Case.getOpenCase().getSleuthkitCase(); + } catch (NoCurrentCaseException ex) { + errorMessages.add(Bundle.AddRawImageTask_noOpenCase_errMsg()); + logger.log(Level.SEVERE, Bundle.AddRawImageTask_noOpenCase_errMsg(), ex); + criticalErrorOccurred = true; + return; + } progressMonitor.setProgressText(Bundle.AddRawImageTask_progress_add_text() + imageFilePath); List imageFilePaths = new ArrayList<>(); - SleuthkitCase caseDatabase = Case.getCurrentCase().getSleuthkitCase(); File imageFile = Paths.get(imageFilePath).toFile(); if (!imageFile.exists()) { String errorMessage = Bundle.AddRawImageTask_image_critical_error_adding() + imageFilePath + Bundle.AddRawImageTask_for_device() diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java index eae41cd814..69439c89c8 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java @@ -91,6 +91,13 @@ class TextMessageAnalyzer { if (DatabasePath == null || DatabasePath.isEmpty()) { return; } + Case currentCase; + try { + currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } try { Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS @@ -99,7 +106,6 @@ class TextMessageAnalyzer { logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS } - Case currentCase = Case.getCurrentCase(); SleuthkitCase skCase = currentCase.getSleuthkitCase(); try { AbstractFile f = skCase.getAbstractFileById(fId); From c071b212e8022c48c216554f40523f8555d314d4 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 17:10:37 -0500 Subject: [PATCH 077/128] 2229: Part 16: Use getOpenCase() instead of getCurrentCase() in stix, ingest, casemodule... --- .../events/BlackBoardArtifactTagAddedEvent.java | 9 +++++---- .../autopsy/directorytree/ExternalViewerAction.java | 10 +++++++++- .../autopsy/ingest/IngestMessageDetailsPanel.java | 7 ++++--- .../autopsy/ingest/events/ContentChangedEvent.java | 7 ++++--- .../autopsy/ingest/events/FileAnalyzedEvent.java | 7 ++++--- .../sleuthkit/autopsy/modules/stix/EvalSystemObj.java | 7 ++++--- .../autopsy/modules/stix/EvalURLHistoryObj.java | 11 ++++++----- 7 files changed, 36 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/BlackBoardArtifactTagAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/BlackBoardArtifactTagAddedEvent.java index 3c3d15aec8..ad08e643fd 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/BlackBoardArtifactTagAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/BlackBoardArtifactTagAddedEvent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.casemodule.events; import java.io.Serializable; import javax.annotation.concurrent.Immutable; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.TskCoreException; @@ -41,11 +42,11 @@ public class BlackBoardArtifactTagAddedEvent extends TagAddedEvent sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,7 @@ import javax.swing.JMenuItem; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.directorytree.DirectoryTreeTopComponent; import org.sleuthkit.autopsy.ingest.IngestMessagePanel.IngestMessageGroup; import org.sleuthkit.datamodel.AbstractFile; @@ -249,8 +250,8 @@ class IngestMessageDetailsPanel extends javax.swing.JPanel { long objId = artifact.getObjectID(); AbstractFile file = null; try { - file = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(objId); - } catch (TskException ex) { + file = Case.getOpenCase().getSleuthkitCase().getAbstractFileById(objId); + } catch (TskException | NoCurrentCaseException ex) { } if (file == null) { diff --git a/Core/src/org/sleuthkit/autopsy/ingest/events/ContentChangedEvent.java b/Core/src/org/sleuthkit/autopsy/ingest/events/ContentChangedEvent.java index 0f7dcdc0dc..d22cefb5ac 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/events/ContentChangedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/events/ContentChangedEvent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.ingest.events; import java.io.Serializable; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -85,10 +86,10 @@ public final class ContentChangedEvent extends AutopsyEvent implements Serializa } try { SerializableEventData data = (SerializableEventData) super.getOldValue(); - Content content = Case.getCurrentCase().getSleuthkitCase().getContentById(data.contentId); + Content content = Case.getOpenCase().getSleuthkitCase().getContentById(data.contentId); eventData = new ModuleContentEvent(data.moduleName, content); return eventData; - } catch (IllegalStateException | TskCoreException ex) { + } catch (NoCurrentCaseException | TskCoreException ex) { logger.log(Level.SEVERE, "Error doing lazy load for remote event", ex); //NON-NLS return null; } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/events/FileAnalyzedEvent.java b/Core/src/org/sleuthkit/autopsy/ingest/events/FileAnalyzedEvent.java index afc8c0a996..6845fcf391 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/events/FileAnalyzedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/events/FileAnalyzedEvent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.ingest.events; import java.io.Serializable; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -76,9 +77,9 @@ public final class FileAnalyzedEvent extends AutopsyEvent implements Serializabl } try { long id = (Long) super.getOldValue(); - file = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(id); + file = Case.getOpenCase().getSleuthkitCase().getAbstractFileById(id); return file; - } catch (IllegalStateException | TskCoreException ex) { + } catch (NoCurrentCaseException | TskCoreException ex) { logger.log(Level.SEVERE, "Error doing lazy load for remote event", ex); //NON-NLS return null; } diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalSystemObj.java b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalSystemObj.java index 7280cad285..bcfc5bc88e 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalSystemObj.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalSystemObj.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,6 +31,7 @@ import java.util.ArrayList; import org.mitre.cybox.objects.SystemObjectType; import org.mitre.cybox.objects.WindowsSystem; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * @@ -135,7 +136,7 @@ class EvalSystemObj extends EvaluatableObject { setUnsupportedFieldWarnings(); try { - Case case1 = Case.getCurrentCase(); + Case case1 = Case.getOpenCase(); SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); List osInfoList = OSUtility.getOSInfo(sleuthkitCase); @@ -217,7 +218,7 @@ class EvalSystemObj extends EvaluatableObject { return new ObservableResult(id, "SystemObject: No OS artifacts found", //NON-NLS spacing, ObservableResult.ObservableState.INDETERMINATE, null); } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { return new ObservableResult(id, "SystemObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS spacing, ObservableResult.ObservableState.INDETERMINATE, null); } diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalURLHistoryObj.java b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalURLHistoryObj.java index f6a6b67075..5cfa2adcec 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalURLHistoryObj.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalURLHistoryObj.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +30,7 @@ import java.util.ArrayList; import org.mitre.cybox.common_2.AnyURIObjectPropertyType; import org.mitre.cybox.objects.URLHistory; import org.mitre.cybox.objects.URLHistoryEntryType; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * @@ -138,7 +139,7 @@ class EvalURLHistoryObj extends EvaluatableObject { } try { - Case case1 = Case.getCurrentCase(); + Case case1 = Case.getOpenCase(); SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); List artList = sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY); @@ -206,7 +207,7 @@ class EvalURLHistoryObj extends EvaluatableObject { } } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { return new ObservableResult(id, "URLHistoryObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS spacing, ObservableResult.ObservableState.INDETERMINATE, null); } @@ -231,7 +232,7 @@ class EvalURLHistoryObj extends EvaluatableObject { // It doesn't seem too useful, but we can just search for the browser name // if there aren't any URL entries try { - Case case1 = Case.getCurrentCase(); + Case case1 = Case.getOpenCase(); SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); List artList = sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY); @@ -264,7 +265,7 @@ class EvalURLHistoryObj extends EvaluatableObject { // Didn't find any matches return new ObservableResult(id, "URLHistoryObject: No matches found for " + baseSearchString, //NON-NLS spacing, ObservableResult.ObservableState.FALSE, null); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { return new ObservableResult(id, "URLHistoryObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS spacing, ObservableResult.ObservableState.INDETERMINATE, null); } From 1be23038f0f4432eb3b6155c86b784abf95dfaa3 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 17:17:34 -0500 Subject: [PATCH 078/128] 2229: Part 14: Use getOpenCase() instead of getCurrentCase() in ReportKML.java --- Core/src/org/sleuthkit/autopsy/report/ReportKML.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java index 174a5a0367..5914cdd0e4 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java @@ -43,6 +43,7 @@ import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; import org.jdom2.CDATA; import org.openide.filesystems.FileUtil; +import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** @@ -111,7 +112,6 @@ class ReportKML implements GeneralReportModule { progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.querying")); String kmlFileFullPath = baseReportDir + REPORT_KML; //NON-NLS - currentCase = Case.getCurrentCase(); skCase = currentCase.getSleuthkitCase(); progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.loading")); @@ -381,7 +381,7 @@ class ReportKML implements GeneralReportModule { if (result == ReportProgressPanel.ReportStatus.ERROR) { prependedStatus = "Incomplete "; } - Case.getCurrentCase().addReport(kmlFileFullPath, + Case.getOpenCase().addReport(kmlFileFullPath, NbBundle.getMessage(this.getClass(), "ReportKML.genReport.srcModuleName.text"), prependedStatus + NbBundle.getMessage(this.getClass(), "ReportKML.genReport.reportName")); } catch (IOException ex) { @@ -391,6 +391,9 @@ class ReportKML implements GeneralReportModule { String errorMessage = String.format("Error adding %s to case as a report", kmlFileFullPath); //NON-NLS logger.log(Level.SEVERE, errorMessage, ex); result = ReportProgressPanel.ReportStatus.ERROR; + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); + result = ReportProgressPanel.ReportStatus.ERROR; } progressPanel.complete(result); From 696914d03ce9ee2b2a49b838deb0f288f9184be2 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 17:42:25 -0500 Subject: [PATCH 079/128] 2229: Part 17: Use getOpenCase() instead of getCurrentCase() in ingest, stix, , datamodule... --- .../autopsy/datamodel/DataSourcesNode.java | 7 +++--- .../autopsy/datamodel/HashsetHits.java | 10 ++++---- .../autopsy/directorytree/ExtractAction.java | 25 ++++++++++++++++--- .../autopsy/ingest/RunIngestSubMenu.java | 7 +++--- .../modules/stix/EvalNetworkShareObj.java | 7 +++--- .../modules/stix/EvaluatableObject.java | 7 +++--- .../modules/stix/STIXReportModule.java | 5 ++-- 7 files changed, 46 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java index 43ddd069a3..0833b87b43 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DataSourcesNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ 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.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; @@ -102,9 +103,9 @@ public class DataSourcesNode extends DisplayableItemNode { private void reloadKeys() { try { - currentKeys = Case.getCurrentCase().getDataSources(); + currentKeys = Case.getOpenCase().getDataSources(); setKeys(currentKeys); - } catch (TskCoreException | IllegalStateException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Error getting data sources: {0}", ex.getMessage()); // NON-NLS setKeys(Collections.emptySet()); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java index df3d239aef..696c1e7116 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2015 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -208,7 +208,7 @@ public class HashsetHits implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); /** * Due to some unresolved issues with how cases are * closed, it is possible for the event to have a null @@ -218,7 +218,7 @@ public class HashsetHits implements AutopsyVisitableItem { if (null != eventData && eventData.getBlackboardArtifactType().getTypeID() == ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { hashsetResults.update(); } - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ @@ -232,9 +232,9 @@ public class HashsetHits implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); hashsetResults.update(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java index 207c9c1231..28a8bac071 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -33,9 +33,11 @@ import javax.swing.JOptionPane; import javax.swing.SwingWorker; import org.netbeans.api.progress.ProgressHandle; import org.openide.util.Cancellable; +import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -95,9 +97,18 @@ public final class ExtractAction extends AbstractAction { * @param e * @param selectedFile Selected file */ + @NbBundle.Messages ({"ExtractAction.noOpenCase.errMsg=No open case available."}) private void extractFile(ActionEvent e, AbstractFile selectedFile) { + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExtractAction_noOpenCase_errMsg()); + logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS + return; + } JFileChooser fileChooser = new JFileChooser(); - fileChooser.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory())); + fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); // If there is an attribute name, change the ":". Otherwise the extracted file will be hidden fileChooser.setSelectedFile(new File(FileUtil.escapeFileName(selectedFile.getName()))); if (fileChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) { @@ -114,9 +125,17 @@ public final class ExtractAction extends AbstractAction { * @param selectedFiles Selected files */ private void extractFiles(ActionEvent e, Collection selectedFiles) { + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + JOptionPane.showMessageDialog((Component) e.getSource(), Bundle.ExtractAction_noOpenCase_errMsg()); + logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS + return; + } JFileChooser folderChooser = new JFileChooser(); folderChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - folderChooser.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory())); + folderChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); if (folderChooser.showSaveDialog((Component) e.getSource()) == JFileChooser.APPROVE_OPTION) { File destinationFolder = folderChooser.getSelectedFile(); if (!destinationFolder.exists()) { diff --git a/Core/src/org/sleuthkit/autopsy/ingest/RunIngestSubMenu.java b/Core/src/org/sleuthkit/autopsy/ingest/RunIngestSubMenu.java index 0900e3041d..d58c57da72 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/RunIngestSubMenu.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/RunIngestSubMenu.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,6 +27,7 @@ import javax.swing.JMenuItem; import org.openide.awt.DynamicMenuContent; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; @@ -48,11 +49,11 @@ final class RunIngestSubMenu extends JMenuItem implements DynamicMenuContent { List dataSources = new ArrayList<>(); try { - dataSources = Case.getCurrentCase().getDataSources(); + dataSources = Case.getOpenCase().getDataSources(); } catch (IllegalStateException ex) { // No open Cases, create a disabled empty menu return getEmpty(); - } catch (TskCoreException e) { + } catch (TskCoreException | NoCurrentCaseException e) { System.out.println("Exception getting images: " + e.getMessage()); //NON-NLS } JComponent[] comps = new JComponent[dataSources.size()]; diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalNetworkShareObj.java b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalNetworkShareObj.java index 843f19c293..7c1379e75f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalNetworkShareObj.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalNetworkShareObj.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ import java.util.List; import org.mitre.cybox.common_2.ConditionApplicationEnum; import org.mitre.cybox.objects.WindowsNetworkShare; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * @@ -88,7 +89,7 @@ class EvalNetworkShareObj extends EvaluatableObject { try { List finalHits = new ArrayList(); - Case case1 = Case.getCurrentCase(); + Case case1 = Case.getOpenCase(); SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); List artList = sleuthkitCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_REMOTE_DRIVE); @@ -128,7 +129,7 @@ class EvalNetworkShareObj extends EvaluatableObject { // Didn't find any matches return new ObservableResult(id, "NetworkObject: No matches found for " + searchString, //NON-NLS spacing, ObservableResult.ObservableState.FALSE, null); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { return new ObservableResult(id, "NetworkObject: Exception during evaluation: " + ex.getLocalizedMessage(), //NON-NLS spacing, ObservableResult.ObservableState.INDETERMINATE, null); } diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/EvaluatableObject.java b/Core/src/org/sleuthkit/autopsy/modules/stix/EvaluatableObject.java index 6d0abbdcb8..5c3a4cc75a 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/EvaluatableObject.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/EvaluatableObject.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -24,6 +24,7 @@ import org.mitre.cybox.common_2.ConditionApplicationEnum; import org.mitre.cybox.common_2.ConditionTypeEnum; import org.mitre.cybox.common_2.StringObjectPropertyType; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; @@ -100,7 +101,7 @@ abstract class EvaluatableObject { List hits = null; try { - Case case1 = Case.getCurrentCase(); + Case case1 = Case.getOpenCase(); SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); String[] parts = item.getValue().toString().split("##comma##"); //NON-NLS @@ -143,7 +144,7 @@ abstract class EvaluatableObject { } else { throw new TskCoreException("Error: Can not apply NONE condition in search"); //NON-NLS } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { addWarning(ex.getLocalizedMessage()); } diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java b/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java index 9d08bf7115..3f295b5cd7 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/STIXReportModule.java @@ -52,6 +52,7 @@ import org.mitre.stix.stix_1.STIXPackage; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.ModuleSettings; @@ -185,7 +186,7 @@ public class STIXReportModule implements GeneralReportModule { // Set the progress bar to done. If any errors occurred along the way, modify // the "complete" message to indicate this. - Case.getCurrentCase().addReport(reportPath, Bundle.STIXReportModule_srcModuleName_text(), ""); + Case.getOpenCase().addReport(reportPath, Bundle.STIXReportModule_srcModuleName_text(), ""); if (hadErrors) { progressPanel.complete(ReportStatus.ERROR); progressPanel.updateStatusLabel( @@ -202,7 +203,7 @@ public class STIXReportModule implements GeneralReportModule { progressPanel.complete(ReportStatus.ERROR); progressPanel.updateStatusLabel( NbBundle.getMessage(this.getClass(), "STIXReportModule.progress.completedWithErrors")); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Unable to add report to database.", ex); } } From 255637b179ad2bb84429203fc58d76d33cd0ee3a Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 2 Mar 2018 17:47:09 -0500 Subject: [PATCH 080/128] 2229: Part 17: Use getOpenCase() instead of getCurrentCase() in HashSetHits.java --- Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java index 696c1e7116..a1b9cf9963 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java @@ -41,6 +41,7 @@ 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.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; From 4347ebef23062f5f5892fc7e08a0576ce91cb95a Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 5 Mar 2018 13:43:40 +0100 Subject: [PATCH 081/128] use AccountDeviceInstance API of getRelationshipCountsPairwise --- .../communications/CommunicationsGraph.java | 47 +++++-------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java index 81ec5d7838..02404a5ea7 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CommunicationsGraph.java @@ -44,10 +44,10 @@ import javax.swing.SwingWorker; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.progress.ProgressIndicator; import org.sleuthkit.datamodel.AccountDeviceInstance; +import org.sleuthkit.datamodel.AccountPair; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.Relationship; import org.sleuthkit.datamodel.TskCoreException; /** @@ -289,63 +289,40 @@ final class CommunicationsGraph extends mxGraph { /** * set to keep track of accounts related to pinned accounts */ - Map relatedAccounts = new HashMap<>(); + Map relatedAccounts = new HashMap<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountModel.getPinnedAccounts()) { if (isCancelled()) { break; } List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); - relatedAccounts.put(adiKey.getAccountDeviceInstance().getAccount().getAccountID(), adiKey); + relatedAccounts.put(adiKey.getAccountDeviceInstance(), adiKey); getOrCreateVertex(adiKey); //get accounts related to pinned account for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); - relatedAccounts.put(relatedADI.getAccount().getAccountID(), relatedADIKey); //store related accounts + relatedAccounts.put(relatedADI, relatedADIKey); //store related accounts } progress.progress(++i); } - Set accountIDs = relatedAccounts.keySet(); + Set accountIDs = relatedAccounts.keySet(); - Map relationshipCounts = commsManager.getRelationshipCounts(accountIDs, currentFilter); + Map relationshipCounts = commsManager.getRelationshipCountsPairwise(accountIDs, currentFilter); int total = relationshipCounts.size(); int k = 0; - progress.switchToDeterminate("", 0,total); - for (Map.Entry entry : relationshipCounts.entrySet()) { + progress.switchToDeterminate("", 0, total); + for (Map.Entry entry : relationshipCounts.entrySet()) { Long count = entry.getValue(); - Relationship.RelationshipKey relationshipKey = entry.getKey(); - AccountDeviceInstanceKey account1 = relatedAccounts.get(relationshipKey.getAccount1ID()); - AccountDeviceInstanceKey account2 = relatedAccounts.get(relationshipKey.getAccount2ID()); + AccountPair relationshipKey = entry.getKey(); + AccountDeviceInstanceKey account1 = relatedAccounts.get(relationshipKey.getFirst()); + AccountDeviceInstanceKey account2 = relatedAccounts.get(relationshipKey.getSecond()); mxCell addEdge = addOrUpdateEdge(count, account1, account2); - progress.progress(addEdge.getId(),k++); + progress.progress(addEdge.getId(), k++); } -// //for each pair of related accounts add edges if they are related o each other. -// // this is O(n^2) in the number of related accounts!!! -// List relatedAccountsList = new ArrayList<>(relatedAccounts); -// progress.switchToDeterminate("", 0, relatedAccountsList.size()); -// -// for (i = 0; i < relatedAccountsList.size(); i++) { -// AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); -// for (int j = i; j < relatedAccountsList.size(); j++) { -// if (isCancelled()) { -// break; -// } -// AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); -// List relationships = commsManager.getRelationshipSources( -// adiKey1.getAccountDeviceInstance(), -// adiKey2.getAccountDeviceInstance(), -// currentFilter); -// if (relationships.size() > 0) { -// mxCell addEdge = addEdge(relationships, adiKey1, adiKey2); -// progress.progress(addEdge.getId()); -// } -// } -// progress.progress(i); -// } } catch (TskCoreException tskCoreException) { logger.log(Level.SEVERE, "Error", tskCoreException); } finally { From 02c462d820666748431a70e265b3102746ca89a6 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Mon, 5 Mar 2018 09:12:04 -0500 Subject: [PATCH 082/128] 2229: Return true for canClose while no open case. --- .../autopsy/corecomponents/DataContentTopComponent.java | 2 +- .../org/sleuthkit/autopsy/corecomponents/DataResultPanel.java | 2 +- .../autopsy/corecomponents/DataResultTopComponent.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java index c63c79ac40..9de489a042 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentTopComponent.java @@ -173,7 +173,7 @@ public final class DataContentTopComponent extends TopComponent implements DataC try { openCase = Case.getOpenCase(); } catch (NoCurrentCaseException ex) { - return false; + return true; } return (!this.isDefault) || openCase.hasData() == false; } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java index ef8eb1c5b6..9751bb024a 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultPanel.java @@ -491,7 +491,7 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C try { openCase = Case.getOpenCase(); } catch (NoCurrentCaseException ex) { - return false; + return true; } return (!this.isMain) || openCase.hasData() == false; } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java index 3beb8a9a33..8ec9886f53 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultTopComponent.java @@ -339,7 +339,7 @@ public class DataResultTopComponent extends TopComponent implements DataResult, try { openCase = Case.getOpenCase(); } catch (NoCurrentCaseException ex) { - return false; + return true; } return (!this.isMain) || openCase.hasData() == false; } From 67b98ee39fda52b3b52dd79f0ae4e812203216a7 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Mon, 5 Mar 2018 10:29:15 -0500 Subject: [PATCH 083/128] 2229: Part 18: Use getOpenCase() instead of getCurrentCase() in examples, ingest, modules... --- .../DirectoryTreeTopComponent.java | 18 ++++++++++++------ .../examples/SampleDataSourceIngestModule.java | 5 +++-- .../autopsy/ingest/IngestMonitor.java | 9 +++++---- .../autopsy/ingest/RunIngestAction.java | 10 ++++++++-- .../EmbeddedFileExtractorIngestModule.java | 13 +++++++++---- .../EncryptionDetectionFileIngestModule.java | 5 ++++- .../FilesIdentifierIngestModule.java | 11 ++++++++--- 7 files changed, 49 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java index e0c9670a2a..13219f863d 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/DirectoryTreeTopComponent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -53,6 +53,7 @@ import org.openide.util.NbBundle.Messages; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.corecomponentinterfaces.CoreComponentControl; @@ -363,8 +364,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); Case currentCase = null; try { - currentCase = Case.getCurrentCase(); - } catch (IllegalStateException ex) { + currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { // No open case. } @@ -524,7 +525,12 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat * be closed if there is no opne case or the open case has no data * sources. */ - return !Case.isCaseOpen() || Case.getCurrentCase().hasData() == false; + try { + Case openCase = Case.getOpenCase(); + return openCase.hasData() == false; + } catch (NoCurrentCaseException ex) { + return true; + } } /** @@ -613,13 +619,13 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat * already closed. */ try { - Case currentCase = Case.getCurrentCase(); + Case currentCase = Case.getOpenCase(); // We only need to trigger openCoreWindows() when the // first data source is added. if (currentCase.getDataSources().size() == 1) { SwingUtilities.invokeLater(CoreComponentControl::openCoreWindows); } - } catch (IllegalStateException | TskCoreException notUsed) { + } catch (NoCurrentCaseException | TskCoreException notUsed) { /** * Case is closed, do nothing. */ diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java index d1f1d7e5aa..8605d33c13 100644 --- a/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java @@ -32,6 +32,7 @@ package org.sleuthkit.autopsy.examples; import java.util.List; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; @@ -76,7 +77,7 @@ class SampleDataSourceIngestModule implements DataSourceIngestModule { try { // Get count of files with .doc extension. - FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); + FileManager fileManager = Case.getOpenCase().getServices().getFileManager(); List docFiles = fileManager.findFiles(dataSource, "%.doc"); long fileCount = 0; @@ -117,7 +118,7 @@ class SampleDataSourceIngestModule implements DataSourceIngestModule { return IngestModule.ProcessResult.OK; - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { IngestServices ingestServices = IngestServices.getInstance(); Logger logger = ingestServices.getLogger(SampleIngestModuleFactory.getModuleName()); logger.log(Level.SEVERE, "File query failed", ex); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java index 8aeda52597..e4c5c41340 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestMonitor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2015 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,6 +31,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import javax.swing.Timer; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.events.AutopsyEvent; @@ -144,11 +145,11 @@ public final class IngestMonitor { */ private void findRootDirectoryForCurrentCase() { try { - Case currentCase = Case.getCurrentCase(); + Case currentCase = Case.getOpenCase(); findRootDirectoryForCurrentCase(currentCase); - } catch (IllegalStateException unused) { + } catch (NoCurrentCaseException unused) { /* - * Case.getCurrentCase() throws IllegalStateException when there + * Case.getOpenCase() throws NoCurrentCaseException when there * is no case. */ root = new File(File.separator); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java b/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java index 49875a3997..72bc963fe7 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/RunIngestAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.Presenter; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * The action associated with assorted Run Ingest Modules menu items. @@ -82,6 +83,11 @@ public final class RunIngestAction extends CallableSystemAction implements Prese @Override public boolean isEnabled() { - return Case.isCaseOpen() && Case.getCurrentCase().hasData(); + try { + Case openCase = Case.getOpenCase(); + return openCase.hasData(); + } catch (NoCurrentCaseException ex) { + return false; + } } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java index 7a9d9b04ef..042d6cb87c 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +28,7 @@ import org.sleuthkit.autopsy.ingest.IngestModule.ProcessResult; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import net.sf.sevenzipjbinding.SevenZipNativeInitializationException; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter; /** @@ -37,7 +38,8 @@ import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter; @NbBundle.Messages({ "CannotCreateOutputFolder=Unable to create output folder.", "CannotRunFileTypeDetection=Unable to run file type detection.", - "UnableToInitializeLibraries=Unable to initialize 7Zip libraries." + "UnableToInitializeLibraries=Unable to initialize 7Zip libraries.", + "EmbeddedFileExtractorIngestModule.NoOpenCase.errMsg=No open case available." }) public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAdapter { @@ -63,10 +65,13 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda * case database for extracted (derived) file paths. The absolute path * is used to write the extracted (derived) files to local storage. */ - final Case currentCase = Case.getCurrentCase(); + try { + final Case currentCase = Case.getOpenCase(); moduleDirRelative = Paths.get(currentCase.getModuleOutputDirectoryRelativePath(), EmbeddedFileExtractorModuleFactory.getModuleName()).toString(); moduleDirAbsolute = Paths.get(currentCase.getModuleDirectory(), EmbeddedFileExtractorModuleFactory.getModuleName()).toString(); - + } catch (NoCurrentCaseException ex) { + throw new IngestModuleException(Bundle.EmbeddedFileExtractorIngestModule_NoOpenCase_errMsg(), ex); + } /* * Create the output directory. */ diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java index 685f85f6b2..d7e9919d55 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionFileIngestModule.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter; @@ -86,10 +87,12 @@ final class EncryptionDetectionFileIngestModule extends FileIngestModuleAdapter public void startUp(IngestJobContext context) throws IngestModule.IngestModuleException { try { validateSettings(); - blackboard = Case.getCurrentCase().getServices().getBlackboard(); + blackboard = Case.getOpenCase().getServices().getBlackboard(); fileTypeDetector = new FileTypeDetector(); } catch (FileTypeDetector.FileTypeDetectorInitException ex) { throw new IngestModule.IngestModuleException("Failed to create file type detector", ex); + } catch (NoCurrentCaseException ex) { + throw new IngestModule.IngestModuleException("Exception while getting open case.", ex); } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java index 88ee3ae1b7..8ae1db3dc5 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +28,7 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -105,8 +106,12 @@ final class FilesIdentifierIngestModule implements FileIngestModule { @Override @Messages({"FilesIdentifierIngestModule.indexError.message=Failed to index interesting file hit artifact for keyword search."}) public ProcessResult process(AbstractFile file) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); - + try { + blackboard = Case.getOpenCase().getServices().getBlackboard(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return ProcessResult.ERROR; + } // Skip slack space files. if (file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.SLACK)) { return ProcessResult.OK; From a4cea63c185a2b3cec45e5911bbaed3ba94d6702 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Mon, 5 Mar 2018 12:19:07 -0500 Subject: [PATCH 084/128] 2229: Part 19: Use getOpenCase() instead of getCurrentCase() in ImageGallery and Experimental. --- .../autoingest/AddArchiveTask.java | 5 ++--- .../autoingest/ArchiveFilePanel.java | 16 ++++++++++++---- .../autoingest/AutoIngestManager.java | 7 ++++--- .../autoingest/FileExportRuleSet.java | 14 ++++++++++---- .../experimental/autoingest/FileExporter.java | 18 ++++++++++++------ .../autoingest/FileExporterSettingsPanel.java | 9 +++++---- .../imagegallery/actions/OpenAction.java | 11 +++++++++-- 7 files changed, 54 insertions(+), 26 deletions(-) diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java index 00f4a06e63..6835510e78 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AddArchiveTask.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.experimental.autoingest; import java.io.File; -import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -101,7 +100,7 @@ class AddArchiveTask implements Runnable { // extract the archive and pass the extracted folder as input try { - Case currentCase = Case.getCurrentCase(); + Case currentCase = Case.getOpenCase(); // create folder to extract archive to Path destinationFolder = createDirectoryForFile(archivePath, currentCase.getModuleDirectory()); diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java index e5285d9e75..449c36dfff 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/ArchiveFilePanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ import javax.swing.filechooser.FileFilter; import org.apache.commons.lang3.StringUtils; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import static org.sleuthkit.autopsy.experimental.autoingest.Bundle.*; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.coreutils.DriveUtils; @@ -204,7 +205,9 @@ class ArchiveFilePanel extends JPanel implements DocumentListener { * * @return true if a proper archive has been selected, false otherwise */ - @NbBundle.Messages("DataSourceOnCDriveError.text=Warning: Path to multi-user data source is on \"C:\" drive") + @NbBundle.Messages({"DataSourceOnCDriveError.text=Warning: Path to multi-user data source is on \"C:\" drive", + "DataSourceOnCDriveError.noOpenCase.errMsg=Warning: Exception while getting open case." + }) public boolean validatePanel() { errorLabel.setVisible(false); String path = getContentPaths(); @@ -213,9 +216,14 @@ class ArchiveFilePanel extends JPanel implements DocumentListener { } // display warning if there is one (but don't disable "next" button) - if (false == PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) { + try { + if (false == PathValidator.isValid(path, Case.getOpenCase().getCaseType())) { + errorLabel.setVisible(true); + errorLabel.setText(Bundle.DataSourceOnCDriveError_text()); + } + } catch (NoCurrentCaseException ex) { errorLabel.setVisible(true); - errorLabel.setText(Bundle.DataSourceOnCDriveError_text()); + errorLabel.setText(Bundle.DataSourceOnCDriveError_noOpenCase_errMsg()); } return new File(path).isFile() diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java index c9fb9a7003..1a1f60a2bc 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/AutoIngestManager.java @@ -63,6 +63,7 @@ import org.sleuthkit.autopsy.casemodule.Case.CaseType; import org.sleuthkit.autopsy.casemodule.CaseActionException; import org.sleuthkit.autopsy.casemodule.CaseDetails; import org.sleuthkit.autopsy.casemodule.CaseMetadata; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coordinationservice.CaseNodeData; import org.sleuthkit.autopsy.coordinationservice.CoordinationService; import org.sleuthkit.autopsy.coordinationservice.CoordinationService.CoordinationServiceException; @@ -2269,7 +2270,7 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen Thread.sleep(AutoIngestUserPreferences.getSecondsToSleepBetweenCases() * 1000); } currentJob.setCaseDirectoryPath(caseDirectoryPath); - Case caseForJob = Case.getCurrentCase(); + Case caseForJob = Case.getOpenCase(); SYS_LOGGER.log(Level.INFO, "Opened case {0} for {1}", new Object[]{caseForJob.getName(), manifest.getFilePath()}); return caseForJob; @@ -2277,10 +2278,10 @@ final class AutoIngestManager extends Observable implements PropertyChangeListen throw new CaseManagementException(String.format("Error creating solr settings file for case %s for %s", caseName, manifest.getFilePath()), ex); } catch (CaseActionException ex) { throw new CaseManagementException(String.format("Error creating or opening case %s for %s", caseName, manifest.getFilePath()), ex); - } catch (IllegalStateException ex) { + } catch (NoCurrentCaseException ex) { /* * Deal with the unfortunate fact that - * Case.getCurrentCase throws IllegalStateException. + * Case.getOpenCase throws NoCurrentCaseException. */ throw new CaseManagementException(String.format("Error getting current case %s for %s", caseName, manifest.getFilePath()), ex); } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExportRuleSet.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExportRuleSet.java index d4616036f7..0029150e3d 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExportRuleSet.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExportRuleSet.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -39,6 +39,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.apache.commons.codec.binary.Hex; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * Uniquely named file export rules organized into uniquely named rule sets. @@ -374,7 +375,7 @@ final class FileExportRuleSet implements Serializable, Comparable evaluate(long dataSourceId) throws ExportRulesException { try { - SleuthkitCase db = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase db = Case.getOpenCase().getSleuthkitCase(); try (SleuthkitCase.CaseDbQuery queryResult = db.executeQuery(getQuery(dataSourceId))) { ResultSet resultSet = queryResult.getResultSet(); List fileIds = new ArrayList<>(); @@ -383,7 +384,7 @@ final class FileExportRuleSet implements Serializable, Comparable sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,6 +34,7 @@ import java.util.Map; import java.util.TreeMap; import java.util.function.Supplier; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; @@ -111,7 +112,7 @@ final class FileExporter { } closeCatalogs(); writeFlagFiles(); - } catch (FileExportSettings.PersistenceException | FileExportRuleSet.ExportRulesException | TskCoreException | IOException ex) { + } catch (FileExportSettings.PersistenceException | FileExportRuleSet.ExportRulesException | TskCoreException | IOException | NoCurrentCaseException ex) { throw new FileExportException("Error occurred during file export", ex); } } @@ -128,7 +129,12 @@ final class FileExporter { * @throws org.sleuthkit.autopsy.autoingest.FileExporter.FileExportException */ private boolean verifyPrerequisites(List dataSources) throws FileExportException { - SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase skCase; + try { + skCase = Case.getOpenCase().getSleuthkitCase(); + } catch (NoCurrentCaseException ex) { + throw new FileExportException("Exception while getting open case.", ex); + } List ingestJobs = new ArrayList<>(); try { // all ingest jobs that were processed as part of this case @@ -310,7 +316,7 @@ final class FileExporter { * @throws IOException If there is a problem writing a file to * secondary storage. */ - private void exportFiles(Map> fileIdsToRuleNames, Supplier cancelCheck) throws TskCoreException, IOException { + private void exportFiles(Map> fileIdsToRuleNames, Supplier cancelCheck) throws TskCoreException, IOException, NoCurrentCaseException { for (Map.Entry> entry : fileIdsToRuleNames.entrySet()) { if (cancelCheck.get()) { return; @@ -334,8 +340,8 @@ final class FileExporter { * @throws IOException If there is a problem writing the file to * storage. */ - private void exportFile(Long fileId, List ruleNames, Supplier cancelCheck) throws TskCoreException, IOException { - AbstractFile file = Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(fileId); + private void exportFile(Long fileId, List ruleNames, Supplier cancelCheck) throws TskCoreException, IOException, NoCurrentCaseException { + AbstractFile file = Case.getOpenCase().getSleuthkitCase().getAbstractFileById(fileId); if (!shouldExportFile(file)) { return; } diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java index ab1b44702c..62b01b558f 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java @@ -82,6 +82,7 @@ import java.time.ZoneId; import javax.swing.DefaultListModel; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * Global settings panel for data-source-level ingest modules that export and @@ -528,11 +529,11 @@ public final class FileExporterSettingsPanel extends JPanel { void populateArtifacts() { Set artifactTypes = scanRulesForArtifacts(); try { - SleuthkitCase currentCase = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase currentCase = Case.getOpenCase().getSleuthkitCase(); for (BlackboardArtifact.Type type : currentCase.getArtifactTypes()) { artifactTypes.add(type.getTypeName()); } - } catch (IllegalStateException | TskCoreException ex) { + } catch (NoCurrentCaseException | TskCoreException ex) { // Unable to find and open case or cannot read the database. Use enum. for (BlackboardArtifact.ARTIFACT_TYPE artifact : BlackboardArtifact.ARTIFACT_TYPE.values()) { artifactTypes.add(artifact.toString()); @@ -602,12 +603,12 @@ public final class FileExporterSettingsPanel extends JPanel { Set attributeTypes = scanRulesForAttributes(); try { - SleuthkitCase currentCase = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase currentCase = Case.getOpenCase().getSleuthkitCase(); for (BlackboardAttribute.Type type : currentCase.getAttributeTypes()) { attributeTypes.add(type.getTypeName()); attributeTypeMap.put(type.getTypeName(), type.getValueType()); } - } catch (IllegalStateException | TskCoreException ex) { + } catch (NoCurrentCaseException | TskCoreException ex) { // Unable to find and open case or cannot read the database. Use enum. for (BlackboardAttribute.ATTRIBUTE_TYPE type : BlackboardAttribute.ATTRIBUTE_TYPE.values()) { attributeTypes.add(type.getLabel()); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java index 67d72bbeb3..9c21f6ebf8 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015-17 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -45,6 +45,7 @@ import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.Installer; import org.sleuthkit.autopsy.core.RuntimeProperties; import org.sleuthkit.autopsy.coreutils.Logger; @@ -106,7 +107,13 @@ public final class OpenAction extends CallableSystemAction { @Override public boolean isEnabled() { - return super.isEnabled() && Case.isCaseOpen() && Installer.isJavaFxInited() && Case.getCurrentCase().hasData(); + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + return false; + } + return super.isEnabled() && Installer.isJavaFxInited() && openCase.hasData(); } /** From c219d065ef999078a7c7a6937898b3d489b55560 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Mon, 5 Mar 2018 13:21:59 -0500 Subject: [PATCH 085/128] 2229: Part 20: Use getOpenCase() instead of getCurrentCase() in ImageGallery, RecentActivity, datamodel... --- .../autopsy/datamodel/InterestingHits.java | 11 ++++++----- .../filetypeid/FileTypeIdIngestModule.java | 5 ++++- .../imagegallery/ImageGalleryController.java | 15 ++++++++++----- .../imagegallery/actions/DeleteTagAction.java | 5 +++-- .../autopsy/imagegallery/actions/OpenAction.java | 11 +++++++---- .../imagegallery/datamodel/DrawableFile.java | 7 ++++--- .../sleuthkit/autopsy/recentactivity/Util.java | 7 ++++--- 7 files changed, 38 insertions(+), 23 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index 7a0b6b9ad9..435fa75023 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,6 +41,7 @@ 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.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; @@ -199,7 +200,7 @@ public class InterestingHits implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); /** * Even with the check above, it is still possible that * the case will be closed in a different thread before @@ -211,7 +212,7 @@ public class InterestingHits implements AutopsyVisitableItem { || eventData.getBlackboardArtifactType().getTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID())) { interestingResults.update(); } - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ @@ -225,9 +226,9 @@ public class InterestingHits implements AutopsyVisitableItem { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); interestingResults.update(); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java index be0bcdfbb3..f9cb21434c 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/FileTypeIdIngestModule.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.FileIngestModule; @@ -155,9 +156,11 @@ public class FileTypeIdIngestModule implements FileIngestModule { attributes.add(ruleNameAttribute); artifact.addAttributes(attributes); try { - Case.getCurrentCase().getServices().getBlackboard().indexArtifact(artifact); + Case.getOpenCase().getServices().getBlackboard().indexArtifact(artifact); } catch (Blackboard.BlackboardException ex) { logger.log(Level.SEVERE, String.format("Unable to index TSK_INTERESTING_FILE_HIT blackboard artifact %d (file obj_id=%d)", artifact.getArtifactID(), file.getId()), ex); //NON-NLS + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS } } catch (TskCoreException ex) { logger.log(Level.SEVERE, String.format("Unable to create TSK_INTERESTING_FILE_HIT artifact for file (obj_id=%d)", file.getId()), ex); //NON-NLS diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index f4e0a6959c..d802136a1c 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-17 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -58,6 +58,7 @@ import org.netbeans.api.progress.ProgressHandle; import org.openide.util.Cancellable; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.core.RuntimeProperties; @@ -209,10 +210,14 @@ public final class ImageGalleryController { private ImageGalleryController() { listeningEnabled.addListener((observable, oldValue, newValue) -> { - //if we just turned on listening and a case is open and that case is not up to date - if (newValue && !oldValue && Case.isCaseOpen() && ImageGalleryModule.isDrawableDBStale(Case.getCurrentCase())) { - //populate the db - queueDBTask(new CopyAnalyzedFiles(instance, db, sleuthKitCase)); + try { + //if we just turned on listening and a case is open and that case is not up to date + if (newValue && !oldValue && ImageGalleryModule.isDrawableDBStale(Case.getOpenCase())) { + //populate the db + queueDBTask(new CopyAnalyzedFiles(instance, db, sleuthKitCase)); + } + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.WARNING, "Exception while getting open case.", ex); } }); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/DeleteTagAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/DeleteTagAction.java index 40451e8443..2145b76d3f 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/DeleteTagAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/DeleteTagAction.java @@ -35,6 +35,7 @@ import org.controlsfx.control.action.ActionUtils; import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.ImageGalleryController; @@ -122,7 +123,7 @@ public class DeleteTagAction extends Action { try { List existingTagsList - = Case.getCurrentCase().getServices().getTagsManager() + = Case.getOpenCase().getServices().getTagsManager() .getContentTagsByContent(file); Collection tagNamesList @@ -138,7 +139,7 @@ public class DeleteTagAction extends Action { } } } - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { Logger.getLogger(TagMenu.class.getName()) .log(Level.SEVERE, "Error retrieving tags for TagMenu", ex); //NON-NLS } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java index 9c21f6ebf8..f318b0d44c 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java @@ -152,10 +152,13 @@ public final class OpenAction extends CallableSystemAction { public void performAction() { //check case - if (!Case.isCaseOpen()) { + final Case currentCase; + try { + currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); return; } - final Case currentCase = Case.getCurrentCase(); if (tooManyFiles()) { Platform.runLater(OpenAction::showTooManyFiles); @@ -185,8 +188,8 @@ public final class OpenAction extends CallableSystemAction { private boolean tooManyFiles() { try { - return FILE_LIMIT < Case.getCurrentCase().getSleuthkitCase().countFilesWhere("1 = 1"); - } catch (IllegalStateException ex) { + return FILE_LIMIT < Case.getOpenCase().getSleuthkitCase().countFilesWhere("1 = 1"); + } catch (NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Can not open image gallery with no case open.", ex); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error counting files in the DB.", ex); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableFile.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableFile.java index af26dd2e97..af1ddd2641 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableFile.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/DrawableFile.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -38,6 +38,7 @@ import javax.annotation.Nonnull; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.text.WordUtils; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.imagegallery.FileTypeUtils; import org.sleuthkit.autopsy.imagegallery.ThumbnailCache; @@ -73,8 +74,8 @@ public abstract class DrawableFile { : new ImageFile(abstractFileById, analyzed); } - public static DrawableFile create(Long id, boolean analyzed) throws TskCoreException, IllegalStateException { - return create(Case.getCurrentCase().getSleuthkitCase().getAbstractFileById(id), analyzed); + public static DrawableFile create(Long id, boolean analyzed) throws TskCoreException, NoCurrentCaseException { + return create(Case.getOpenCase().getSleuthkitCase().getAbstractFileById(id), analyzed); } private SoftReference imageRef; diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java index a69ee8edf7..9048c57da0 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Util.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2012 Basis Technology Corp. + * Copyright 2012-2018 Basis Technology Corp. * * Copyright 2012 42six Solutions. * Contact: aebadirad 42six com @@ -41,6 +41,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.Content; @@ -190,11 +191,11 @@ class Util { int index = parent_path.lastIndexOf('/'); String name = parent_path.substring(++index); parent_path = parent_path.substring(0, index); - FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); List files = null; try { + FileManager fileManager = Case.getOpenCase().getServices().getFileManager(); files = fileManager.findFiles(dataSource, name, parent_path); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.WARNING, "Error fetching 'index.data' files for Internet Explorer history."); //NON-NLS } From 4b640fea925e4ef319b22f5b120f739a24c3c142 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Mon, 5 Mar 2018 13:58:15 -0500 Subject: [PATCH 086/128] 2229: Part 21: Use getOpenCase() instead of getCurrentCase() in filesearch, modules, datamodel... --- .../casemodule/IngestJobInfoPanel.java | 8 ++++---- .../OptionalCasePropertiesPanel.java | 20 +++++++++++++------ .../datasourceprocessors/RawDSInputPanel.java | 13 +++++++++--- .../autopsy/filesearch/DateSearchFilter.java | 11 +++++----- .../exif/ExifParserFileIngestModule.java | 11 +++++++--- .../PhotoRecCarverFileIngestModule.java | 11 +++++++--- .../autopsy/modules/stix/EvalRegistryObj.java | 20 +++++++++++++++---- 7 files changed, 66 insertions(+), 28 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java index 37e4a8fb02..8a496d8fac 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/IngestJobInfoPanel.java @@ -75,12 +75,12 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { } private void refresh() { - SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); try { + SleuthkitCase skCase = Case.getOpenCase().getSleuthkitCase(); List ingestJobs = skCase.getIngestJobs(); this.ingestJobs = ingestJobs; this.repaint(); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to load ingest jobs.", ex); JOptionPane.showMessageDialog(this, Bundle.IngestJobInfoPanel_loadIngestJob_error_text(), Bundle.IngestJobInfoPanel_loadIngestJob_error_title(), JOptionPane.ERROR_MESSAGE); } @@ -114,11 +114,11 @@ public final class IngestJobInfoPanel extends javax.swing.JPanel { @Override public Object getValueAt(int rowIndex, int columnIndex) { IngestJobInfo currIngestJob = ingestJobs.get(rowIndex); - SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); if (columnIndex == 0) { try { + SleuthkitCase skCase = Case.getOpenCase().getSleuthkitCase(); return skCase.getContentById(currIngestJob.getObjectId()).getName(); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to get content from db", ex); return ""; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java index 3840c9ed1d..65aedd9392 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/OptionalCasePropertiesPanel.java @@ -537,7 +537,8 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { @Messages({ "OptionalCasePropertiesPanel.errorDialog.emptyCaseNameMessage=No case name entered.", - "OptionalCasePropertiesPanel.errorDialog.invalidCaseNameMessage=Case names cannot include the following symbols: \\, /, :, *, ?, \", <, >, |" + "OptionalCasePropertiesPanel.errorDialog.invalidCaseNameMessage=Case names cannot include the following symbols: \\, /, :, *, ?, \", <, >, |", + "OptionalCasePropertiesPanel.errorDialog.noOpenCase.errMsg=Exception while getting open case." }) void saveUpdatedCaseDetails() { if (caseDisplayNameTextField.getText().trim().isEmpty()) { @@ -548,14 +549,19 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { MessageNotifyUtil.Message.error(Bundle.OptionalCasePropertiesPanel_errorDialog_invalidCaseNameMessage()); return; } - updateCaseDetails(); + try { + updateCaseDetails(); + } catch (NoCurrentCaseException ex) { + MessageNotifyUtil.Message.error(Bundle.OptionalCasePropertiesPanel_errorDialog_noOpenCase_errMsg()); + return; + } updateCorrelationCase(); } - private void updateCaseDetails() { + private void updateCaseDetails() throws NoCurrentCaseException { if (caseDisplayNameTextField.isVisible()) { try { - Case.getCurrentCase().updateCaseDetails(new CaseDetails( + Case.getOpenCase().updateCaseDetails(new CaseDetails( caseDisplayNameTextField.getText(), caseNumberTextField.getText(), examinerTextField.getText(), tfExaminerPhoneText.getText(), tfExaminerEmailText.getText(), taNotesText.getText())); @@ -574,7 +580,7 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { if (EamDb.isEnabled()) { try { EamDb dbManager = EamDb.getInstance(); - CorrelationCase correlationCase = dbManager.getCase(Case.getCurrentCase()); + CorrelationCase correlationCase = dbManager.getCase(Case.getOpenCase()); if (caseDisplayNameTextField.isVisible()) { correlationCase.setDisplayName(caseDisplayNameTextField.getText()); } @@ -586,7 +592,9 @@ final class OptionalCasePropertiesPanel extends javax.swing.JPanel { correlationCase.setNotes(taNotesText.getText()); dbManager.updateCase(correlationCase); } catch (EamDbException ex) { - LOGGER.log(Level.SEVERE, "Error connecting to central repository database", ex); // NON-NLS + LOGGER.log(Level.SEVERE, "Error connecting to central repository database", ex); // NON-NLS + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS } finally { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java index 8fb6fe5ccf..de53de4ee8 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/RawDSInputPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -28,6 +28,7 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.autopsy.coreutils.PathValidator; @@ -296,12 +297,18 @@ final class RawDSInputPanel extends JPanel implements DocumentListener { * * @param path Absolute path to the selected data source */ - @Messages({"RawDSInputPanel.error.text=Path to multi-user data source is on \"C:\" drive"}) + @Messages({"RawDSInputPanel.error.text=Path to multi-user data source is on \"C:\" drive", + "RawDSInputPanel.noOpenCase.errMsg=Exception while getting open case."}) private void warnIfPathIsInvalid(String path) { - if (!PathValidator.isValid(path, Case.getCurrentCase().getCaseType())) { + try { + if (!PathValidator.isValid(path, Case.getOpenCase().getCaseType())) { errorLabel.setVisible(true); errorLabel.setText(Bundle.RawDSInputPanel_error_text()); } + } catch (NoCurrentCaseException ex) { + errorLabel.setVisible(true); + errorLabel.setText(Bundle.RawDSInputPanel_noOpenCase_errMsg()); + } } void storeSettings() { diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java index 64666e66d9..661e08a542 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/DateSearchFilter.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -43,6 +43,7 @@ import javax.swing.border.EmptyBorder; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * Filters file date properties (modified/created/etc.. times) @@ -140,7 +141,7 @@ class DateSearchFilter extends AbstractFileSearchFilter { try { // get the latest case - Case currentCase = Case.getCurrentCase(); // get the most updated case + Case currentCase = Case.getOpenCase(); // get the most updated case Set caseTimeZones = currentCase.getTimeZones(); Iterator iterator = caseTimeZones.iterator(); @@ -167,7 +168,7 @@ class DateSearchFilter extends AbstractFileSearchFilter { String item = String.format("(GMT%+d:%02d) %s", hour, minutes, id); //NON-NLS timeZones.add(item); } - } catch (IllegalStateException ex) { + } catch (NoCurrentCaseException ex) { // No current case. } @@ -281,9 +282,9 @@ class DateSearchFilter extends AbstractFileSearchFilter { * that is already closed. */ try { - Case.getCurrentCase(); + Case.getOpenCase(); SwingUtilities.invokeLater(DateSearchFilter.this::updateTimeZoneList); - } catch (IllegalStateException notUsed) { + } catch (NoCurrentCaseException notUsed) { /** * Case is closed, do nothing. */ diff --git a/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java index 7aa1224e18..0e1d58da65 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2015 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -40,6 +40,7 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -101,8 +102,12 @@ public final class ExifParserFileIngestModule implements FileIngestModule { @Override public ProcessResult process(AbstractFile content) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); - + try { + blackboard = Case.getOpenCase().getServices().getBlackboard(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS + return ProcessResult.ERROR; + } //skip unalloc if ((content.getType().equals(TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) || (content.getType().equals(TSK_DB_FILES_TYPE_ENUM.SLACK)))) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index 04f7f42355..89a606bc1e 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -37,9 +37,9 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import org.openide.modules.InstalledFileLocator; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.ExecUtil; import org.sleuthkit.autopsy.coreutils.FileUtil; import org.sleuthkit.autopsy.coreutils.Logger; @@ -404,7 +404,12 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { * @throws org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException */ synchronized Path createModuleOutputDirectoryForCase() throws IngestModule.IngestModuleException { - Path path = Paths.get(Case.getCurrentCase().getModuleDirectory(), PhotoRecCarverIngestModuleFactory.getModuleName()); + Path path; + try { + path = Paths.get(Case.getOpenCase().getModuleDirectory(), PhotoRecCarverIngestModuleFactory.getModuleName()); + } catch (NoCurrentCaseException ex) { + throw new IngestModule.IngestModuleException(Bundle.cannotCreateOutputDir_message(ex.getLocalizedMessage()), ex); + } try { Files.createDirectory(path); if (UNCPathUtilities.isUNC(path)) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalRegistryObj.java b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalRegistryObj.java index 53b8b2d1f2..6040ea66e5 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalRegistryObj.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalRegistryObj.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -35,6 +35,7 @@ import java.util.regex.Matcher; import org.mitre.cybox.objects.WindowsRegistryKey; import org.mitre.cybox.common_2.ConditionTypeEnum; import com.williballenthin.rejistry.*; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * @@ -345,7 +346,12 @@ class EvalRegistryObj extends EvaluatableObject { List regFilesLocal = new ArrayList(); // Make the temp directory - String tmpDir = Case.getCurrentCase().getTempDirectory() + File.separator + "STIX"; //NON-NLS + String tmpDir; + try { + tmpDir = Case.getOpenCase().getTempDirectory() + File.separator + "STIX"; //NON-NLS + } catch (NoCurrentCaseException ex) { + throw new TskCoreException(ex.getLocalizedMessage()); + } File dir = new File(tmpDir); if (dir.exists() == false) { dir.mkdirs(); @@ -377,9 +383,15 @@ class EvalRegistryObj extends EvaluatableObject { */ private static List findRegistryFiles() throws TskCoreException { List registryFiles = new ArrayList(); - org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + throw new TskCoreException(ex.getLocalizedMessage()); + } + org.sleuthkit.autopsy.casemodule.services.FileManager fileManager = openCase.getServices().getFileManager(); - for (Content ds : Case.getCurrentCase().getDataSources()) { + for (Content ds : openCase.getDataSources()) { // find the user-specific ntuser-dat files registryFiles.addAll(fileManager.findFiles(ds, "ntuser.dat")); //NON-NLS From d409ddeda1f57e600cf4756f12d6ba5e8fe5e220 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Mon, 5 Mar 2018 14:28:19 -0500 Subject: [PATCH 087/128] 2229: Part 22: Use getOpenCase() instead of getCurrentCase() in ingest and modules. --- .../autopsy/ingest/DataSourceIngestJob.java | 9 +++++---- .../events/DataSourceAnalysisEvent.java | 7 ++++--- .../FileExtMismatchIngestModule.java | 10 ++++++++-- .../autopsy/modules/iOS/ContactAnalyzer.java | 19 +++++++++++++------ .../autopsy/modules/stix/EvalAddressObj.java | 12 ++++++++++-- .../autopsy/modules/stix/EvalDomainObj.java | 13 ++++++++++--- .../autopsy/modules/stix/EvalURIObj.java | 13 +++++++++++-- 7 files changed, 61 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java index 568e410de6..7cddb8028b 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestJob.java @@ -34,6 +34,7 @@ import org.openide.util.Cancellable; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.NetworkUtils; import org.sleuthkit.datamodel.AbstractFile; @@ -254,12 +255,12 @@ final class DataSourceIngestJob { */ Thread.currentThread().interrupt(); } - SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); try { + SleuthkitCase skCase = Case.getOpenCase().getSleuthkitCase(); this.addIngestModules(firstStageDataSourceModuleTemplates, IngestModuleType.DATA_SOURCE_LEVEL, skCase); this.addIngestModules(fileIngestModuleTemplates, IngestModuleType.FILE_LEVEL, skCase); this.addIngestModules(secondStageDataSourceModuleTemplates, IngestModuleType.DATA_SOURCE_LEVEL, skCase); - } catch (TskCoreException ex) { + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to add ingest modules to database.", ex); } } @@ -400,8 +401,8 @@ final class DataSourceIngestJob { this.startSecondStage(); } try { - this.ingestJob = Case.getCurrentCase().getSleuthkitCase().addIngestJob(dataSource, NetworkUtils.getLocalHostName(), ingestModules, new Date(this.createTime), new Date(0), IngestJobStatusType.STARTED, ""); - } catch (TskCoreException ex) { + this.ingestJob = Case.getOpenCase().getSleuthkitCase().addIngestJob(dataSource, NetworkUtils.getLocalHostName(), ingestModules, new Date(this.createTime), new Date(0), IngestJobStatusType.STARTED, ""); + } catch (TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Failed to add ingest job to database.", ex); } } diff --git a/Core/src/org/sleuthkit/autopsy/ingest/events/DataSourceAnalysisEvent.java b/Core/src/org/sleuthkit/autopsy/ingest/events/DataSourceAnalysisEvent.java index c508d3e276..100db13067 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/events/DataSourceAnalysisEvent.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/events/DataSourceAnalysisEvent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.ingest.events; import java.io.Serializable; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -96,9 +97,9 @@ public abstract class DataSourceAnalysisEvent extends AutopsyEvent implements Se } try { long id = (Long) super.getNewValue(); - dataSource = Case.getCurrentCase().getSleuthkitCase().getContentById(id); + dataSource = Case.getOpenCase().getSleuthkitCase().getContentById(id); return dataSource; - } catch (IllegalStateException | TskCoreException ex) { + } catch (NoCurrentCaseException | TskCoreException ex) { logger.log(Level.SEVERE, "Error doing lazy load for remote event", ex); //NON-NLS return null; } diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java index 672bf83d97..1d33730e20 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,6 +25,7 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -108,7 +109,12 @@ public class FileExtMismatchIngestModule implements FileIngestModule { @Override @Messages({"FileExtMismatchIngestModule.indexError.message=Failed to index file extension mismatch artifact for keyword search."}) public ProcessResult process(AbstractFile abstractFile) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); + try { + blackboard = Case.getOpenCase().getServices().getBlackboard(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Exception while getting open case.", ex); //NON-NLS + return ProcessResult.ERROR; + } if (this.settings.skipKnownFiles() && (abstractFile.getKnown() == FileKnown.KNOWN)) { return ProcessResult.OK; } diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java index efa4494f26..31d3f30a68 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,6 +34,7 @@ import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -59,19 +60,25 @@ class ContactAnalyzer { private Blackboard blackboard; public void findContacts(IngestJobContext context) { - - blackboard = Case.getCurrentCase().getServices().getBlackboard(); + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } + + blackboard = openCase.getServices().getBlackboard(); List absFiles; try { - SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase skCase = openCase.getSleuthkitCase(); absFiles = skCase.findAllFilesWhere("LOWER(name) LIKE LOWER('%call_history%') "); //NON-NLS //get exact file names if (absFiles.isEmpty()) { return; } for (AbstractFile AF : absFiles) { try { - jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), AF.getName().replaceAll("[<>%|\"/:*\\\\]", "")); - //jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), i+".txt"); + jFile = new java.io.File(openCase.getTempDirectory(), AF.getName().replaceAll("[<>%|\"/:*\\\\]", "")); ContentUtils.writeToFile(AF, jFile, context::dataSourceIngestIsCancelled); //copyFileUsingStreams(AF,jFile); //copyFileUsingStream(AF,jFile); diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalAddressObj.java b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalAddressObj.java index faaac1dc4e..4141ba874f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalAddressObj.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalAddressObj.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,6 +30,7 @@ import org.mitre.cybox.common_2.ConditionApplicationEnum; import org.mitre.cybox.common_2.ConditionTypeEnum; import org.mitre.cybox.objects.Address; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * @@ -54,6 +55,14 @@ class EvalAddressObj extends EvaluatableObject { spacing, ObservableResult.ObservableState.INDETERMINATE, null); } + Case case1; + try { + case1 = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + return new ObservableResult(id, "Exception while getting open case.", //NON-NLS + spacing, ObservableResult.ObservableState.FALSE, null); + } + String origAddressStr = obj.getAddressValue().getValue().toString(); // For now, we don't support "NONE" because it honestly doesn't seem like it @@ -67,7 +76,6 @@ class EvalAddressObj extends EvaluatableObject { // Set warnings for any unsupported fields setUnsupportedFieldWarnings(); - Case case1 = Case.getCurrentCase(); SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); try { diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalDomainObj.java b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalDomainObj.java index 2dd6c7f680..d06c5a19e0 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalDomainObj.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalDomainObj.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -27,6 +27,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.SleuthkitCase; /** @@ -52,7 +53,14 @@ class EvalDomainObj extends EvaluatableObject { spacing, ObservableResult.ObservableState.INDETERMINATE, null); } - // Since we have single URL artifacts, ALL and NONE conditions probably don't make sense to test + Case case1; + try { + case1 = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + return new ObservableResult(id, "Exception while getting open case.", //NON-NLS + spacing, ObservableResult.ObservableState.FALSE, null); + } + // Since we have single URL artifacts, ALL and NONE conditions probably don't make sense to test if (!((obj.getValue().getApplyCondition() == null) || (obj.getValue().getApplyCondition() == ConditionApplicationEnum.ANY))) { return new ObservableResult(id, "DomainObject: Can not process apply condition " + obj.getValue().getApplyCondition().toString() //NON-NLS @@ -66,7 +74,6 @@ class EvalDomainObj extends EvaluatableObject { + " on DomainName - using substring comparison"); //NON-NLS } - Case case1 = Case.getCurrentCase(); SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); try { diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalURIObj.java b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalURIObj.java index c5e598b034..123468041d 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalURIObj.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalURIObj.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ import java.util.ArrayList; import org.mitre.cybox.common_2.ConditionApplicationEnum; import org.mitre.cybox.objects.URIObjectType; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * @@ -52,6 +53,15 @@ class EvalURIObj extends EvaluatableObject { return new ObservableResult(id, "URIObject: No URI value field found", //NON-NLS spacing, ObservableResult.ObservableState.INDETERMINATE, null); } + + Case case1; + try { + case1 = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + return new ObservableResult(id, "Exception while getting open case: " + ex.getLocalizedMessage(), //NON-NLS + spacing, ObservableResult.ObservableState.FALSE, null); + } + String addressStr = obj.getValue().getValue().toString(); // Strip off http:// or https:// @@ -65,7 +75,6 @@ class EvalURIObj extends EvaluatableObject { + " on URI object", spacing, ObservableResult.ObservableState.INDETERMINATE, null); //NON-NLS } - Case case1 = Case.getCurrentCase(); SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); try { From 0f8e5f86e0fad9bdde1db47fc5b0a25f1d4f2f31 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Mon, 5 Mar 2018 15:56:50 -0500 Subject: [PATCH 088/128] 2229: part 4 remove unused imports --- Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java | 1 - .../sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java | 1 - 2 files changed, 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java index 6fa73b7ff7..d9955feff0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/ImageFilePanel.java @@ -31,7 +31,6 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.filechooser.FileFilter; import org.apache.commons.lang3.StringUtils; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import static org.sleuthkit.autopsy.casemodule.Bundle.*; import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java index 4e70d4b248..1e1efde334 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalEvidenceFilePanel.java @@ -32,7 +32,6 @@ import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; -import org.openide.util.Exceptions; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.PathValidator; From eabe09fb12e8b925192ab75c9d4027444c64bb4d Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Mon, 5 Mar 2018 16:14:23 -0500 Subject: [PATCH 089/128] 2229: part 14 remove unused imports --- Core/src/org/sleuthkit/autopsy/report/ReportKML.java | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java index 5914cdd0e4..7d230ea1b2 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportKML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportKML.java @@ -43,7 +43,6 @@ import org.jdom2.output.Format; import org.jdom2.output.XMLOutputter; import org.jdom2.CDATA; import org.openide.filesystems.FileUtil; -import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** From 284e133fc044e97390aea1450d5c9f0364c77f3a Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Mon, 5 Mar 2018 17:34:51 -0500 Subject: [PATCH 090/128] 2229: Part 23: Use getOpenCase() instead of getCurrentCase() in ingest, modules, casemodule... --- .../autopsy/casemodule/events/ReportAddedEvent.java | 7 ++++--- .../autopsy/casemodule/services/TagOptionsPanel.java | 9 +++++++-- .../sleuthkit/autopsy/filesearch/FileSearchPanel.java | 5 +++-- .../autopsy/ingest/GetFilesCountVisitor.java | 9 ++++++++- .../EmbeddedFileExtractorIngestModule.java | 9 +++++++-- .../MSOfficeEmbeddedContentExtractor.java | 5 +++-- .../autopsy/modules/iOS/ContactAnalyzer.java | 8 +++++++- .../sleuthkit/autopsy/modules/stix/EvalFileObj.java | 11 +++++++++-- 8 files changed, 48 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java b/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java index 534f6b646a..2fe152d32e 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/events/ReportAddedEvent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,7 @@ import java.io.Serializable; import java.util.List; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.events.AutopsyEvent; import org.sleuthkit.datamodel.Report; @@ -69,7 +70,7 @@ public final class ReportAddedEvent extends AutopsyEvent implements Serializable } try { long id = (Long) super.getNewValue(); - List reports = Case.getCurrentCase().getSleuthkitCase().getAllReports(); + List reports = Case.getOpenCase().getSleuthkitCase().getAllReports(); for (Report thisReport : reports) { if (thisReport.getId() == id) { report = thisReport; @@ -77,7 +78,7 @@ public final class ReportAddedEvent extends AutopsyEvent implements Serializable } } return report; - } catch (IllegalStateException | TskCoreException ex) { + } catch (NoCurrentCaseException | TskCoreException ex) { logger.log(Level.SEVERE, "Error doing lazy load for remote event", ex); //NON-NLS return null; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java index 11d6495a39..65bfc2c2c0 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/TagOptionsPanel.java @@ -24,6 +24,7 @@ import java.beans.PropertyChangeListener; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; +import java.util.logging.Level; import javax.swing.DefaultListModel; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; @@ -32,10 +33,12 @@ import org.netbeans.spi.options.OptionsPanelController; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TskData; +import org.sleuthkit.autopsy.coreutils.Logger; /** * A panel to allow the user to create and delete custom tag types. @@ -425,8 +428,10 @@ final class TagOptionsPanel extends javax.swing.JPanel implements OptionsPanel { private void sendStatusChangedEvents() { for (String modifiedTagDisplayName : updatedStatusTags) { //if user closes their case after options have been changed but before application of them is complete don't notify - if (Case.isCaseOpen()) { - Case.getCurrentCase().notifyTagDefinitionChanged(modifiedTagDisplayName); + try { + Case.getOpenCase().notifyTagDefinitionChanged(modifiedTagDisplayName); + } catch (NoCurrentCaseException ex) { + Logger.getLogger(TagOptionsPanel.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS } } updatedStatusTags.clear(); diff --git a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java index e39070cd70..7cf9b60ff7 100644 --- a/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filesearch/FileSearchPanel.java @@ -43,6 +43,7 @@ import org.openide.NotifyDescriptor; import org.openide.util.NbBundle; import org.openide.windows.TopComponent; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.coreutils.Logger; @@ -152,7 +153,7 @@ class FileSearchPanel extends javax.swing.JPanel { String pathText = NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.results.pathText"); // try to get the number of matches first - Case currentCase = Case.getCurrentCase(); // get the most updated case + Case currentCase = Case.getOpenCase(); // get the most updated case long totalMatches = 0; List contentList = null; try { @@ -189,7 +190,7 @@ class FileSearchPanel extends javax.swing.JPanel { throw new FilterValidationException( NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.exception.noFilterSelected.msg")); } - } catch (FilterValidationException ex) { + } catch (FilterValidationException | NoCurrentCaseException ex) { NotifyDescriptor d = new NotifyDescriptor.Message( NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.validationErr.msg", ex.getMessage())); DialogDisplayer.getDefault().notify(d); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/GetFilesCountVisitor.java b/Core/src/org/sleuthkit/autopsy/ingest/GetFilesCountVisitor.java index df9eeacbff..94b9b2a93b 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/GetFilesCountVisitor.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/GetFilesCountVisitor.java @@ -22,6 +22,7 @@ import java.util.List; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ContentVisitor; import org.sleuthkit.datamodel.FileSystem; @@ -44,7 +45,13 @@ final class GetFilesCountVisitor extends ContentVisitor.Default { public Long visit(FileSystem fs) { //recursion stop here //case of a real fs, query all files for it - SleuthkitCase sc = Case.getCurrentCase().getSleuthkitCase(); + SleuthkitCase sc; + try { + sc = Case.getOpenCase().getSleuthkitCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return 0L; + } StringBuilder queryB = new StringBuilder(); queryB.append("( (fs_obj_id = ").append(fs.getId()); //NON-NLS //queryB.append(") OR (fs_obj_id = NULL) )"); diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java index 042d6cb87c..331b6d8830 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/EmbeddedFileExtractorIngestModule.java @@ -39,7 +39,8 @@ import org.sleuthkit.autopsy.ingest.FileIngestModuleAdapter; "CannotCreateOutputFolder=Unable to create output folder.", "CannotRunFileTypeDetection=Unable to run file type detection.", "UnableToInitializeLibraries=Unable to initialize 7Zip libraries.", - "EmbeddedFileExtractorIngestModule.NoOpenCase.errMsg=No open case available." + "EmbeddedFileExtractorIngestModule.NoOpenCase.errMsg=No open case available.", + "EmbeddedFileExtractorIngestModule.UnableToGetMSOfficeExtractor.errMsg=Unable to get MSOfficeEmbeddedContentExtractor." }) public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAdapter { @@ -106,7 +107,11 @@ public final class EmbeddedFileExtractorIngestModule extends FileIngestModuleAda * Construct an embedded content extractor for processing Microsoft * Office documents. */ - this.officeExtractor = new MSOfficeEmbeddedContentExtractor(context, fileTypeDetector, moduleDirRelative, moduleDirAbsolute); + try { + this.officeExtractor = new MSOfficeEmbeddedContentExtractor(context, fileTypeDetector, moduleDirRelative, moduleDirAbsolute); + } catch (NoCurrentCaseException ex) { + throw new IngestModuleException(Bundle.EmbeddedFileExtractorIngestModule_UnableToGetMSOfficeExtractor_errMsg(), ex); + } } @Override diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java index 506fc5a6f3..23e33dc180 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/MSOfficeEmbeddedContentExtractor.java @@ -55,6 +55,7 @@ import org.apache.tika.parser.microsoft.OfficeParserConfig; import org.apache.tika.sax.BodyContentHandler; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestJobContext; @@ -115,9 +116,9 @@ class MSOfficeEmbeddedContentExtractor { } private SupportedExtractionFormats abstractFileExtractionFormat; - MSOfficeEmbeddedContentExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) { + MSOfficeEmbeddedContentExtractor(IngestJobContext context, FileTypeDetector fileTypeDetector, String moduleDirRelative, String moduleDirAbsolute) throws NoCurrentCaseException { - this.fileManager = Case.getCurrentCase().getServices().getFileManager(); + this.fileManager = Case.getOpenCase().getServices().getFileManager(); this.services = IngestServices.getInstance(); this.context = context; this.fileTypeDetector = fileTypeDetector; diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java index 31d3f30a68..d3553956f5 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java @@ -106,6 +106,13 @@ class ContactAnalyzer { if (DatabasePath == null || DatabasePath.isEmpty()) { return; } + Case currentCase; + try { + currentCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } try { Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver connection = DriverManager.getConnection("jdbc:sqlite:" + DatabasePath); //NON-NLS @@ -114,7 +121,6 @@ class ContactAnalyzer { logger.log(Level.SEVERE, "Error opening database", e); //NON-NLS } - Case currentCase = Case.getCurrentCase(); SleuthkitCase skCase = currentCase.getSleuthkitCase(); try { AbstractFile f = skCase.getAbstractFileById(fId); diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalFileObj.java b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalFileObj.java index 1d2925060e..e99f34fe06 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/EvalFileObj.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/EvalFileObj.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,6 +41,7 @@ import org.mitre.cybox.common_2.HashType; import org.mitre.cybox.common_2.DateTimeObjectPropertyType; import org.mitre.cybox.common_2.StringObjectPropertyType; import org.mitre.cybox.common_2.UnsignedLongObjectPropertyType; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; /** * @@ -59,7 +60,13 @@ class EvalFileObj extends EvaluatableObject { @SuppressWarnings("deprecation") public synchronized ObservableResult evaluate() { - Case case1 = Case.getCurrentCase(); + Case case1; + try { + case1 = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + return new ObservableResult(id, "Exception while getting open case.", //NON-NLS + spacing, ObservableResult.ObservableState.FALSE, null); + } SleuthkitCase sleuthkitCase = case1.getSleuthkitCase(); setWarnings(""); From 724350e467f5e6db6e3a0009993a20dee888aba1 Mon Sep 17 00:00:00 2001 From: Raman Date: Tue, 6 Mar 2018 06:53:23 -0500 Subject: [PATCH 091/128] Removed duplicate log messages, ensure DB resources closed in case of error. --- .../autopsy/contentviewers/SQLiteViewer.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index e67623039e..d7ee98896a 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -325,7 +325,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { new SwingWorker() { @Override - protected Void doInBackground() throws Exception { + protected Void doInBackground() throws IOException, SQLException, ClassNotFoundException { try { // Copy the file to temp folder @@ -347,7 +347,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { LOGGER.log(Level.SEVERE, "Failed to copy DB file " + sqliteFile.getName(), ex); //NON-NLS throw ex; } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Failed to open DB file " + sqliteFile.getName(), ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Failed to get tables from DB file " + sqliteFile.getName(), ex); //NON-NLS throw ex; } catch (ClassNotFoundException ex) { LOGGER.log(Level.SEVERE, "Failed to initialize JDBC SQLite.", ex); //NON-NLS @@ -437,21 +437,33 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { */ private void getTables() throws SQLException { - - Statement statement = connection.createStatement(); - ResultSet resultSet = statement.executeQuery( + Statement statement = null; + ResultSet resultSet = null; + + try { + statement = connection.createStatement(); + resultSet = statement.executeQuery( "SELECT name, sql FROM sqlite_master " + " WHERE type= 'table' " + " ORDER BY name;"); //NON-NLS + + while (resultSet.next()) { + String tableName = resultSet.getString("name"); //NON-NLS + String tableSQL = resultSet.getString("sql"); //NON-NLS - while (resultSet.next()) { - String tableName = resultSet.getString("name"); //NON-NLS - String tableSQL = resultSet.getString("sql"); //NON-NLS - - dbTablesMap.put(tableName, tableSQL); + dbTablesMap.put(tableName, tableSQL); + } + } catch(SQLException ex) { + throw ex; + } + finally { + if (null != resultSet) { + resultSet.close(); + } + if (null != statement) { + statement.close(); + } } - resultSet.close(); - statement.close(); } @NbBundle.Messages({"# {0} - tableName", @@ -476,7 +488,6 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { return resultSet.getInt("count"); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Failed to get row count for table " + tableName, ex); //NON-NLS throw ex; } finally { @@ -556,7 +567,6 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { return resultSetToArrayList(resultSet); } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Failed to get data for table " + tableName, ex); //NON-NLS throw ex; } finally { From 6361ae591e5dbaf9536d4c204d4d906a000ff173 Mon Sep 17 00:00:00 2001 From: Raman Date: Tue, 6 Mar 2018 08:07:11 -0500 Subject: [PATCH 092/128] 3594: Move Application viewer tab to left - Reordered content viewers --- Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java | 2 +- Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java | 2 +- .../autopsy/corecomponents/DataContentViewerArtifact.java | 2 +- .../sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java index 395f2aa7e5..1d2a1a2a5b 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FileViewer.java @@ -36,7 +36,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.FileTypeViewer; /** * Generic Application content viewer */ -@ServiceProvider(service = DataContentViewer.class, position = 5) +@ServiceProvider(service = DataContentViewer.class, position = 3) public class FileViewer extends javax.swing.JPanel implements DataContentViewer { private static final int CONFIDENCE_LEVEL = 7; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java index df1db76507..060280ed08 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/Metadata.java @@ -35,7 +35,7 @@ import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; * shows the same data that can also be found in the ResultViewer table, just a * different order and allows the full path to be visible in the bottom area. */ -@ServiceProvider(service = DataContentViewer.class, position = 3) +@ServiceProvider(service = DataContentViewer.class, position = 6) public class Metadata extends javax.swing.JPanel implements DataContentViewer { /** diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java index c4f35e9b8a..a3e33ef02e 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java @@ -62,7 +62,7 @@ import org.netbeans.swing.etable.ETable; * Content represented by a Node. Each BlackboardArtifact is rendered displayed * in a JTable representation of its BlackboardAttributes. */ -@ServiceProvider(service = DataContentViewer.class, position = 3) +@ServiceProvider(service = DataContentViewer.class, position = 7) public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer { @NbBundle.Messages({ diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java index 08b2d0a7bb..2c348efc7a 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java @@ -49,7 +49,7 @@ import org.sleuthkit.datamodel.TskCoreException; * A content viewer that displays the indexed text associated with a file or an * artifact, possibly marked up with HTML to highlight keyword hits. */ -@ServiceProvider(service = DataContentViewer.class, position = 4) +@ServiceProvider(service = DataContentViewer.class, position = 5) public class ExtractedContentViewer implements DataContentViewer { private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName()); From 5917545641047e969faa1311a5d5a0eed0ac330a Mon Sep 17 00:00:00 2001 From: Raman Date: Tue, 6 Mar 2018 09:33:08 -0500 Subject: [PATCH 093/128] 3594: Move Application viewer tab to left Swapped positions for Message veiwer and Indexed text viewer --- .../sleuthkit/autopsy/contentviewers/MessageContentViewer.java | 2 +- .../sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 228dc09c13..cd3722fa93 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -70,7 +70,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Shows SMS/MMS/EMail messages */ -@ServiceProvider(service = DataContentViewer.class, position = 4) +@ServiceProvider(service = DataContentViewer.class, position = 5) public class MessageContentViewer extends javax.swing.JPanel implements DataContentViewer { private static final long serialVersionUID = 1L; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java index 2c348efc7a..08b2d0a7bb 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ExtractedContentViewer.java @@ -49,7 +49,7 @@ import org.sleuthkit.datamodel.TskCoreException; * A content viewer that displays the indexed text associated with a file or an * artifact, possibly marked up with HTML to highlight keyword hits. */ -@ServiceProvider(service = DataContentViewer.class, position = 5) +@ServiceProvider(service = DataContentViewer.class, position = 4) public class ExtractedContentViewer implements DataContentViewer { private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName()); From 3542fab909bb92be490d88443c9a1b6e6c2a4551 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Tue, 6 Mar 2018 10:24:48 -0500 Subject: [PATCH 094/128] 2229: Part 24: Use getOpenCase() instead of getCurrentCase() in contentviewers, modules, casemodule... --- .../casemodule/CaseInformationPanel.java | 8 +++++- .../casemodule/CasePropertiesPanel.java | 11 +++++--- .../autopsy/contentviewers/FXVideoPanel.java | 11 ++++++-- .../autopsy/contentviewers/GstVideoPanel.java | 26 ++++++++++++++++--- .../autopsy/coreutils/VideoUtils.java | 15 ++++++++--- .../hashdatabase/HashDbIngestModule.java | 8 +++++- .../PhotoRecCarverOutputParser.java | 7 ++--- 7 files changed, 69 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java index 04632e76ba..f600dd3b55 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CaseInformationPanel.java @@ -20,9 +20,11 @@ package org.sleuthkit.autopsy.casemodule; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.logging.Level; import javax.swing.JDialog; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import org.sleuthkit.autopsy.coreutils.Logger; import org.openide.util.NbBundle.Messages; import org.openide.windows.WindowManager; @@ -51,7 +53,11 @@ class CaseInformationPanel extends javax.swing.JPanel { "CaseInformationPanel.editDetailsDialog.title=Edit Case Details" }) private void customizeComponents() { - propertiesPanel = new CasePropertiesPanel(Case.getCurrentCase()); + try { + propertiesPanel = new CasePropertiesPanel(Case.getOpenCase()); + } catch (NoCurrentCaseException ex) { + Logger.getLogger(CaseInformationPanel.class.getName()).log(Level.INFO, "Exception while getting open case.", ex); + } propertiesPanel.setSize(propertiesPanel.getPreferredSize()); this.tabbedPane.addTab(Bundle.CaseInformationPanel_caseDetails_header(), propertiesPanel); this.tabbedPane.addTab(Bundle.CaseInformationPanel_ingestJobInfo_header(), new IngestJobInfoPanel()); diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java index 101f8688dd..d07f850868 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/CasePropertiesPanel.java @@ -49,7 +49,12 @@ final class CasePropertiesPanel extends javax.swing.JPanel { } void updateCaseInfo() { - theCase = Case.getCurrentCase(); + try { + theCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); + return; + } lbCaseNameText.setText(theCase.getDisplayName()); lbCaseNumberText.setText(theCase.getNumber()); lbExaminerNameText.setText(theCase.getExaminer()); @@ -78,9 +83,9 @@ final class CasePropertiesPanel extends javax.swing.JPanel { try { EamDb dbManager = EamDb.getInstance(); if (dbManager != null) { - CorrelationCase correlationCase = dbManager.getCase(Case.getCurrentCase()); + CorrelationCase correlationCase = dbManager.getCase(theCase); if (null == correlationCase) { - correlationCase = dbManager.newCase(Case.getCurrentCase()); + correlationCase = dbManager.newCase(theCase); } currentOrg = correlationCase.getOrg(); } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java index 64203d674b..ef6acc1fd4 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-15 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -59,6 +59,7 @@ import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProviders; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.Installer; import org.sleuthkit.autopsy.corecomponents.FrameCapture; import org.sleuthkit.autopsy.corecomponents.VideoFrame; @@ -136,7 +137,13 @@ public class FXVideoPanel extends MediaViewVideoPanel { mediaPane.setInfoLabelText(path); mediaPane.setInfoLabelToolTipText(path); - final File tempFile = VideoUtils.getTempVideoFile(currentFile); + final File tempFile; + try { + tempFile = VideoUtils.getTempVideoFile(currentFile); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } new Thread(mediaPane.new ExtractMedia(currentFile, tempFile)).start(); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.java index e32c551d65..c686471a41 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-15 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -55,6 +55,7 @@ import org.netbeans.api.progress.ProgressHandle; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProviders; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponents.FrameCapture; import org.sleuthkit.autopsy.corecomponents.VideoFrame; import org.sleuthkit.autopsy.coreutils.Logger; @@ -181,6 +182,7 @@ public class GstVideoPanel extends MediaViewVideoPanel { } @Override + @NbBundle.Messages ({"GstVideoPanel.noOpenCase.errMsg=No open case available."}) void setupVideo(final AbstractFile file, final Dimension dims) { reset(); infoLabel.setText(""); @@ -194,6 +196,18 @@ public class GstVideoPanel extends MediaViewVideoPanel { return; } + java.io.File ioFile; + try { + ioFile = VideoUtils.getTempVideoFile(file); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + infoLabel.setText(Bundle.GstVideoPanel_noOpenCase_errMsg()); + pauseButton.setEnabled(false); + progressSlider.setEnabled(false); + + return; + } + String path = ""; try { path = file.getUniquePath(); @@ -205,7 +219,6 @@ public class GstVideoPanel extends MediaViewVideoPanel { pauseButton.setEnabled(true); progressSlider.setEnabled(true); - java.io.File ioFile = VideoUtils.getTempVideoFile(file); gstVideoComponent = new VideoComponent(); synchronized (playbinLock) { @@ -537,7 +550,14 @@ public class GstVideoPanel extends MediaViewVideoPanel { return; } } else if (state.equals(State.READY)) { - final File tempVideoFile = VideoUtils.getTempVideoFile(currentFile); + final File tempVideoFile; + try { + tempVideoFile = VideoUtils.getTempVideoFile(currentFile); + } catch (NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Exception while getting open case."); //NON-NLS + infoLabel.setText(MEDIA_PLAYER_ERROR_STRING); + return; + } new ExtractMedia(currentFile, tempVideoFile).execute(); diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/VideoUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/VideoUtils.java index 81670a878c..59d16ac5f3 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/VideoUtils.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/VideoUtils.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2015-16 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -34,6 +34,7 @@ import org.opencv.core.Mat; import org.opencv.highgui.VideoCapture; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corelibs.ScalrWrapper; import static org.sleuthkit.autopsy.coreutils.ImageUtils.isMediaThumbnailSupported; import org.sleuthkit.autopsy.datamodel.ContentUtils; @@ -90,8 +91,8 @@ public class VideoUtils { private VideoUtils() { } - public static File getTempVideoFile(AbstractFile file) { - return Paths.get(Case.getCurrentCase().getTempDirectory(), "videos", file.getId() + "." + file.getNameExtension()).toFile(); //NON-NLS + public static File getTempVideoFile(AbstractFile file) throws NoCurrentCaseException { + return Paths.get(Case.getOpenCase().getTempDirectory(), "videos", file.getId() + "." + file.getNameExtension()).toFile(); //NON-NLS } public static boolean isVideoThumbnailSupported(AbstractFile file) { @@ -101,7 +102,13 @@ public class VideoUtils { @NbBundle.Messages({"# {0} - file name", "VideoUtils.genVideoThumb.progress.text=extracting temporary file {0}"}) static BufferedImage generateVideoThumbnail(AbstractFile file, int iconSize) { - java.io.File tempFile = getTempVideoFile(file); + java.io.File tempFile; + try { + tempFile = getTempVideoFile(file); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.WARNING, "Exception while getting open case.", ex); //NON-NLS + return null; + } if (tempFile.exists() == false || tempFile.length() < file.getSize()) { ProgressHandle progress = ProgressHandle.createHandle(Bundle.VideoUtils_genVideoThumb_progress_text(file.getName())); progress.start(100); diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java index 022cda8a9c..1a11c6702f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java @@ -29,6 +29,7 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -144,7 +145,12 @@ public class HashDbIngestModule implements FileIngestModule { @Override public ProcessResult process(AbstractFile file) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); + try { + blackboard = Case.getOpenCase().getServices().getBlackboard(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return ProcessResult.ERROR; + } // Skip unallocated space files. if ((file.getType().equals(TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS) || diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverOutputParser.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverOutputParser.java index 01d19b84d1..6c1975f5a8 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverOutputParser.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverOutputParser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -29,6 +29,7 @@ import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -99,7 +100,7 @@ class PhotoRecCarverOutputParser { NodeList fileRanges; Element entry; Path filePath; - FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); + FileManager fileManager = Case.getOpenCase().getServices().getFileManager(); // create and initialize the list to put into the database List carvedFiles = new ArrayList<>(); @@ -156,7 +157,7 @@ class PhotoRecCarverOutputParser { } } return fileManager.addCarvedFiles(new CarvingResult(af, carvedFiles)); - } catch (NumberFormatException | TskCoreException ex) { + } catch (NumberFormatException | TskCoreException | NoCurrentCaseException ex) { logger.log(Level.SEVERE, "Error parsing PhotoRec output and inserting it into the database", ex); //NON-NLS } From b5d4935f6b4361ac6bb92658604978114b361ff7 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Tue, 6 Mar 2018 10:56:42 -0500 Subject: [PATCH 095/128] 2229: Catch the NoCurrentCaseException in VideoFile.java --- .../autopsy/imagegallery/datamodel/VideoFile.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java index 7e963e512b..f5f322a65c 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java @@ -29,6 +29,7 @@ import javafx.scene.media.Media; import javafx.scene.media.MediaException; import org.netbeans.api.progress.ProgressHandle; import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.VideoUtils; @@ -64,7 +65,7 @@ public class VideoFile extends DrawableFile { private SoftReference mediaRef; @NbBundle.Messages({"VideoFile.getMedia.progress=writing temporary file to disk"}) - public Media getMedia() throws IOException, MediaException { + public Media getMedia() throws IOException, MediaException, NoCurrentCaseException { Media media = (mediaRef != null) ? mediaRef.get() : null; if (media != null) { @@ -90,7 +91,7 @@ public class VideoFile extends DrawableFile { Double getWidth() { try { return (double) getMedia().getWidth(); - } catch (IOException | MediaException ex) { + } catch (IOException | MediaException | NoCurrentCaseException ex) { return -1.0; } } @@ -104,7 +105,7 @@ public class VideoFile extends DrawableFile { Double getHeight() { try { return (double) getMedia().getHeight(); - } catch (IOException | MediaException ex) { + } catch (IOException | MediaException | NoCurrentCaseException ex) { return -1.0; } } From cd6b24075d3fa6cd26f236bded15323c7d3a109d Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Tue, 6 Mar 2018 16:36:25 -0500 Subject: [PATCH 096/128] 2229: Part 25: Use getOpenCase() instead of getCurrentCase() in ingest, modules, recentactivity... --- .../DataContentViewerOtherCases.java | 36 ++++++++++++++----- .../directorytree/ExtractUnallocAction.java | 15 ++++++-- .../autopsy/ingest/IngestServices.java | 11 +++--- .../SevenZipExtractor.java | 18 ++++++---- .../autopsy/recentactivity/Extract.java | 11 ++++-- .../autopsy/recentactivity/ExtractIE.java | 7 ++-- .../recentactivity/RAImageIngestModule.java | 13 ++++--- 7 files changed, 79 insertions(+), 32 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java index c098112efa..c16d16d70c 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/contentviewer/DataContentViewerOtherCases.java @@ -1,7 +1,7 @@ /* * Central Repository * - * Copyright 2015-2017 Basis Technology Corp. + * Copyright 2015-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -46,6 +46,7 @@ import org.openide.nodes.Node; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.ServiceProvider; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttribute; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; @@ -97,7 +98,11 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D } else if (jmi.equals(showCaseDetailsMenuItem)) { showCaseDetails(otherCasesTable.getSelectedRow()); } else if (jmi.equals(exportToCSVMenuItem)) { - saveToCSV(); + try { + saveToCSV(); + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS + } } else if (jmi.equals(showCommonalityMenuItem)) { showCommonalityDetails(); } @@ -159,8 +164,19 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D @Messages({"DataContentViewerOtherCases.caseDetailsDialog.notSelected=No Row Selected", "DataContentViewerOtherCases.caseDetailsDialog.noDetails=No details for this case.", "DataContentViewerOtherCases.caseDetailsDialog.noDetailsReference=No case details for Global reference properties.", - "DataContentViewerOtherCases.caseDetailsDialog.noCaseNameError=Error"}) + "DataContentViewerOtherCases.caseDetailsDialog.noCaseNameError=Error", + "DataContentViewerOtherCases.noOpenCase.errMsg=No open case available."}) private void showCaseDetails(int selectedRowViewIdx) { + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, + Bundle.DataContentViewerOtherCases_noOpenCase_errMsg(), + Bundle.DataContentViewerOtherCases_noOpenCase_errMsg(), + DEFAULT_OPTION, PLAIN_MESSAGE); + return; + } String caseDisplayName = Bundle.DataContentViewerOtherCases_caseDetailsDialog_noCaseNameError(); try { if (-1 != selectedRowViewIdx) { @@ -177,7 +193,7 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D } caseDisplayName = eamCasePartial.getDisplayName(); // query case details - CorrelationCase eamCase = dbManager.getCase(Case.getCurrentCase()); + CorrelationCase eamCase = dbManager.getCase(openCase); if (eamCase == null) { JOptionPane.showConfirmDialog(showCaseDetailsMenuItem, Bundle.DataContentViewerOtherCases_caseDetailsDialog_noDetails(), @@ -205,11 +221,11 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D } } - private void saveToCSV() { + private void saveToCSV() throws NoCurrentCaseException { if (0 != otherCasesTable.getSelectedRowCount()) { Calendar now = Calendar.getInstance(); String fileName = String.format("%1$tY%1$tm%1$te%1$tI%1$tM%1$tS_other_data_sources.csv", now); - CSVFileChooser.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory())); + CSVFileChooser.setCurrentDirectory(new File(Case.getOpenCase().getExportDirectory())); CSVFileChooser.setSelectedFile(new File(fileName)); CSVFileChooser.setFileFilter(new FileNameExtensionFilter("csv file", "csv")); @@ -417,8 +433,8 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D */ private Collection getCorrelatedInstances(CorrelationAttribute corAttr, String dataSourceName, String deviceId) { // @@@ Check exception - String caseUUID = Case.getCurrentCase().getName(); try { + String caseUUID = Case.getOpenCase().getName(); EamDb dbManager = EamDb.getInstance(); Collection artifactInstances = dbManager.getArtifactInstancesByTypeValue(corAttr.getCorrelationType(), corAttr.getCorrelationValue()).stream() .filter(artifactInstance -> !artifactInstance.getCorrelationCase().getCaseUUID().equals(caseUUID) @@ -428,6 +444,8 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D return artifactInstances; } catch (EamDbException ex) { LOGGER.log(Level.SEVERE, "Error getting artifact instances from database.", ex); // NON-NLS + } catch (NoCurrentCaseException ex) { + LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); // NON-NLS } return Collections.emptyList(); @@ -473,9 +491,9 @@ public class DataContentViewerOtherCases extends javax.swing.JPanel implements D if (af != null) { Content dataSource = af.getDataSource(); dataSourceName = dataSource.getName(); - deviceId = Case.getCurrentCase().getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId(); + deviceId = Case.getOpenCase().getSleuthkitCase().getDataSource(dataSource.getId()).getDeviceId(); } - } catch (TskException ex) { + } catch (TskException | NoCurrentCaseException ex) { // do nothing. // @@@ Review this behavior } diff --git a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java index b415b190ea..1960089075 100644 --- a/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java +++ b/Core/src/org/sleuthkit/autopsy/directorytree/ExtractUnallocAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -41,6 +41,7 @@ import org.netbeans.api.progress.ProgressHandle; import org.openide.util.Cancellable; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.datamodel.AbstractContent; @@ -100,7 +101,8 @@ final class ExtractUnallocAction extends AbstractAction { "ExtractUnallocAction.volumeInProgress=Already extracting unallocated space into {0} - will skip this volume", "ExtractUnallocAction.volumeError=Error extracting unallocated space from volume", "ExtractUnallocAction.noFiles=No unallocated files found on volume", - "ExtractUnallocAction.imageError=Error extracting unallocated space from image"}) + "ExtractUnallocAction.imageError=Error extracting unallocated space from image", + "ExtractUnallocAction.noOpenCase.errMsg=No open case available."}) @Override public void actionPerformed(ActionEvent e) { if (filesToExtract != null && filesToExtract.size() > 0) { @@ -111,6 +113,13 @@ final class ExtractUnallocAction extends AbstractAction { //JOptionPane.showMessageDialog(new Frame(), "Unallocated Space is already being extracted on this Image. Please select a different Image."); return; } + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + MessageNotifyUtil.Message.info(Bundle.ExtractAction_noOpenCase_errMsg()); + return; + } List copyList = new ArrayList() { { addAll(filesToExtract); @@ -130,7 +139,7 @@ final class ExtractUnallocAction extends AbstractAction { } }; - fileChooser.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory())); + fileChooser.setCurrentDirectory(new File(openCase.getExportDirectory())); fileChooser.setDialogTitle( NbBundle.getMessage(this.getClass(), "ExtractUnallocAction.dlgTitle.selectDirToSaveTo.msg")); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java index 8310d36297..f70c9b0813 100644 --- a/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java +++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestServices.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.ingest; import java.util.Map; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ModuleSettings; import org.sleuthkit.datamodel.SleuthkitCase; @@ -53,18 +54,20 @@ public final class IngestServices { * Get the current Autopsy case. * * @return The current case. + * @throws NoCurrentCaseException if there is no open case. */ - public Case getCurrentCase() { - return Case.getCurrentCase(); + public Case getOpenCase() throws NoCurrentCaseException { + return Case.getOpenCase(); } /** * Get the current SleuthKit case. The SleuthKit case is the case database. * * @return The current case database. + * @throws NoCurrentCaseException if there is no open case. */ - public SleuthkitCase getCurrentSleuthkitCaseDb() { - return Case.getCurrentCase().getSleuthkitCase(); + public SleuthkitCase getCurrentSleuthkitCaseDb() throws NoCurrentCaseException { + return Case.getOpenCase().getSleuthkitCase(); } /** diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index ee63ac7754..e62ddee64b 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-2014 Basis Technology Corp. + * Copyright 2013-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -42,6 +42,7 @@ import org.netbeans.api.progress.ProgressHandle; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; import org.sleuthkit.autopsy.coreutils.FileUtil; @@ -252,7 +253,12 @@ class SevenZipExtractor { */ @Messages({"SevenZipExtractor.indexError.message=Failed to index encryption detected artifact for keyword search."}) void unpack(AbstractFile archiveFile) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); + try { + blackboard = Case.getOpenCase().getServices().getBlackboard(); + } catch (NoCurrentCaseException ex) { + logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS + return; + } String archiveFilePath; try { archiveFilePath = archiveFile.getUniquePath(); @@ -518,8 +524,8 @@ class SevenZipExtractor { } } - } catch (TskCoreException e) { - logger.log(Level.SEVERE, "Error populating complete derived file hierarchy from the unpacked dir structure"); //NON-NLS + } catch (TskCoreException | NoCurrentCaseException e) { + logger.log(Level.SEVERE, "Error populating complete derived file hierarchy from the unpacked dir structure", e); //NON-NLS //TODO decide if anything to cleanup, for now bailing } @@ -839,8 +845,8 @@ class SevenZipExtractor { * Traverse the tree top-down after unzipping is done and create derived * files for the entire hierarchy */ - void addDerivedFilesToCase() throws TskCoreException { - final FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); + void addDerivedFilesToCase() throws TskCoreException, NoCurrentCaseException { + final FileManager fileManager = Case.getOpenCase().getServices().getFileManager(); for (UnpackedNode child : rootNode.children) { addDerivedFilesToCaseRec(child, fileManager); } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java index 5af71ecec0..4cb6eaf8e3 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2012-2014 Basis Technology Corp. + * Copyright 2012-2018 Basis Technology Corp. * * Copyright 2012 42six Solutions. * Contact: aebadirad 42six com @@ -30,6 +30,7 @@ import java.util.logging.Level; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; @@ -106,15 +107,19 @@ abstract class Extract { * * @param bbart Blackboard artifact to be indexed */ - @Messages({"Extract.indexError.message=Failed to index artifact for keyword search."}) + @Messages({"Extract.indexError.message=Failed to index artifact for keyword search.", + "Extract.noOpenCase.errMsg=No open case available."}) void indexArtifact(BlackboardArtifact bbart) { - Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); try { + Blackboard blackboard = Case.getOpenCase().getServices().getBlackboard(); // index the artifact for keyword search blackboard.indexArtifact(bbart); } catch (Blackboard.BlackboardException ex) { logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error(Bundle.Extract_indexError_message(), bbart.getDisplayName()); + } catch (NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + MessageNotifyUtil.Notify.error(Bundle.Extract_noOpenCase_errMsg(), bbart.getDisplayName()); } } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java index a494f64f77..3e8bb2d611 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/ExtractIE.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * * Copyright 2012 42six Solutions. * Contact: aebadirad 42six com @@ -44,6 +44,7 @@ import java.util.Scanner; import java.util.stream.Collectors; import org.openide.modules.InstalledFileLocator; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; @@ -72,9 +73,9 @@ class ExtractIE extends Extract { private Content dataSource; private IngestJobContext context; - ExtractIE() { + ExtractIE() throws NoCurrentCaseException { moduleName = NbBundle.getMessage(ExtractIE.class, "ExtractIE.moduleName.text"); - moduleTempResultsDir = RAImageIngestModule.getRATempPath(Case.getCurrentCase(), "IE") + File.separator + "results"; //NON-NLS + moduleTempResultsDir = RAImageIngestModule.getRATempPath(Case.getOpenCase(), "IE") + File.separator + "results"; //NON-NLS JAVA_PATH = PlatformUtil.getJavaPath(); } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java index 9fdec551b4..7907db41bc 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/RAImageIngestModule.java @@ -2,7 +2,7 @@ * * Autopsy Forensic Browser * - * Copyright 2012-2014 Basis Technology Corp. + * Copyright 2012-2018 Basis Technology Corp. * * Copyright 2012 42six Solutions. * Contact: aebadirad 42six com @@ -27,9 +27,9 @@ import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; @@ -59,9 +59,14 @@ public final class RAImageIngestModule implements DataSourceIngestModule { public void startUp(IngestJobContext context) throws IngestModuleException { this.context = context; - + Extract iexplore; + try { + iexplore = new ExtractIE(); + } catch (NoCurrentCaseException ex) { + throw new IngestModuleException(ex.getMessage(), ex); + } + Extract registry = new ExtractRegistry(); - Extract iexplore = new ExtractIE(); Extract recentDocuments = new RecentDocumentsByLnk(); Extract chrome = new Chrome(); Extract firefox = new Firefox(); From 7eadf5c7acf8b5dc44f89a2dbf2b468d21c3d8c1 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Wed, 7 Mar 2018 14:57:10 -0500 Subject: [PATCH 097/128] Feature implemented to keep corrupted files. --- .../modules/photoreccarver/Bundle.properties | 2 + .../PhotoRecCarverFileIngestModule.java | 29 +++-- .../PhotoRecCarverIngestJobSettings.java | 70 ++++++++++++ .../PhotoRecCarverIngestJobSettingsPanel.form | 63 +++++++++++ .../PhotoRecCarverIngestJobSettingsPanel.java | 100 ++++++++++++++++++ .../PhotoRecCarverIngestModuleFactory.java | 26 ++++- 6 files changed, 281 insertions(+), 9 deletions(-) create mode 100755 Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java create mode 100755 Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form create mode 100755 Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties index 295f82f1cb..c47632b85f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties @@ -17,3 +17,5 @@ PhotoRecIngestModule.cancelledByUser=PhotoRec cancelled by user. PhotoRecIngestModule.error.exitValue=PhotoRec carver returned error exit value \= {0} when scanning {1} PhotoRecIngestModule.error.msg=Error processing {0} with PhotoRec carver. PhotoRecIngestModule.complete.numberOfErrors=Number of Errors while Carving\: +PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Carver Settings +PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text=Keep corrupted files diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index 1f0bafb892..bcf3168b1a 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -77,6 +77,8 @@ import org.sleuthkit.datamodel.TskData; }) final class PhotoRecCarverFileIngestModule implements FileIngestModule { + static final boolean DEFAULT_CONFIG_KEEP_CORRUPTED_FILES = false; + private static final String PHOTOREC_DIRECTORY = "photorec_exec"; //NON-NLS private static final String PHOTOREC_EXECUTABLE = "photorec_win.exe"; //NON-NLS private static final String PHOTOREC_LINUX_EXECUTABLE = "photorec"; @@ -96,13 +98,22 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { private IngestServices services; private UNCPathUtilities uncPathUtilities = new UNCPathUtilities(); private long jobId; + + private final boolean keepCorruptedFiles; private static class IngestJobTotals { - - private AtomicLong totalItemsRecovered = new AtomicLong(0); - private AtomicLong totalItemsWithErrors = new AtomicLong(0); - private AtomicLong totalWritetime = new AtomicLong(0); - private AtomicLong totalParsetime = new AtomicLong(0); + private final AtomicLong totalItemsRecovered = new AtomicLong(0); + private final AtomicLong totalItemsWithErrors = new AtomicLong(0); + private final AtomicLong totalWritetime = new AtomicLong(0); + private final AtomicLong totalParsetime = new AtomicLong(0); + } + /** + * Create a PhotoRec Carver ingest module instance. + * + * @param settings Ingest job settings used to configure the module. + */ + PhotoRecCarverFileIngestModule(PhotoRecCarverIngestJobSettings settings) { + keepCorruptedFiles = settings.isKeepCorruptedFiles(); } private static synchronized IngestJobTotals getTotalsForIngestJobs(long ingestJobId) { @@ -228,8 +239,12 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { "/d", // NON-NLS outputDirPath.toAbsolutePath().toString() + File.separator + PHOTOREC_RESULTS_BASE, "/cmd", // NON-NLS - tempFilePath.toFile().toString(), - "search"); // NON-NLS + tempFilePath.toFile().toString()); + if (keepCorruptedFiles) { + processAndSettings.command().add("options,keep_corrupted_file,search"); // NON-NLS + } else { + processAndSettings.command().add("search"); // NON-NLS + } // Add environment variable to force PhotoRec to run with the same permissions Autopsy uses processAndSettings.environment().put("__COMPAT_LAYER", "RunAsInvoker"); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java new file mode 100755 index 0000000000..7e03815980 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java @@ -0,0 +1,70 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.modules.photoreccarver; + +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; + +/** + * Ingest job settings for the PhotoRec Carver module. + */ +public class PhotoRecCarverIngestJobSettings implements IngestModuleIngestJobSettings { + + private static final long serialVersionUID = 1L; + + private boolean keepCorruptedFiles; + + /** + * Instantiate the ingest job settings with default values. + */ + PhotoRecCarverIngestJobSettings() { + this.keepCorruptedFiles = PhotoRecCarverFileIngestModule.DEFAULT_CONFIG_KEEP_CORRUPTED_FILES; + } + + /** + * Instantiate the ingest job settings. + * + * @param keepCorruptedFiles Keep corrupted files. + */ + PhotoRecCarverIngestJobSettings(boolean keepCorruptedFiles) { + this.keepCorruptedFiles = keepCorruptedFiles; + } + + @Override + public long getVersionNumber() { + return serialVersionUID; + } + + /** + * Are corrupted files being kept? + * + * @return True if keeping corrupted files; otherwise false. + */ + boolean isKeepCorruptedFiles() { + return keepCorruptedFiles; + } + + /** + * Keep or disgard corrupted files. + * + * @param keepCorruptedFiles Are corrupted files being kept? + */ + void setKeepCorruptedFiles(boolean keepCorruptedFiles) { + this.keepCorruptedFiles = keepCorruptedFiles; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form new file mode 100755 index 0000000000..b12034e682 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form @@ -0,0 +1,63 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java new file mode 100755 index 0000000000..28aa6c0762 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -0,0 +1,100 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.modules.photoreccarver; + +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; + +/** + * Ingest job settings panel for the Encryption Detection module. + */ +final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSettingsPanel { + + /** + * Instantiate the ingest job settings panel. + * + * @param settings The ingest job settings. + */ + public PhotoRecCarverIngestJobSettingsPanel(PhotoRecCarverIngestJobSettings settings) { + initComponents(); + customizeComponents(settings); + } + + /** + * Update components with values from the ingest job settings. + * + * @param settings The ingest job settings. + */ + private void customizeComponents(PhotoRecCarverIngestJobSettings settings) { + keepCorruptedFilesCheckbox.setSelected(settings.isKeepCorruptedFiles()); + } + + @Override + public IngestModuleIngestJobSettings getSettings() { + return new PhotoRecCarverIngestJobSettings( + keepCorruptedFilesCheckbox.isSelected()); + } + + /** + * 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") + // //GEN-BEGIN:initComponents + private void initComponents() { + + keepCorruptedFilesCheckbox = new javax.swing.JCheckBox(); + detectionSettingsLabel = new javax.swing.JLabel(); + + org.openide.awt.Mnemonics.setLocalizedText(keepCorruptedFilesCheckbox, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text")); // NOI18N + + detectionSettingsLabel.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(detectionSettingsLabel, org.openide.util.NbBundle.getMessage(PhotoRecCarverIngestJobSettingsPanel.class, "PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text")); // NOI18N + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(10, 10, 10) + .addComponent(keepCorruptedFilesCheckbox)) + .addComponent(detectionSettingsLabel)) + .addContainerGap(155, Short.MAX_VALUE)) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(detectionSettingsLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(keepCorruptedFilesCheckbox) + .addContainerGap(245, Short.MAX_VALUE)) + ); + }// //GEN-END:initComponents + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel detectionSettingsLabel; + private javax.swing.JCheckBox keepCorruptedFilesCheckbox; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java index 0898f20928..309422106f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java @@ -24,6 +24,7 @@ import org.sleuthkit.autopsy.ingest.FileIngestModule; import org.sleuthkit.autopsy.ingest.IngestModuleFactory; import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; +import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; /** * A factory for creating instances of file ingest modules that carve @@ -32,7 +33,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; @ServiceProvider(service = IngestModuleFactory.class) public class PhotoRecCarverIngestModuleFactory extends IngestModuleFactoryAdapter { - private static final String VERSION = "7.0"; + private static final String VERSION = "8.0"; /** * Gets the ingest module name for use within this package. @@ -65,7 +66,28 @@ public class PhotoRecCarverIngestModuleFactory extends IngestModuleFactoryAdapte @Override public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) { - return new PhotoRecCarverFileIngestModule(); + if (!(settings instanceof PhotoRecCarverIngestJobSettings)) { + throw new IllegalArgumentException("Expected settings argument to be an instance of PhotoRecCarverIngestJobSettings."); + } + return new PhotoRecCarverFileIngestModule((PhotoRecCarverIngestJobSettings) settings); + } + + @Override + public IngestModuleIngestJobSettings getDefaultIngestJobSettings() { + return new PhotoRecCarverIngestJobSettings(); + } + + @Override + public boolean hasIngestJobSettingsPanel() { + return true; + } + + @Override + public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) { + if (!(settings instanceof PhotoRecCarverIngestJobSettings)) { + throw new IllegalArgumentException("Expected settings argument to be an instance of PhotoRecCarverIngestJobSettings"); + } + return new PhotoRecCarverIngestJobSettingsPanel((PhotoRecCarverIngestJobSettings) settings); } } From 9054478e6b3724674dcbc1bc184fb39581e839c4 Mon Sep 17 00:00:00 2001 From: Brian Carrier Date: Wed, 7 Mar 2018 15:39:29 -0500 Subject: [PATCH 098/128] refined codacy --- ruleset.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ruleset.xml b/ruleset.xml index 88bfefaff2..07a44a73c9 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -114,7 +114,8 @@ - + From 3a010a9da949d8d09935c6e935db0065d82ad095 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Wed, 7 Mar 2018 17:27:11 -0500 Subject: [PATCH 099/128] Addressed IDE hints. --- .../photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form | 2 +- .../photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form index b12034e682..e367a696e0 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form @@ -25,7 +25,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index 28aa6c0762..529972c530 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.modules.photoreccarver; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; @@ -80,7 +79,7 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe .addGap(10, 10, 10) .addComponent(keepCorruptedFilesCheckbox)) .addComponent(detectionSettingsLabel)) - .addContainerGap(155, Short.MAX_VALUE)) + .addContainerGap(145, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) From dadffe7df2729a390500272f26bf48d16cd50f04 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Wed, 7 Mar 2018 17:45:54 -0500 Subject: [PATCH 100/128] Minor changes to conform to coding standards. --- .../photoreccarver/PhotoRecCarverIngestJobSettings.java | 2 +- .../photoreccarver/PhotoRecCarverIngestModuleFactory.java | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java index 7e03815980..93de74f1d6 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettings.java @@ -23,7 +23,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettings; /** * Ingest job settings for the PhotoRec Carver module. */ -public class PhotoRecCarverIngestJobSettings implements IngestModuleIngestJobSettings { +final class PhotoRecCarverIngestJobSettings implements IngestModuleIngestJobSettings { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java index 309422106f..64c4f5b605 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -66,9 +66,6 @@ public class PhotoRecCarverIngestModuleFactory extends IngestModuleFactoryAdapte @Override public FileIngestModule createFileIngestModule(IngestModuleIngestJobSettings settings) { - if (!(settings instanceof PhotoRecCarverIngestJobSettings)) { - throw new IllegalArgumentException("Expected settings argument to be an instance of PhotoRecCarverIngestJobSettings."); - } return new PhotoRecCarverFileIngestModule((PhotoRecCarverIngestJobSettings) settings); } @@ -84,9 +81,6 @@ public class PhotoRecCarverIngestModuleFactory extends IngestModuleFactoryAdapte @Override public IngestModuleIngestJobSettingsPanel getIngestJobSettingsPanel(IngestModuleIngestJobSettings settings) { - if (!(settings instanceof PhotoRecCarverIngestJobSettings)) { - throw new IllegalArgumentException("Expected settings argument to be an instance of PhotoRecCarverIngestJobSettings"); - } return new PhotoRecCarverIngestJobSettingsPanel((PhotoRecCarverIngestJobSettings) settings); } From e4dadfc56a3ea266ba492fcb0e86e4f755ea4d58 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Wed, 7 Mar 2018 17:51:21 -0500 Subject: [PATCH 101/128] Another IDE hint addressed. --- .../modules/photoreccarver/PhotoRecCarverFileIngestModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java index bcf3168b1a..0a4ccd9715 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverFileIngestModule.java @@ -96,7 +96,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule { private Path rootOutputDirPath; private File executableFile; private IngestServices services; - private UNCPathUtilities uncPathUtilities = new UNCPathUtilities(); + private final UNCPathUtilities uncPathUtilities = new UNCPathUtilities(); private long jobId; private final boolean keepCorruptedFiles; From eba55e86f97b3a4bf6e965d1037edec1cba3f561 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Thu, 8 Mar 2018 11:28:11 -0500 Subject: [PATCH 102/128] Added comment about module version number. --- .../photoreccarver/PhotoRecCarverIngestModuleFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java index 64c4f5b605..47658b55e0 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestModuleFactory.java @@ -33,7 +33,7 @@ import org.sleuthkit.autopsy.ingest.IngestModuleIngestJobSettingsPanel; @ServiceProvider(service = IngestModuleFactory.class) public class PhotoRecCarverIngestModuleFactory extends IngestModuleFactoryAdapter { - private static final String VERSION = "8.0"; + private static final String VERSION = "7.0"; // Version should match the PhotoRec tool version. /** * Gets the ingest module name for use within this package. From 6ef85988cf96cbf481ceaa8a8798a3914d630f77 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Thu, 8 Mar 2018 12:08:44 -0500 Subject: [PATCH 103/128] Resized settings panel to 300x200. --- .../autopsy/modules/encryptiondetection/Bundle.properties | 2 +- .../EncryptionDetectionIngestJobSettingsPanel.form | 4 ++-- .../EncryptionDetectionIngestJobSettingsPanel.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle.properties index 95ce1719ef..37cfd1d8b2 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/Bundle.properties @@ -1,6 +1,6 @@ EncryptionDetectionIngestJobSettingsPanel.minimumEntropyLabel.text=Minimum Entropy: EncryptionDetectionIngestJobSettingsPanel.minimumFileSizeLabel.text=Minimum File Size: -EncryptionDetectionIngestJobSettingsPanel.fileSizeMultiplesEnforcedCheckbox.text=Consider only files with sizes that are multiples of 512. +EncryptionDetectionIngestJobSettingsPanel.fileSizeMultiplesEnforcedCheckbox.text=Consider only file sizes that are multiples of 512. EncryptionDetectionIngestJobSettingsPanel.slackFilesAllowedCheckbox.text=Consider slack space files. EncryptionDetectionIngestJobSettingsPanel.mbLabel.text=MB EncryptionDetectionIngestJobSettingsPanel.detectionSettingsLabel.text=Detection Settings diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.form index c2bb97607e..2f66c65b81 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.form @@ -35,7 +35,7 @@ - + @@ -59,7 +59,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java index eb318ccdfd..b4e2ed487a 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/encryptiondetection/EncryptionDetectionIngestJobSettingsPanel.java @@ -174,7 +174,7 @@ final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngest .addComponent(minimumEntropyLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(minimumEntropyTextbox, javax.swing.GroupLayout.PREFERRED_SIZE, 32, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addContainerGap(15, Short.MAX_VALUE)) + .addContainerGap(33, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -194,7 +194,7 @@ final class EncryptionDetectionIngestJobSettingsPanel extends IngestModuleIngest .addComponent(fileSizeMultiplesEnforcedCheckbox) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(slackFilesAllowedCheckbox) - .addContainerGap(160, Short.MAX_VALUE)) + .addContainerGap(60, Short.MAX_VALUE)) ); }// //GEN-END:initComponents From e2ee2d6ba0b8002104e6301611cc3afe00d6fee9 Mon Sep 17 00:00:00 2001 From: Ann Priestman Date: Thu, 8 Mar 2018 12:36:02 -0500 Subject: [PATCH 104/128] Dispose of progress bar dialog when complete --- .../sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java b/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java index d49574bf24..3273d2c5f9 100644 --- a/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java +++ b/Core/src/org/sleuthkit/autopsy/progress/ModalDialogProgressIndicator.java @@ -227,6 +227,7 @@ public final class ModalDialogProgressIndicator implements ProgressIndicator { public synchronized void finish() { SwingUtilities.invokeLater(() -> { this.dialog.setVisible(false); + this.dialog.dispose(); }); } From 878f166af41684c25a8556e1dddd03938d395f53 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Thu, 8 Mar 2018 14:29:17 -0500 Subject: [PATCH 105/128] Fixed Application panel; work in progress for Central Repo. --- .../optionspanel/Bundle.properties | 5 ++++ .../optionspanel/GlobalSettingsPanel.form | 6 ++--- .../optionspanel/GlobalSettingsPanel.java | 6 ++--- .../corecomponents/AutopsyOptionsPanel.form | 27 ++++++++++++++++--- .../corecomponents/AutopsyOptionsPanel.java | 18 +++++++++---- .../autopsy/corecomponents/Bundle.properties | 10 +++++++ 6 files changed, 57 insertions(+), 15 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties index 9faba7a7b3..dfca6b70b7 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties @@ -58,3 +58,8 @@ ManageCorrelationPropertiesDialog.okButton.text=OK GlobalSettingsPanel.bnManageProperties.text=Manage Correlation Properties EamDbSettingsDialog.lbDatabaseDesc.text=Database File: EamDbSettingsDialog.lbFullDbPath.text= +GlobalSettingsPanel.cbUseCentralRepo.text=Use a central repository +GlobalSettingsPanel.correlationPropertiesTextArea.text=Choose which file and result properties to store in the central repository for later correlation.\n +GlobalSettingsPanel.organizationTextArea.text=Organization information can be tracked in the central repository. +GlobalSettingsPanel.manageOrganizationButton.text=Manage Organizations +GlobalSettingsPanel.lbCentralRepository.text=A central repository allows you to correlate files and results between cases. diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form index c53942cc0c..fb3b1e2cd3 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form @@ -25,9 +25,9 @@ - + - + @@ -307,7 +307,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java index 8b0a1b6b93..7f7b282551 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java @@ -287,7 +287,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addComponent(organizationScrollPane) .addGroup(organizationPanelLayout.createSequentialGroup() .addComponent(manageOrganizationButton) - .addGap(0, 0, Short.MAX_VALUE))) + .addGap(0, 308, Short.MAX_VALUE))) .addContainerGap()) ); organizationPanelLayout.setVerticalGroup( @@ -310,9 +310,9 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addComponent(tbOops, javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(organizationPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) + .addComponent(organizationPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(lbCentralRepository, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 349, Short.MAX_VALUE) + .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 479, Short.MAX_VALUE) .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(cbUseCentralRepo, javax.swing.GroupLayout.Alignment.LEADING)) .addContainerGap()))) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form index 0ee7b6314e..a872ca5c76 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form @@ -9,6 +9,11 @@ + + + + + @@ -24,12 +29,15 @@ - + - + + + + @@ -39,6 +47,9 @@ + + +
@@ -46,7 +57,7 @@ - + @@ -56,7 +67,7 @@ - + @@ -92,6 +103,9 @@ + + + @@ -441,6 +455,11 @@ + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java index 51a374260a..91e190757d 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java @@ -572,11 +572,15 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { logFileCount = new javax.swing.JTextField(); logNumAlert = new javax.swing.JTextField(); - jScrollPane1.setBorder(null); + setPreferredSize(new java.awt.Dimension(1022, 488)); - jPanel1.setPreferredSize(new java.awt.Dimension(671, 488)); + jScrollPane1.setBorder(null); + jScrollPane1.setPreferredSize(new java.awt.Dimension(1022, 407)); + + jPanel1.setPreferredSize(new java.awt.Dimension(1022, 407)); logoPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.logoPanel.border.title"))); // NOI18N + logoPanel.setPreferredSize(new java.awt.Dimension(533, 87)); agencyLogoPathField.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoPathField.text")); // NOI18N @@ -894,7 +898,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() .addContainerGap() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(logoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(logoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 1010, Short.MAX_VALUE) .addGroup(jPanel1Layout.createSequentialGroup() .addComponent(viewPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) @@ -913,17 +917,21 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addContainerGap()) ); + runtimePanel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.runtimePanel.AccessibleContext.accessibleName")); // NOI18N + jScrollPane1.setViewportView(jPanel1); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 1010, Short.MAX_VALUE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createSequentialGroup() + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 479, Short.MAX_VALUE) + .addGap(0, 0, Short.MAX_VALUE)) ); }// //GEN-END:initComponents diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 3976f755f0..0055b0e5f8 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -169,3 +169,13 @@ CriterionChooser.removeButton.text=Remove CriterionChooser.descendingRadio.text=\u25bc Descending AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text= AutopsyOptionsPanel.logNumAlert.text= +AutopsyOptionsPanel.runtimePanel.AccessibleContext.accessibleName= +AutopsyOptionsPanel.totalMemoryLabel.text=Total System Memory: +AutopsyOptionsPanel.maxMemoryLabel.text=Maximum JVM Memory: +AutopsyOptionsPanel.maxLogFileCount.text=Maximum Log Files: +AutopsyOptionsPanel.maxMemoryUnitsLabel.text=GB +AutopsyOptionsPanel.restartNecessaryWarning.text=A restart is necessary for any changes to max memory to take effect. +AutopsyOptionsPanel.browseLogosButton.text=Browse +AutopsyOptionsPanel.agencyLogoPreview.text=
No logo
selected
+AutopsyOptionsPanel.defaultLogoRB.text=Use default +AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo From 71de2410ff17cc734f78553ab7de013b1104a765 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Thu, 8 Mar 2018 14:50:05 -0500 Subject: [PATCH 106/128] Modified sizing and label in settings panel. --- .../autopsy/modules/photoreccarver/Bundle.properties | 2 +- .../photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form | 4 ++-- .../photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties index c47632b85f..f4089caff7 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/Bundle.properties @@ -17,5 +17,5 @@ PhotoRecIngestModule.cancelledByUser=PhotoRec cancelled by user. PhotoRecIngestModule.error.exitValue=PhotoRec carver returned error exit value \= {0} when scanning {1} PhotoRecIngestModule.error.msg=Error processing {0} with PhotoRec carver. PhotoRecIngestModule.complete.numberOfErrors=Number of Errors while Carving\: -PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Carver Settings +PhotoRecCarverIngestJobSettingsPanel.detectionSettingsLabel.text=PhotoRec Settings PhotoRecCarverIngestJobSettingsPanel.keepCorruptedFilesCheckbox.text=Keep corrupted files diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form index e367a696e0..2fe8e89dba 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.form @@ -25,7 +25,7 @@
- +
@@ -36,7 +36,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java index 529972c530..9af6902a2d 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/photoreccarver/PhotoRecCarverIngestJobSettingsPanel.java @@ -79,7 +79,7 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe .addGap(10, 10, 10) .addComponent(keepCorruptedFilesCheckbox)) .addComponent(detectionSettingsLabel)) - .addContainerGap(145, Short.MAX_VALUE)) + .addContainerGap(159, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -88,7 +88,7 @@ final class PhotoRecCarverIngestJobSettingsPanel extends IngestModuleIngestJobSe .addComponent(detectionSettingsLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(keepCorruptedFilesCheckbox) - .addContainerGap(245, Short.MAX_VALUE)) + .addContainerGap(145, Short.MAX_VALUE)) ); }// //GEN-END:initComponents From 39aa20a0bb486c24064066d0568ee22c5990237f Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Thu, 8 Mar 2018 16:32:32 -0500 Subject: [PATCH 107/128] Finished Central Repo panel; removed redundant strings. --- .../optionspanel/GlobalSettingsPanel.form | 661 ++++++++++-------- .../optionspanel/GlobalSettingsPanel.java | 118 ++-- .../corecomponents/AutopsyOptionsPanel.form | 5 + .../corecomponents/AutopsyOptionsPanel.java | 13 +- .../autopsy/corecomponents/Bundle.properties | 2 +- 5 files changed, 434 insertions(+), 365 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form index fb3b1e2cd3..17e79fc12e 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form @@ -3,6 +3,9 @@
+ + + @@ -19,352 +22,396 @@ - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - + + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - + + - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + - - - - - - - + - diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java index 7f7b282551..033e8406c9 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java @@ -59,17 +59,12 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i } @Messages({"GlobalSettingsPanel.title=Central Repository Settings", - "GlobalSettingsPanel.cbUseCentralRepo.text=Use a central repository", "GlobalSettingsPanel.pnTagManagement.border.title=Tags", "GlobalSettingsPanel.pnCorrelationProperties.border.title=Correlation Properties", - "GlobalSettingsPanel.lbCentralRepository.text=A central repository allows you to correlate files and results between cases.", "GlobalSettingsPanel.manageTagsTextArea.text=Configure which tag names are associated with notable items. " + "When these tags are used, the file or result will be recorded in the central repository. " + "If that file or result is seen again in future cases, it will be flagged.", - "GlobalSettingsPanel.correlationPropertiesTextArea.text=Choose which file and result properties to store in the central repository for later correlation.", "GlobalSettingsPanel.organizationPanel.border.title=Organizations", - "GlobalSettingsPanel.manageOrganizationButton.text=Manage Organizations", - "GlobalSettingsPanel.organizationTextArea.text=Organization information can be tracked in the central repository" }) private void customizeComponents() { @@ -116,6 +111,10 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i // //GEN-BEGIN:initComponents private void initComponents() { + jScrollPane1 = new javax.swing.JScrollPane(); + jPanel1 = new javax.swing.JPanel(); + lbCentralRepository = new javax.swing.JLabel(); + cbUseCentralRepo = new javax.swing.JCheckBox(); pnDatabaseConfiguration = new javax.swing.JPanel(); lbDbPlatformTypeLabel = new javax.swing.JLabel(); lbDbNameLabel = new javax.swing.JLabel(); @@ -124,19 +123,34 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i lbDbPlatformValue = new javax.swing.JLabel(); lbDbNameValue = new javax.swing.JLabel(); lbDbLocationValue = new javax.swing.JLabel(); - cbUseCentralRepo = new javax.swing.JCheckBox(); - tbOops = new javax.swing.JTextField(); pnCorrelationProperties = new javax.swing.JPanel(); bnManageTypes = new javax.swing.JButton(); correlationPropertiesScrollPane = new javax.swing.JScrollPane(); correlationPropertiesTextArea = new javax.swing.JTextArea(); - lbCentralRepository = new javax.swing.JLabel(); organizationPanel = new javax.swing.JPanel(); manageOrganizationButton = new javax.swing.JButton(); organizationScrollPane = new javax.swing.JScrollPane(); organizationTextArea = new javax.swing.JTextArea(); + tbOops = new javax.swing.JTextField(); setName(""); // NOI18N + setPreferredSize(new java.awt.Dimension(1022, 488)); + + jScrollPane1.setBorder(null); + jScrollPane1.setMinimumSize(new java.awt.Dimension(21, 22)); + jScrollPane1.setPreferredSize(new java.awt.Dimension(1022, 407)); + + jPanel1.setMinimumSize(new java.awt.Dimension(0, 0)); + jPanel1.setPreferredSize(new java.awt.Dimension(1020, 407)); + + org.openide.awt.Mnemonics.setLocalizedText(lbCentralRepository, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.lbCentralRepository.text")); // NOI18N + + org.openide.awt.Mnemonics.setLocalizedText(cbUseCentralRepo, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.cbUseCentralRepo.text")); // NOI18N + cbUseCentralRepo.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + cbUseCentralRepoActionPerformed(evt); + } + }); pnDatabaseConfiguration.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.pnDatabaseConfiguration.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N @@ -194,18 +208,6 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addGap(8, 8, 8)) ); - org.openide.awt.Mnemonics.setLocalizedText(cbUseCentralRepo, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.cbUseCentralRepo.text")); // NOI18N - cbUseCentralRepo.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - cbUseCentralRepoActionPerformed(evt); - } - }); - - tbOops.setEditable(false); - tbOops.setFont(tbOops.getFont().deriveFont(tbOops.getFont().getStyle() | java.awt.Font.BOLD, 12)); - tbOops.setText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.tbOops.text")); // NOI18N - tbOops.setBorder(null); - pnCorrelationProperties.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.pnCorrelationProperties.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N pnCorrelationProperties.setPreferredSize(new java.awt.Dimension(674, 93)); @@ -253,8 +255,6 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addGap(8, 8, 8)) ); - org.openide.awt.Mnemonics.setLocalizedText(lbCentralRepository, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.lbCentralRepository.text")); // NOI18N - organizationPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(null, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.organizationPanel.border.title"), javax.swing.border.TitledBorder.DEFAULT_JUSTIFICATION, javax.swing.border.TitledBorder.DEFAULT_POSITION, new java.awt.Font("Tahoma", 0, 12))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(manageOrganizationButton, org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.manageOrganizationButton.text")); // NOI18N @@ -287,7 +287,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addComponent(organizationScrollPane) .addGroup(organizationPanelLayout.createSequentialGroup() .addComponent(manageOrganizationButton) - .addGap(0, 308, Short.MAX_VALUE))) + .addGap(0, 0, Short.MAX_VALUE))) .addContainerGap()) ); organizationPanelLayout.setVerticalGroup( @@ -300,38 +300,58 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addGap(8, 8, 8)) ); + tbOops.setEditable(false); + tbOops.setFont(tbOops.getFont().deriveFont(tbOops.getFont().getStyle() | java.awt.Font.BOLD, 12)); + tbOops.setText(org.openide.util.NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.tbOops.text")); // NOI18N + tbOops.setBorder(null); + + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(lbCentralRepository, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(cbUseCentralRepo) + .addGap(0, 0, Short.MAX_VALUE)) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, 974, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(36, Short.MAX_VALUE)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 1010, Short.MAX_VALUE) + .addComponent(organizationPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addContainerGap()) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addComponent(lbCentralRepository) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(cbUseCentralRepo) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(organizationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 92, Short.MAX_VALUE)) + ); + + jScrollPane1.setViewportView(jPanel1); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(tbOops, javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(organizationPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(lbCentralRepository, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 479, Short.MAX_VALUE) - .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(cbUseCentralRepo, javax.swing.GroupLayout.Alignment.LEADING)) - .addContainerGap()))) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(lbCentralRepository) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(cbUseCentralRepo) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0) - .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0) - .addComponent(organizationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0) - .addComponent(tbOops, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 488, Short.MAX_VALUE) ); }// //GEN-END:initComponents @@ -523,6 +543,8 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i private javax.swing.JCheckBox cbUseCentralRepo; private javax.swing.JScrollPane correlationPropertiesScrollPane; private javax.swing.JTextArea correlationPropertiesTextArea; + private javax.swing.JPanel jPanel1; + private javax.swing.JScrollPane jScrollPane1; private javax.swing.JLabel lbCentralRepository; private javax.swing.JLabel lbDbLocationLabel; private javax.swing.JLabel lbDbLocationValue; diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form index a872ca5c76..829c646f76 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form @@ -193,6 +193,11 @@ + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java index 91e190757d..be53fd752f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java @@ -50,15 +50,12 @@ import org.sleuthkit.autopsy.report.ReportBranding; /** * Options panel that allow users to set application preferences. */ -@Messages({"AutopsyOptionsPanel.agencyLogoPreview.text=
No logo
selected
", +@Messages({ + "AutopsyOptionsPanel.agencyLogoPreview.text=
No logo
selected
", "AutopsyOptionsPanel.logoPanel.border.title=Logo", "AutopsyOptionsPanel.viewPanel.border.title=View", "AutopsyOptionsPanel.invalidImageFile.msg=The selected file was not able to be used as an agency logo.", "AutopsyOptionsPanel.invalidImageFile.title=Invalid Image File", - "AutopsyOptionsPanel.restartNecessaryWarning.text=A restart is necessary for any changes to max memory to take effect.", - "AutopsyOptionsPanel.totalMemoryLabel.text=Total System Memory:", - "AutopsyOptionsPanel.maxMemoryLabel.text=Maximum JVM Memory:", - "AutopsyOptionsPanel.maxMemoryUnitsLabel.text=GB", "AutopsyOptionsPanel.runtimePanel.border.title=Runtime", "AutopsyOptionsPanel.memFieldValidationLabel.not64BitInstall.text=JVM memory settings only enabled for 64 bit version", "AutopsyOptionsPanel.memFieldValidationLabel.noValueEntered.text=No value entered", @@ -68,13 +65,9 @@ import org.sleuthkit.autopsy.report.ReportBranding; "# {0} - systemMemory", "AutopsyOptionsPanel.memFieldValidationLabel.overMaxMemory.text=Value must be less than the total system memory of {0}GB", "AutopsyOptionsPanel.memFieldValidationLabel.developerMode.text=Memory settings are not available while running in developer mode", - "AutopsyOptionsPanel.defaultLogoRB.text=Use default", - "AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo", - "AutopsyOptionsPanel.browseLogosButton.text=Browse", "AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.invalidPath.text=Path is not valid.", "AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.invalidImageSpecified.text=Invalid image file specified.", "AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.pathNotSet.text=Agency logo path must be set.", - "AutopsyOptionsPanel.maxLogFileCount.text=Maximum Log Files:", "AutopsyOptionsPanel.logNumAlert.invalidInput.text=A positive integer is required here." }) @@ -654,6 +647,8 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addGap(0, 0, Short.MAX_VALUE)) ); + agencyLogoPreview.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoPreview.AccessibleContext.accessibleName")); // NOI18N + viewPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.viewPanel.border.title"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectFile, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelSelectFile.text")); // NOI18N diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index 0055b0e5f8..ddb22f487b 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -176,6 +176,6 @@ AutopsyOptionsPanel.maxLogFileCount.text=Maximum Log Files: AutopsyOptionsPanel.maxMemoryUnitsLabel.text=GB AutopsyOptionsPanel.restartNecessaryWarning.text=A restart is necessary for any changes to max memory to take effect. AutopsyOptionsPanel.browseLogosButton.text=Browse -AutopsyOptionsPanel.agencyLogoPreview.text=
No logo
selected
AutopsyOptionsPanel.defaultLogoRB.text=Use default AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo +AutopsyOptionsPanel.agencyLogoPreview.AccessibleContext.accessibleName= From 73c97dc2bd922bc41c7c1a529a38b14e8e3ed4d9 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\zhaohui" Date: Fri, 9 Mar 2018 15:47:47 -0500 Subject: [PATCH 108/128] 2229: Part 26: Use getOpenCase() instead of getCurrentCase() in casemodule, modules, imageGallery. --- .../AddImageWizardAddingProgressPanel.java | 20 ++++++++++---- .../hashdatabase/HashDbIngestModule.java | 5 ++-- .../hashdatabase/HashLookupModuleFactory.java | 9 +++++-- .../autopsy/modules/iOS/CallLogAnalyzer.java | 2 +- .../modules/iOS/TextMessageAnalyzer.java | 2 +- .../imagegallery/ImageGalleryController.java | 6 +++-- .../ImageGalleryOptionsPanel.java | 27 ++++++++++++++----- 7 files changed, 51 insertions(+), 20 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java index 6204d8fb27..e4034d65b3 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/AddImageWizardAddingProgressPanel.java @@ -29,6 +29,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.UUID; +import java.util.logging.Level; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; @@ -46,6 +47,7 @@ import org.sleuthkit.autopsy.ingest.IngestJobSettings; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.runIngestModuleWizard.ShortcutWizardDescriptorPanel; import org.sleuthkit.datamodel.Content; +import org.sleuthkit.autopsy.coreutils.Logger; /** * The final panel of the add image wizard. It displays a progress bar and @@ -331,7 +333,11 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel { cleanupTask.enable(); new Thread(() -> { - Case.getCurrentCase().notifyAddingDataSource(dataSourceId); + try { + Case.getOpenCase().notifyAddingDataSource(dataSourceId); + } catch (NoCurrentCaseException ex) { + Logger.getLogger(AddImageWizardAddingProgressVisual.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + } }).start(); DataSourceProcessorCallback cbObj = new DataSourceProcessorCallback() { @Override @@ -398,10 +404,14 @@ class AddImageWizardAddingProgressPanel extends ShortcutWizardDescriptorPanel { //notify the UI of the new content added to the case new Thread(() -> { - if (!contents.isEmpty()) { - Case.getCurrentCase().notifyDataSourceAdded(contents.get(0), dataSourceId); - } else { - Case.getCurrentCase().notifyFailedAddingDataSource(dataSourceId); + try { + if (!contents.isEmpty()) { + Case.getOpenCase().notifyDataSourceAdded(contents.get(0), dataSourceId); + } else { + Case.getOpenCase().notifyFailedAddingDataSource(dataSourceId); + } + } catch (NoCurrentCaseException ex) { + Logger.getLogger(AddImageWizardAddingProgressVisual.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS } }).start(); diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java index 1a11c6702f..92e2989e64 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java @@ -62,7 +62,7 @@ public class HashDbIngestModule implements FileIngestModule { private static final Logger logger = Logger.getLogger(HashDbIngestModule.class.getName()); private static final int MAX_COMMENT_SIZE = 500; private final IngestServices services = IngestServices.getInstance(); - private final SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase(); + private final SleuthkitCase skCase; private final HashDbManager hashDbManager = HashDbManager.getInstance(); private final HashLookupModuleSettings settings; private List knownBadHashSets = new ArrayList<>(); @@ -88,8 +88,9 @@ public class HashDbIngestModule implements FileIngestModule { return totals; } - HashDbIngestModule(HashLookupModuleSettings settings) { + HashDbIngestModule(HashLookupModuleSettings settings) throws NoCurrentCaseException { this.settings = settings; + skCase = Case.getOpenCase().getSleuthkitCase(); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java index 2fb515832b..257f90ad2f 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashLookupModuleFactory.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.List; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Version; import org.sleuthkit.autopsy.ingest.IngestModuleFactoryAdapter; import org.sleuthkit.autopsy.ingest.FileIngestModule; @@ -105,6 +106,10 @@ public class HashLookupModuleFactory extends IngestModuleFactoryAdapter { throw new IllegalArgumentException( NbBundle.getMessage(this.getClass(), "HashLookupModuleFactory.createFileIngestModule.exception.msg")); } - return new HashDbIngestModule((HashLookupModuleSettings) settings); + try { + return new HashDbIngestModule((HashLookupModuleSettings) settings); + } catch (NoCurrentCaseException ex) { + throw new IllegalArgumentException("Exception while getting open case.", ex); + } } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java index c337a9efe3..976f8aefac 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java @@ -80,7 +80,7 @@ final class CallLogAnalyzer { } for (AbstractFile file : absFiles) { try { - jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), file.getName().replaceAll("[<>%|\"/:*\\\\]", "")); + jFile = new java.io.File(Case.getOpenCase().getTempDirectory(), file.getName().replaceAll("[<>%|\"/:*\\\\]", "")); dbPath = jFile.toString(); //path of file as string fileId = file.getId(); ContentUtils.writeToFile(file, jFile, context::dataSourceIngestIsCancelled); diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java index 87bc5f5600..dd3cc14d5b 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java @@ -82,7 +82,7 @@ class TextMessageAnalyzer { } for (AbstractFile file : absFiles) { try { - jFile = new java.io.File(Case.getCurrentCase().getTempDirectory(), file.getName().replaceAll("[<>%|\"/:*\\\\]", "")); + jFile = new java.io.File(Case.getOpenCase().getTempDirectory(), file.getName().replaceAll("[<>%|\"/:*\\\\]", "")); dbPath = jFile.toString(); //path of file as string fileId = file.getId(); ContentUtils.writeToFile(file, jFile, context::dataSourceIngestIsCancelled); diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index d802136a1c..5c9acf45bf 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -193,8 +193,10 @@ public final class ImageGalleryController { Platform.runLater(() -> { stale.set(b); }); - if (Case.isCaseOpen()) { - new PerCaseProperties(Case.getCurrentCase()).setConfigSetting(ImageGalleryModule.getModuleName(), PerCaseProperties.STALE, b.toString()); + try { + new PerCaseProperties(Case.getOpenCase()).setConfigSetting(ImageGalleryModule.getModuleName(), PerCaseProperties.STALE, b.toString()); + } catch (NoCurrentCaseException ex) { + Logger.getLogger(ImageGalleryController.class.getName()).log(Level.WARNING, "Exception while getting open case."); //NON-NLS } } diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryOptionsPanel.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryOptionsPanel.java index 6437566fa8..b97a116ad8 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryOptionsPanel.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryOptionsPanel.java @@ -19,9 +19,12 @@ package org.sleuthkit.autopsy.imagegallery; import java.awt.event.ActionEvent; +import java.util.logging.Level; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.ingest.IngestManager; +import org.sleuthkit.autopsy.coreutils.Logger; /** * The Image/Video Gallery panel in the NetBeans provided Options Dialogs @@ -183,10 +186,15 @@ final class ImageGalleryOptionsPanel extends javax.swing.JPanel { void load() { enabledByDefaultBox.setSelected(ImageGalleryPreferences.isEnabledByDefault()); - if (Case.isCaseOpen() && IngestManager.getInstance().isIngestRunning() == false) { - enabledForCaseBox.setEnabled(true); - enabledForCaseBox.setSelected(ImageGalleryModule.isEnabledforCase(Case.getCurrentCase())); - } else { + try { + if (IngestManager.getInstance().isIngestRunning() == false) { + enabledForCaseBox.setEnabled(true); + enabledForCaseBox.setSelected(ImageGalleryModule.isEnabledforCase(Case.getOpenCase())); + } else { + enabledForCaseBox.setEnabled(false); + enabledForCaseBox.setSelected(enabledByDefaultBox.isSelected()); + } + } catch (NoCurrentCaseException ex) { enabledForCaseBox.setEnabled(false); enabledForCaseBox.setSelected(enabledByDefaultBox.isSelected()); } @@ -194,11 +202,16 @@ final class ImageGalleryOptionsPanel extends javax.swing.JPanel { } void store() { + Case openCase; + try { + openCase = Case.getOpenCase(); + } catch (NoCurrentCaseException ex) { + Logger.getLogger(ImageGalleryOptionsPanel.class.getName()).log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS + return; + } ImageGalleryPreferences.setEnabledByDefault(enabledByDefaultBox.isSelected()); ImageGalleryController.getDefault().setListeningEnabled(enabledForCaseBox.isSelected()); - if (Case.isCaseOpen()) { - new PerCaseProperties(Case.getCurrentCase()).setConfigSetting(ImageGalleryModule.getModuleName(), PerCaseProperties.ENABLED, Boolean.toString(enabledForCaseBox.isSelected())); - } + new PerCaseProperties(openCase).setConfigSetting(ImageGalleryModule.getModuleName(), PerCaseProperties.ENABLED, Boolean.toString(enabledForCaseBox.isSelected())); ImageGalleryPreferences.setGroupCategorizationWarningDisabled(groupCategorizationWarningBox.isSelected()); } From d57985ac8cd2497b0cce65832998f449586e4cc8 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 12 Mar 2018 13:05:58 +0100 Subject: [PATCH 109/128] enable "View File in Directory" action for attachments --- .../autopsy/contentviewers/MessageContentViewer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 228dc09c13..a5b86e9d6c 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 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -706,7 +706,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont private static class AttachmentNode extends FileNode { AttachmentNode(AbstractFile file) { - super(file, true); + super(file, false); } @Override From 53c3718e039a54314c0469bcce5c0ae0b21b2dc3 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 12 Mar 2018 13:08:24 +0100 Subject: [PATCH 110/128] add Tags property to RelationshipNodes and AttachementNodes, enabling highlighting. --- .../communications/RelationshipNode.java | 5 +++- .../datamodel/BlackboardArtifactNode.java | 26 ++++++++++++++++--- .../autopsy/datamodel/Bundle.properties | 2 -- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index e952573f50..d83eaa4fa3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -46,7 +46,7 @@ import org.sleuthkit.datamodel.TskCoreException; 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"); @@ -113,6 +113,9 @@ final class RelationshipNode extends BlackboardArtifactNode { break; } } + + addTagProperty(ss); + return s; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 33dd6a2cc0..6309c23722 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -29,6 +29,7 @@ import java.util.EnumSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.MissingResourceException; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; @@ -78,8 +79,12 @@ public class BlackboardArtifactNode extends AbstractContentNode> customProperties; + private final static String NO_DESCR = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.noDesc.text"); + + /* * Artifact types which should have the full unique path of the associated * content as a property. @@ -310,7 +315,6 @@ public class BlackboardArtifactNode extends AbstractContentNode map = new LinkedHashMap<>(); fillPropertyMap(map, artifact); @@ -438,6 +442,22 @@ public class BlackboardArtifactNode extends AbstractContentNode tags = new ArrayList<>(); try { @@ -446,10 +466,8 @@ public class BlackboardArtifactNode extends AbstractContentNode("Tags", NbBundle.getMessage(AbstractAbstractFileNode.class, "BlackboardArtifactNode.createSheet.tags.displayName"), + ss.put(new NodeProperty<>("Tags", Bundle.BlackboardArtifactNode_createSheet_tags_displayName(), NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", ")))); - - return s; } private void updateSheet() { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties index 7063656562..733c49e8b7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Bundle.properties @@ -207,8 +207,6 @@ DeleteReportAction.actionDisplayName.multipleReports=Delete Reports DeleteReportAction.actionPerformed.showConfirmDialog.title=Confirm Deletion DeleteReportAction.actionPerformed.showConfirmDialog.single.msg=Do you want to delete 1 report from the case? DeleteReportAction.actionPerformed.showConfirmDialog.multiple.msg=Do you want to delete {0} reports from the case? -BlackboardArtifactNode.createSheet.tags.name=Tags -BlackboardArtifactNode.createSheet.tags.displayName=Tags FileTypeExtensionFilters.tskImgFilter.text=Images FileTypeExtensionFilters.tskVideoFilter.text=Videos FileTypeExtensionFilters.tskAudioFilter.text=Audio From 06e461890ad8f9f92088e09c67cd95d567386212 Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dgrove" Date: Mon, 12 Mar 2018 13:41:58 -0400 Subject: [PATCH 111/128] Improved bundle message references. --- .../optionspanel/Bundle.properties | 2 ++ .../optionspanel/GlobalSettingsPanel.form | 9 +++------ .../optionspanel/GlobalSettingsPanel.java | 14 ++------------ .../corecomponents/AutopsyOptionsPanel.form | 16 +++------------- .../corecomponents/AutopsyOptionsPanel.java | 10 +--------- .../autopsy/corecomponents/Bundle.properties | 6 ++++-- 6 files changed, 15 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties index dfca6b70b7..0106566831 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/Bundle.properties @@ -63,3 +63,5 @@ GlobalSettingsPanel.correlationPropertiesTextArea.text=Choose which file and res GlobalSettingsPanel.organizationTextArea.text=Organization information can be tracked in the central repository. GlobalSettingsPanel.manageOrganizationButton.text=Manage Organizations GlobalSettingsPanel.lbCentralRepository.text=A central repository allows you to correlate files and results between cases. +GlobalSettingsPanel.pnCorrelationProperties.border.title=Correlation Properties +GlobalSettingsPanel.organizationPanel.border.title=Organizations diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form index 17e79fc12e..788fef4660 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.form @@ -37,9 +37,6 @@ - - - @@ -73,7 +70,7 @@ - + @@ -225,7 +222,7 @@ - + @@ -317,7 +314,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java index 033e8406c9..28a34d6777 100644 --- a/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/centralrepository/optionspanel/GlobalSettingsPanel.java @@ -58,17 +58,8 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i addIngestJobEventsListener(); } - @Messages({"GlobalSettingsPanel.title=Central Repository Settings", - "GlobalSettingsPanel.pnTagManagement.border.title=Tags", - "GlobalSettingsPanel.pnCorrelationProperties.border.title=Correlation Properties", - "GlobalSettingsPanel.manageTagsTextArea.text=Configure which tag names are associated with notable items. " - + "When these tags are used, the file or result will be recorded in the central repository. " - + "If that file or result is seen again in future cases, it will be flagged.", - "GlobalSettingsPanel.organizationPanel.border.title=Organizations", - }) - private void customizeComponents() { - setName(Bundle.GlobalSettingsPanel_title()); + setName(NbBundle.getMessage(GlobalSettingsPanel.class, "GlobalSettingsPanel.pnCorrelationProperties.border.title")); } private void addIngestJobEventsListener() { @@ -137,7 +128,6 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i setPreferredSize(new java.awt.Dimension(1022, 488)); jScrollPane1.setBorder(null); - jScrollPane1.setMinimumSize(new java.awt.Dimension(21, 22)); jScrollPane1.setPreferredSize(new java.awt.Dimension(1022, 407)); jPanel1.setMinimumSize(new java.awt.Dimension(0, 0)); @@ -320,7 +310,7 @@ public final class GlobalSettingsPanel extends IngestModuleGlobalSettingsPanel i .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(pnDatabaseConfiguration, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 1010, Short.MAX_VALUE) + .addComponent(pnCorrelationProperties, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 1012, Short.MAX_VALUE) .addComponent(organizationPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()) ); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form index 829c646f76..a9ead822e7 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.form @@ -98,7 +98,7 @@ - + @@ -193,11 +193,6 @@ - - - - -
@@ -241,7 +236,7 @@ - + @@ -454,17 +449,12 @@ - + - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java index be53fd752f..de9a8b8e69 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/AutopsyOptionsPanel.java @@ -51,12 +51,8 @@ import org.sleuthkit.autopsy.report.ReportBranding; * Options panel that allow users to set application preferences. */ @Messages({ - "AutopsyOptionsPanel.agencyLogoPreview.text=
No logo
selected
", - "AutopsyOptionsPanel.logoPanel.border.title=Logo", - "AutopsyOptionsPanel.viewPanel.border.title=View", "AutopsyOptionsPanel.invalidImageFile.msg=The selected file was not able to be used as an agency logo.", "AutopsyOptionsPanel.invalidImageFile.title=Invalid Image File", - "AutopsyOptionsPanel.runtimePanel.border.title=Runtime", "AutopsyOptionsPanel.memFieldValidationLabel.not64BitInstall.text=JVM memory settings only enabled for 64 bit version", "AutopsyOptionsPanel.memFieldValidationLabel.noValueEntered.text=No value entered", "AutopsyOptionsPanel.memFieldValidationLabel.invalidCharacters.text=Invalid characters, value must be a positive integer", @@ -334,7 +330,7 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { private void updateAgencyLogo(String path) throws IOException { agencyLogoPathField.setText(path); ImageIcon agencyLogoIcon = new ImageIcon(); - agencyLogoPreview.setText(Bundle.AutopsyOptionsPanel_agencyLogoPreview_text()); + agencyLogoPreview.setText(NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoPreview.text")); if (!agencyLogoPathField.getText().isEmpty()) { File file = new File(agencyLogoPathField.getText()); if (file.exists()) { @@ -647,8 +643,6 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addGap(0, 0, Short.MAX_VALUE)) ); - agencyLogoPreview.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.agencyLogoPreview.AccessibleContext.accessibleName")); // NOI18N - viewPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.viewPanel.border.title"))); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectFile, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelSelectFile.text")); // NOI18N @@ -912,8 +906,6 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel { .addContainerGap()) ); - runtimePanel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.runtimePanel.AccessibleContext.accessibleName")); // NOI18N - jScrollPane1.setViewportView(jPanel1); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties index ddb22f487b..535c88ce61 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/Bundle.properties @@ -169,7 +169,6 @@ CriterionChooser.removeButton.text=Remove CriterionChooser.descendingRadio.text=\u25bc Descending AutopsyOptionsPanel.agencyLogoPathFieldValidationLabel.text= AutopsyOptionsPanel.logNumAlert.text= -AutopsyOptionsPanel.runtimePanel.AccessibleContext.accessibleName= AutopsyOptionsPanel.totalMemoryLabel.text=Total System Memory: AutopsyOptionsPanel.maxMemoryLabel.text=Maximum JVM Memory: AutopsyOptionsPanel.maxLogFileCount.text=Maximum Log Files: @@ -178,4 +177,7 @@ AutopsyOptionsPanel.restartNecessaryWarning.text=A restart is necessary for any AutopsyOptionsPanel.browseLogosButton.text=Browse AutopsyOptionsPanel.defaultLogoRB.text=Use default AutopsyOptionsPanel.specifyLogoRB.text=Specify a logo -AutopsyOptionsPanel.agencyLogoPreview.AccessibleContext.accessibleName= +AutopsyOptionsPanel.agencyLogoPreview.text=
No logo
selected
+AutopsyOptionsPanel.logoPanel.border.title=Logo +AutopsyOptionsPanel.runtimePanel.border.title=Runtime +AutopsyOptionsPanel.viewPanel.border.title=View From 1d92a70664375c39ae471d782ec75886a4bb3574 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 12 Mar 2018 13:59:05 -0400 Subject: [PATCH 112/128] Fix compile error for NoCurrentCaseException in VideoFile.java --- .../imagegallery/datamodel/VideoFile.java | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java index 8959113abc..ea783160e4 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java @@ -29,7 +29,6 @@ import javafx.scene.image.Image; import javafx.scene.media.Media; import javafx.scene.media.MediaException; import org.netbeans.api.progress.ProgressHandle; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.ImageUtils; @@ -47,9 +46,9 @@ public class VideoFile extends DrawableFile { /** * Instantiate a VideoFile object. - * + * * @param file The file on which to base the object. - * @param analyzed + * @param analyzed */ VideoFile(AbstractFile file, Boolean analyzed) { super(file, analyzed); @@ -57,15 +56,13 @@ public class VideoFile extends DrawableFile { /** * Get the genereric video thumbnail. - * + * * @return The thumbnail. */ public static Image getGenericVideoThumbnail() { return videoIcon; } - - @Override String getMessageTemplate(final Exception exception) { return "Failed to get image preview for video {0}: " + exception.toString(); //NON-NLS @@ -80,11 +77,11 @@ public class VideoFile extends DrawableFile { /** * Get the media associated with the VideoFile. - * + * * @return The media. - * + * * @throws IOException - * @throws MediaException + * @throws MediaException */ @NbBundle.Messages({"VideoFile.getMedia.progress=writing temporary file to disk"}) public Media getMedia() throws IOException, MediaException, NoCurrentCaseException { @@ -106,24 +103,23 @@ public class VideoFile extends DrawableFile { media = new Media(Paths.get(cacheFile.getAbsolutePath()).toUri().toString()); mediaRef = new SoftReference<>(media); return media; - } @Override Double getWidth() { - double retValue = -1.0; - + double width = -1.0; try { - retValue = (double) getMedia().getWidth(); + width = getMedia().getWidth(); } catch (ReadContentInputStreamException ex) { - logger.log(Level.WARNING, "Error reading video file.", ex); //NON-NLS + logger.log(Level.WARNING, "Error reading video file", ex); //NON-NLS } catch (IOException ex) { - logger.log(Level.SEVERE, "Error writing video file to disk.", ex); //NON-NLS + logger.log(Level.SEVERE, "Error writing video file to disk", ex); //NON-NLS } catch (MediaException ex) { - logger.log(Level.SEVERE, "Error creating media from source file.", ex); //NON-NLS + logger.log(Level.SEVERE, "Error creating media from source file", ex); //NON-NLS } catch (NoCurrentCaseException ex) { - - return retValue; + logger.log(Level.SEVERE, "The current case has been closed", ex); //NON-NLS + } + return width; } @Override @@ -133,10 +129,9 @@ public class VideoFile extends DrawableFile { @Override Double getHeight() { - double retValue = -1.0; - + double height = -1.0; try { - retValue = (double) getMedia().getHeight(); + height = getMedia().getHeight(); } catch (ReadContentInputStreamException ex) { logger.log(Level.WARNING, "Error reading video file.", ex); //NON-NLS } catch (IOException ex) { @@ -144,8 +139,8 @@ public class VideoFile extends DrawableFile { } catch (MediaException ex) { logger.log(Level.SEVERE, "Error creating media from source file.", ex); //NON-NLS } catch (NoCurrentCaseException ex) { - logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS - - return retValue; + logger.log(Level.SEVERE, "The current case has been closed", ex); //NON-NLS + } + return height; } } From fb551f01bcfd5f09c460e8fcebebdfec079b7e44 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 12 Mar 2018 14:00:57 -0400 Subject: [PATCH 113/128] Fix compile error for NoCurrentCaseException in VideoFile.java --- .../org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java | 1 + 1 file changed, 1 insertion(+) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java index ea783160e4..81fc907069 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/datamodel/VideoFile.java @@ -82,6 +82,7 @@ public class VideoFile extends DrawableFile { * * @throws IOException * @throws MediaException + * @throws NoCurrentCaseException */ @NbBundle.Messages({"VideoFile.getMedia.progress=writing temporary file to disk"}) public Media getMedia() throws IOException, MediaException, NoCurrentCaseException { From b0d35cf3130a6caf379d01115bbacd104bab74bf Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 12 Mar 2018 14:10:01 -0400 Subject: [PATCH 114/128] Fix compile error, improve error handling in SQLiteViewer.java --- .../autopsy/contentviewers/SQLiteViewer.java | 266 +++++++----------- 1 file changed, 108 insertions(+), 158 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index 4e8fb1fe80..aa0967b16c 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -40,6 +40,7 @@ import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JComboBox; import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.openide.util.NbBundle; import org.openide.windows.WindowManager; @@ -53,32 +54,27 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.autopsy.corecomponentinterfaces.FileTypeViewer; +import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; /** - * A file content viewer for SQLITE db files. - * + * A file content viewer for SQLite database files. */ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { + private static final long serialVersionUID = 1L; public static final String[] SUPPORTED_MIMETYPES = new String[]{"application/x-sqlite3"}; - private static final Logger LOGGER = Logger.getLogger(FileViewer.class.getName()); - private Connection connection = null; - - private String tmpDBPathName = null; - private File tmpDBFile = null; - - private final Map dbTablesMap = new TreeMap<>(); - private static final int ROWS_PER_PAGE = 100; + private static final Logger logger = Logger.getLogger(FileViewer.class.getName()); + private final SQLiteTableView selectedTableView = new SQLiteTableView(); + private AbstractFile sqliteDbFile; + private File tmpDbFile; + private Connection connection; private int numRows; // num of rows in the selected table private int currPage = 0; // curr page of rows being displayed - - SQLiteTableView selectedTableView = new SQLiteTableView(); private SwingWorker worker; - /** - * Creates new form SQLiteViewer + * Constructs a file content viewer for SQLite database files. */ public SQLiteViewer() { initComponents(); @@ -217,7 +213,6 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { }// //GEN-END:initComponents private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed - currPage++; if (currPage * ROWS_PER_PAGE > numRows) { nextPageButton.setEnabled(false); @@ -231,7 +226,6 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { }//GEN-LAST:event_nextPageButtonActionPerformed private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed - currPage--; if (currPage == 1) { prevPageButton.setEnabled(false); @@ -250,7 +244,6 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { if (null == tableName) { return; } - selectTable(tableName); }//GEN-LAST:event_tablesDropdownListActionPerformed @@ -276,7 +269,8 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { @Override public void setFile(AbstractFile file) { - processSQLiteFile(file); + sqliteDbFile = file; + processSQLiteFile(); } @Override @@ -286,9 +280,6 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { @Override public void resetComponent() { - - dbTablesMap.clear(); - tablesDropdownList.setEnabled(true); tablesDropdownList.removeAllItems(); numEntriesField.setText(""); @@ -299,75 +290,59 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { connection.close(); connection = null; } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS + logger.log(Level.SEVERE, "Failed to close DB connection to file.", ex); //NON-NLS } } // delete last temp file - if (null != tmpDBFile) { - tmpDBFile.delete(); - tmpDBFile = null; + if (null != tmpDbFile) { + tmpDbFile.delete(); + tmpDbFile = null; } } /** - * Process the given SQLite DB file - * - * @param sqliteFile - - * - * @return none + * Process the given SQLite DB file. */ - @NbBundle.Messages({"# {0} - fileName", - "SQLiteViewer.processSQLiteFile.errorMessage=Error opening SQLite file {0}" - }) - private void processSQLiteFile(AbstractFile sqliteFile) { - - tablesDropdownList.removeAllItems(); - - new SwingWorker() { + @NbBundle.Messages({ + "SQLiteViewer.comboBox.noTableEntry=No tables found", + "SQLiteViewer.errorMessage.interrupted=The processing of the file was interrupted.", + "SQLiteViewer.errorMessage.noCurrentCase=The case has been closed.", + "SQLiteViewer.errorMessage.failedToExtractFile=The file could not be extracted from the data source.", + "SQLiteViewer.errorMessage.failedToQueryDatabase=The database tables in the file could not be read.", + "SQLiteViewer.errorMessage.failedToinitJDBCDriver=The JDBC driver for SQLite could not be loaded.", + "# {0} - exception message", "SQLiteViewer.errorMessage.unexpectedError=An unexpected error occurred:\n{0).",}) + private void processSQLiteFile() { + SwingUtilities.invokeLater(() -> { + tablesDropdownList.removeAllItems(); + }); + new SwingWorker, Void>() { @Override - protected Void doInBackground() throws IOException, SQLException, ClassNotFoundException { + protected Map doInBackground() throws NoCurrentCaseException, TskCoreException, IOException, SQLException, ClassNotFoundException { + // Copy the file to temp folder + String tmpDBPathName = Case.getOpenCase().getTempDirectory() + File.separator + sqliteDbFile.getName(); + tmpDbFile = new File(tmpDBPathName); + ContentUtils.writeToFile(sqliteDbFile, tmpDbFile); - try { - // Copy the file to temp folder - tmpDBPathName = Case.getOpenCase().getTempDirectory() + File.separator + sqliteFile.getName(); - tmpDBFile = new File(tmpDBPathName); - ContentUtils.writeToFile(sqliteFile, tmpDBFile); + // Look for any meta files associated with this DB - WAL, SHM, etc. + findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-wal"); + findAndCopySQLiteMetaFile(sqliteDbFile, sqliteDbFile.getName() + "-shm"); - // look for any meta files associated with this DB - WAL, SHM - findAndCopySQLiteMetaFile(sqliteFile, sqliteFile.getName() + "-wal"); - findAndCopySQLiteMetaFile(sqliteFile, sqliteFile.getName() + "-shm"); - - // Open copy using JDBC - Class.forName("org.sqlite.JDBC"); //NON-NLS //load JDBC driver - connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS - - // Read all table names and schema - return getTables(); - } catch (NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS - } catch (IOException ex) { - LOGGER.log(Level.SEVERE, "Failed to copy DB file " + sqliteFile.getName(), ex); //NON-NLS - throw ex; - } catch (SQLException ex) { - LOGGER.log(Level.SEVERE, "Failed to get tables from DB file " + sqliteFile.getName(), ex); //NON-NLS - throw ex; - } catch (ClassNotFoundException ex) { - LOGGER.log(Level.SEVERE, "Failed to initialize JDBC SQLite.", ex); //NON-NLS - throw ex; - } - return null; + // Load the SQLite JDBC driver, if necessary. + Class.forName("org.sqlite.JDBC"); //NON-NLS + connection = DriverManager.getConnection("jdbc:sqlite:" + tmpDBPathName); //NON-NLS + + // Query the file for the table names and schemas. + return getTables(); } @Override protected void done() { super.done(); try { - - get(); + Map dbTablesMap = get(); if (dbTablesMap.isEmpty()) { - // Populate error message - tablesDropdownList.addItem("No tables found"); + tablesDropdownList.addItem(Bundle.SQLiteViewer_comboBox_noTableEntry()); tablesDropdownList.setEnabled(false); } else { dbTablesMap.keySet().forEach((tableName) -> { @@ -375,97 +350,76 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { }); } } catch (InterruptedException ex) { - - LOGGER.log(Level.SEVERE, "Interrupted while opening DB file " + sqliteFile.getName(), ex); //NON-NLS - JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), - ex.getMessage(), - Bundle.SQLiteViewer_processSQLiteFile_errorMessage(sqliteFile.getName()), - JOptionPane.ERROR_MESSAGE); - } - catch (ExecutionException ex) { - LOGGER.log(Level.SEVERE, "Unexpected exception while opening DB file " + sqliteFile.getName(), ex); //NON-NLS - JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), - ex.getCause().getMessage(), - Bundle.SQLiteViewer_processSQLiteFile_errorMessage(sqliteFile.getName()), - JOptionPane.ERROR_MESSAGE); + logger.log(Level.SEVERE, String.format("Interrupted while opening SQLite database file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS + MessageNotifyUtil.Message.error(Bundle.SQLiteViewer_errorMessage_interrupted()); + } catch (ExecutionException ex) { + String errorMessage; + Throwable cause = ex.getCause(); + if (cause instanceof NoCurrentCaseException) { + logger.log(Level.SEVERE, "Current case has been closed", ex); //NON-NLS + errorMessage = Bundle.SQLiteViewer_errorMessage_noCurrentCase(); + } else if (cause instanceof TskCoreException || cause instanceof IOException) { + logger.log(Level.SEVERE, String.format("Failed to create temp copy of DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS + errorMessage = Bundle.SQLiteViewer_errorMessage_failedToExtractFile(); + } else if (cause instanceof SQLException) { + logger.log(Level.SEVERE, String.format("Failed to get tables from DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS + errorMessage = Bundle.SQLiteViewer_errorMessage_failedToQueryDatabase(); + } else if (cause instanceof ClassNotFoundException) { + logger.log(Level.SEVERE, String.format("Failed to initialize JDBC SQLite '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS + errorMessage = Bundle.SQLiteViewer_errorMessage_failedToinitJDBCDriver(); + } else { + logger.log(Level.SEVERE, String.format("Unexpected exception while processing DB file '%s' (objId=%d)", sqliteDbFile.getName(), sqliteDbFile.getId()), ex); //NON-NLS + errorMessage = Bundle.SQLiteViewer_errorMessage_unexpectedError(cause.getLocalizedMessage()); + } + MessageNotifyUtil.Message.error(errorMessage); } } }.execute(); - } /** - * Searches for a meta file associated with the give SQLite db - * If found, copies the file to the temp folder + * Searches for a meta file associated with the give SQLite db If found, + * copies the file to the temp folder * - * @param sqliteFile - SQLIte db file being processed + * @param sqliteFile - SQLIte db file being processed * @param metaFileName name of meta file to look for - * - * @return true if the meta file is found and copied successfully, false otherwise */ - private boolean findAndCopySQLiteMetaFile(AbstractFile sqliteFile, String metaFileName ) { - Case openCase; - try { - openCase = Case.getOpenCase(); - } catch (NoCurrentCaseException ex) { - LOGGER.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS - return false; - } + private void findAndCopySQLiteMetaFile(AbstractFile sqliteFile, String metaFileName) throws NoCurrentCaseException, TskCoreException, IOException { + Case openCase = Case.getOpenCase(); SleuthkitCase sleuthkitCase = openCase.getSleuthkitCase(); Services services = new Services(sleuthkitCase); FileManager fileManager = services.getFileManager(); - - List metaFiles = null; - try { - metaFiles = fileManager.findFiles(sqliteFile.getDataSource(), metaFileName, sqliteFile.getParent().getName() ); - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Unexpected exception while searching SQLite meta file = " + metaFileName , ex); //NON-NLS - return false; - } - + List metaFiles = fileManager.findFiles(sqliteFile.getDataSource(), metaFileName, sqliteFile.getParent().getName()); if (metaFiles != null) { - for (AbstractFile metaFile: metaFiles) { + for (AbstractFile metaFile : metaFiles) { String tmpMetafilePathName = openCase.getTempDirectory() + File.separator + metaFile.getName(); - File tmpMetafile = new File(tmpMetafilePathName); - try { - ContentUtils.writeToFile(metaFile, tmpMetafile); - } catch (IOException ex) { - LOGGER.log(Level.SEVERE, "Unexpected exception while copying SQLite meta file = " + metaFileName , ex); //NON-NLS - return false; - } + ContentUtils.writeToFile(metaFile, tmpMetafile); } } - - return true; } - /** - * Gets the table names and their schema from loaded SQLite db file - * - * @return true if success, false otherwise - */ - private void getTables() throws SQLException { + /** + * Gets the table names and schemas from the SQLite database file. + * + * @return A mapping of table names to SQL CREATE TABLE statements. + */ + private Map getTables() throws SQLException { + Map dbTablesMap = new TreeMap<>(); Statement statement = null; ResultSet resultSet = null; - try { - statement = connection.createStatement(); - resultSet = statement.executeQuery( - "SELECT name, sql FROM sqlite_master " - + " WHERE type= 'table' " - + " ORDER BY name;"); //NON-NLS - + statement = connection.createStatement(); + resultSet = statement.executeQuery( + "SELECT name, sql FROM sqlite_master " + + " WHERE type= 'table' " + + " ORDER BY name;"); //NON-NLS while (resultSet.next()) { String tableName = resultSet.getString("name"); //NON-NLS String tableSQL = resultSet.getString("sql"); //NON-NLS - dbTablesMap.put(tableName, tableSQL); } - } catch(SQLException ex) { - throw ex; - } - finally { + } finally { if (null != resultSet) { resultSet.close(); } @@ -473,6 +427,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { statement.close(); } } + return dbTablesMap; } @NbBundle.Messages({"# {0} - tableName", @@ -498,8 +453,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { return resultSet.getInt("count"); } catch (SQLException ex) { throw ex; - } - finally { + } finally { if (null != resultSet) { resultSet.close(); } @@ -523,7 +477,6 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { prevPageButton.setEnabled(false); - if (numRows > 0) { nextPageButton.setEnabled(((numRows > ROWS_PER_PAGE))); readTable(tableName, (currPage - 1) * ROWS_PER_PAGE + 1, ROWS_PER_PAGE); @@ -532,19 +485,18 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { selectedTableView.setupTable(Collections.emptyList()); } - } catch (InterruptedException ex ) { - LOGGER.log(Level.SEVERE, "Interrupted while getting row count from table " + tableName, ex); //NON-NLS + } catch (InterruptedException ex) { + logger.log(Level.SEVERE, "Interrupted while getting row count from table " + tableName, ex); //NON-NLS JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), ex.getMessage(), Bundle.SQLiteViewer_selectTable_errorText(tableName), JOptionPane.ERROR_MESSAGE); - } - catch (ExecutionException ex) { - LOGGER.log(Level.SEVERE, "Unexpected exception while getting row count from table " + tableName, ex); //NON-NLS - JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), - ex.getCause().getMessage(), - Bundle.SQLiteViewer_selectTable_errorText(tableName), - JOptionPane.ERROR_MESSAGE); + } catch (ExecutionException ex) { + logger.log(Level.SEVERE, "Unexpected exception while getting row count from table " + tableName, ex); //NON-NLS + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + ex.getCause().getMessage(), + Bundle.SQLiteViewer_selectTable_errorText(tableName), + JOptionPane.ERROR_MESSAGE); } } }; @@ -563,7 +515,7 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { worker = new SwingWorker>, Void>() { @Override protected ArrayList> doInBackground() throws Exception { - + Statement statement = null; ResultSet resultSet = null; try { @@ -573,12 +525,11 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { + " LIMIT " + Integer.toString(numRowsToRead) + " OFFSET " + Integer.toString(startRow - 1) ); //NON-NLS - + return resultSetToArrayList(resultSet); } catch (SQLException ex) { throw ex; - } - finally { + } finally { if (null != resultSet) { resultSet.close(); } @@ -600,19 +551,18 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { ArrayList> rows = get(); if (Objects.nonNull(rows)) { selectedTableView.setupTable(rows); - }else{ + } else { selectedTableView.setupTable(Collections.emptyList()); } } catch (InterruptedException ex) { - LOGGER.log(Level.SEVERE, "Interrupted while reading table " + tableName, ex); //NON-NLS - JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + logger.log(Level.SEVERE, "Interrupted while reading table " + tableName, ex); //NON-NLS + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), ex.getMessage(), Bundle.SQLiteViewer_readTable_errorText(tableName), JOptionPane.ERROR_MESSAGE); - } - catch (ExecutionException ex) { - LOGGER.log(Level.SEVERE, "Unexpected exception while reading table " + tableName, ex); //NON-NLS - JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), + } catch (ExecutionException ex) { + logger.log(Level.SEVERE, "Unexpected exception while reading table " + tableName, ex); //NON-NLS + JOptionPane.showMessageDialog(WindowManager.getDefault().getMainWindow(), ex.getCause().getMessage(), Bundle.SQLiteViewer_readTable_errorText(tableName), JOptionPane.ERROR_MESSAGE); From 8ffc128607e96763c377618653439a70f1b5ac15 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 12 Mar 2018 14:13:57 -0400 Subject: [PATCH 115/128] Fix compile error, improve error handling in SQLiteViewer.java --- Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java index aa0967b16c..b9e71ccdf8 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/SQLiteViewer.java @@ -299,6 +299,8 @@ public class SQLiteViewer extends javax.swing.JPanel implements FileTypeViewer { tmpDbFile.delete(); tmpDbFile = null; } + + sqliteDbFile = null; } /** From d99dc9c48970f444eb3845b57d9ed259e9042f20 Mon Sep 17 00:00:00 2001 From: Richard Cordovano Date: Mon, 12 Mar 2018 16:03:19 -0400 Subject: [PATCH 116/128] Disbaled publicMethodCommentRequirement rule --- ruleset.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ruleset.xml b/ruleset.xml index 07a44a73c9..ba1bfa6374 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -57,6 +57,8 @@ + +