Autopsy  4.14.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
CommunicationsGraph.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2018 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.communications;
20 
21 import com.github.mustachejava.DefaultMustacheFactory;
22 import com.github.mustachejava.Mustache;
23 import com.google.common.collect.Multimap;
24 import com.google.common.collect.MultimapBuilder;
25 import com.mxgraph.model.mxCell;
26 import com.mxgraph.model.mxICell;
27 import com.mxgraph.util.mxConstants;
28 import com.mxgraph.view.mxGraph;
29 import com.mxgraph.view.mxStylesheet;
30 import java.io.InputStream;
31 import java.io.InputStreamReader;
32 import java.io.StringWriter;
33 import java.net.URL;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 import java.util.concurrent.CancellationException;
39 import java.util.concurrent.ExecutionException;
40 import java.util.logging.Level;
41 import javax.swing.SwingWorker;
44 import org.sleuthkit.datamodel.AccountDeviceInstance;
45 import org.sleuthkit.datamodel.AccountPair;
46 import org.sleuthkit.datamodel.CommunicationsFilter;
47 import org.sleuthkit.datamodel.CommunicationsManager;
48 import org.sleuthkit.datamodel.Content;
49 import org.sleuthkit.datamodel.TskCoreException;
50 
55 final class CommunicationsGraph extends mxGraph {
56 
57  private static final Logger logger = Logger.getLogger(CommunicationsGraph.class.getName());
58  private static final URL MARKER_PIN_URL = CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png");
59  private static final URL LOCK_URL = CommunicationsGraph.class.getResource("/org/sleuthkit/autopsy/communications/images/lock_large_locked.png");
60 
61  /* mustache.java template */
62  private final static Mustache labelMustache;
63 
64  static {
65  final InputStream templateStream = CommunicationsGraph.class.getResourceAsStream("/org/sleuthkit/autopsy/communications/Vertex_Label_template.html");
66  labelMustache = new DefaultMustacheFactory().compile(new InputStreamReader(templateStream), "Vertex_Label");
67  }
68 
69  /* Style sheet for default vertex and edge styles. These are initialized in
70  * the static block below. */
71  static final private mxStylesheet mxStylesheet = new mxStylesheet();
72 
73  static {
74  //initialize defaul vertex properties
75  mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE);
76  mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE);
77  mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "000000");
78 
79  //initialize defaul edge properties
80  mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true);
81  mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_PERIMETER_SPACING, 0);
82  mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ENDARROW, mxConstants.NONE);
83  mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE);
84  }
85 
87  private final Map<String, mxCell> nodeMap = new HashMap<>();
88 
90  private final Multimap<Content, mxCell> edgeMap = MultimapBuilder.hashKeys().hashSetValues().build();
91  private final LockedVertexModel lockedVertexModel;
92 
93  private final PinnedAccountModel pinnedAccountModel;
94 
95  CommunicationsGraph(PinnedAccountModel pinnedAccountModel, LockedVertexModel lockedVertexModel) {
96  super(mxStylesheet);
97  this.pinnedAccountModel = pinnedAccountModel;
98  this.lockedVertexModel = lockedVertexModel;
99  //set fixed properties of graph.
100  setAutoSizeCells(true);
101  setCellsCloneable(false);
102  setDropEnabled(false);
103  setCellsCloneable(false);
104  setCellsEditable(false);
105  setCellsResizable(false);
106  setCellsMovable(true);
107  setCellsDisconnectable(false);
108  setConnectableEdges(false);
109  setDisconnectOnMove(false);
110  setEdgeLabelsMovable(false);
111  setVertexLabelsMovable(false);
112  setAllowDanglingEdges(false);
113  setCellsBendable(true);
114  setKeepEdgesInBackground(true);
115  setResetEdgesOnMove(true);
116  setHtmlLabels(true);
117  }
118 
124  LockedVertexModel getLockedVertexModel() {
125  return lockedVertexModel;
126  }
127 
128  PinnedAccountModel getPinnedAccountModel() {
129  return pinnedAccountModel;
130  }
131 
132  void clear() {
133  nodeMap.clear();
134  edgeMap.clear();
135  removeCells(getChildVertices(getDefaultParent()));
136  }
137 
138  @Override
139  public String convertValueToString(Object cell) {
140  final StringWriter stringWriter = new StringWriter();
141  HashMap<String, Object> scopes = new HashMap<>();
142 
143  Object value = getModel().getValue(cell);
144  if (value instanceof AccountDeviceInstanceKey) {
145  final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value;
146 
147  scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID());
148  scopes.put("size", Math.round(Math.log(adiKey.getMessageCount()) + 5));
149  scopes.put("iconFileName", CommunicationsGraph.class.getResource(Utils.getIconFilePath(adiKey.getAccountDeviceInstance().getAccount().getAccountType())));
150  scopes.put("pinned", pinnedAccountModel.isAccountPinned(adiKey));
151  scopes.put("MARKER_PIN_URL", MARKER_PIN_URL);
152  scopes.put("locked", lockedVertexModel.isVertexLocked((mxCell) cell));
153  scopes.put("LOCK_URL", LOCK_URL);
154 
155  labelMustache.execute(stringWriter, scopes);
156 
157  return stringWriter.toString();
158  } else {
159  return "";
160  }
161  }
162 
163  @Override
164  public String getToolTipForCell(Object cell) {
165  final StringWriter stringWriter = new StringWriter();
166  HashMap<String, Object> scopes = new HashMap<>();
167 
168  Object value = getModel().getValue(cell);
169  if (value instanceof AccountDeviceInstanceKey) {
170  final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value;
171 
172  scopes.put("accountName", adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID());
173  scopes.put("relationships", 12);// Math.round(Math.log(adiKey.getMessageCount()) + 5));
174  scopes.put("iconFileName", CommunicationsGraph.class.getResource(Utils.getIconFilePath(adiKey.getAccountDeviceInstance().getAccount().getAccountType())));
175  scopes.put("pinned", pinnedAccountModel.isAccountPinned(adiKey));
176  scopes.put("MARKER_PIN_URL", MARKER_PIN_URL);
177  scopes.put("locked", lockedVertexModel.isVertexLocked((mxCell) cell));
178  scopes.put("LOCK_URL", LOCK_URL);
179  scopes.put("device_id", adiKey.getAccountDeviceInstance().getDeviceId());
180 
181  labelMustache.execute(stringWriter, scopes);
182 
183  return stringWriter.toString();
184  } else {
185  final mxICell edge = (mxICell) cell;
186  final long count = (long) edge.getValue();
187  return "<html>" + edge.getId() + "<br>" + count + (count == 1 ? " relationship" : " relationships") + "</html>";
188  }
189  }
190 
191  SwingWorker<?, ?> rebuild(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) {
192  return new RebuildWorker(progress, commsManager, currentFilter);
193  }
194 
195  void resetGraph() {
196  clear();
197  getView().setScale(1);
198  pinnedAccountModel.clear();
199  lockedVertexModel.clear();
200  }
201 
202  private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) {
203  final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance();
204  final String name = accountDeviceInstance.getAccount().getTypeSpecificID();
205 
206  final mxCell vertex = nodeMap.computeIfAbsent(name + accountDeviceInstance.getDeviceId(), vertexName -> {
207  double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10;
208 
209  mxCell newVertex = (mxCell) insertVertex(
210  getDefaultParent(),
211  name, accountDeviceInstanceKey,
212  Math.random() * 400,
213  Math.random() * 400,
214  size,
215  size);
216  return newVertex;
217  });
218  return vertex;
219  }
220 
221  @SuppressWarnings("unchecked")
222  private mxCell addOrUpdateEdge(long relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) {
223  mxCell vertex1 = getOrCreateVertex(account1);
224  mxCell vertex2 = getOrCreateVertex(account2);
225  Object[] edgesBetween = getEdgesBetween(vertex1, vertex2);
226  mxCell edge;
227  if (edgesBetween.length == 0) {
228  final String edgeName = vertex1.getId() + " - " + vertex2.getId();
229  edge = (mxCell) insertEdge(getDefaultParent(), edgeName, relSources, vertex1, vertex2,
230  "strokeWidth=" + (Math.log(relSources) + 1));
231  } else {
232  edge = (mxCell) edgesBetween[0];
233  edge.setStyle("strokeWidth=" + (Math.log(relSources) + 1));
234  }
235  return edge;
236  }
237 
242  private class RebuildWorker extends SwingWorker<Void, Void> {
243 
245  private final CommunicationsManager commsManager;
246  private final CommunicationsFilter currentFilter;
247 
248  RebuildWorker(ProgressIndicator progress, CommunicationsManager commsManager, CommunicationsFilter currentFilter) {
249  this.progressIndicator = progress;
250  this.currentFilter = currentFilter;
251  this.commsManager = commsManager;
252 
253  }
254 
255  @Override
256  protected Void doInBackground() {
257  progressIndicator.start("Loading accounts");
258  int progressCounter = 0;
259  try {
263  final Map<AccountDeviceInstance, AccountDeviceInstanceKey> relatedAccounts = new HashMap<>();
264  for (final AccountDeviceInstanceKey adiKey : pinnedAccountModel.getPinnedAccounts()) {
265  if (isCancelled()) {
266  break;
267  }
268  //get accounts related to pinned account
269  final List<AccountDeviceInstance> relatedAccountDeviceInstances
270  = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter);
271  relatedAccounts.put(adiKey.getAccountDeviceInstance(), adiKey);
272  getOrCreateVertex(adiKey);
273 
274  for (final AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) {
275  final long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter);
276  final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount);
277  relatedAccounts.put(relatedADI, relatedADIKey); //store related accounts
278  }
279  progressIndicator.progress(++progressCounter);
280  }
281 
282  Set<AccountDeviceInstance> accounts = relatedAccounts.keySet();
283 
284  Map<AccountPair, Long> relationshipCounts = commsManager.getRelationshipCountsPairwise(accounts, currentFilter);
285 
286  int total = relationshipCounts.size();
287  int progress = 0;
288  String progressText = "";
289  progressIndicator.switchToDeterminate("", 0, total);
290  for (Map.Entry<AccountPair, Long> entry : relationshipCounts.entrySet()) {
291  Long count = entry.getValue();
292  AccountPair relationshipKey = entry.getKey();
293  AccountDeviceInstanceKey account1 = relatedAccounts.get(relationshipKey.getFirst());
294  AccountDeviceInstanceKey account2 = relatedAccounts.get(relationshipKey.getSecond());
295 
296  if (pinnedAccountModel.isAccountPinned(account1)
297  || pinnedAccountModel.isAccountPinned(account2)) {
298  mxCell addEdge = addOrUpdateEdge(count, account1, account2);
299  progressText = addEdge.getId();
300  }
301  progressIndicator.progress(progressText, progress++);
302  }
303  } catch (TskCoreException tskCoreException) {
304  logger.log(Level.SEVERE, "Error", tskCoreException);
305  }
306 
307  return null;
308  }
309 
310  @Override
311  protected void done() {
312  super.done();
313  try {
314  get();
315  } catch (InterruptedException | ExecutionException ex) {
316  logger.log(Level.SEVERE, "Error building graph visualization. ", ex);
317  } catch (CancellationException ex) {
318  logger.log(Level.INFO, "Graph visualization cancelled");
319  } finally {
320  progressIndicator.finish();
321  }
322  }
323  }
324 }
void start(String message, int totalWorkUnits)
void switchToDeterminate(String message, int workUnitsCompleted, int totalWorkUnits)

Copyright © 2012-2020 Basis Technology. Generated on: Wed Apr 8 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.