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

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.