Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
CallLogArtifactViewer.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2020-2021 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.contentviewers.artifactviewers;
20
21import java.awt.Component;
22import java.awt.GridBagConstraints;
23import java.awt.GridBagLayout;
24import java.awt.Insets;
25import java.util.ArrayList;
26import java.util.Arrays;
27import java.util.HashMap;
28import java.util.HashSet;
29import java.util.List;
30import java.util.Map;
31import java.util.Optional;
32import java.util.Set;
33import java.util.logging.Level;
34import javax.swing.JScrollPane;
35import javax.swing.border.EmptyBorder;
36import org.apache.commons.lang3.ObjectUtils;
37import org.apache.commons.lang3.StringUtils;
38import org.openide.util.NbBundle;
39import org.openide.util.lookup.ServiceProvider;
40import org.sleuthkit.autopsy.casemodule.Case;
41import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
42import org.sleuthkit.autopsy.centralrepository.datamodel.CentralRepository;
43import org.sleuthkit.autopsy.contentviewers.layout.ContentViewerDefaults;
44import org.sleuthkit.autopsy.coreutils.Logger;
45import org.sleuthkit.autopsy.guiutils.ContactCache;
46import org.sleuthkit.datamodel.BlackboardArtifact;
47import org.sleuthkit.datamodel.BlackboardAttribute;
48import org.sleuthkit.datamodel.Content;
49import org.sleuthkit.datamodel.DataSource;
50import org.sleuthkit.datamodel.TskCoreException;
51
57@ServiceProvider(service = ArtifactContentViewer.class)
58public class CallLogArtifactViewer extends javax.swing.JPanel implements ArtifactContentViewer {
59
60 private final static Logger logger = Logger.getLogger(CallLogArtifactViewer.class.getName());
61 private static final long serialVersionUID = 1L;
62
63 private static final Set<Integer> HANDLED_ATTRIBUTE_TYPES = new HashSet<Integer>(Arrays.asList(
64 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(),
65 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO.getTypeID(),
66 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM.getTypeID(),
67 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID.getTypeID(),
68 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(),
69 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
70 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(),
71 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID()
72 ));
73
74 private GridBagLayout m_gridBagLayout = new GridBagLayout();
75 private GridBagConstraints m_constraints = new GridBagConstraints();
76
77 private PersonaAccountFetcher currentAccountFetcher = null;
78
84 this.setBorder(new EmptyBorder(ContentViewerDefaults.getPanelInsets()));
85 }
86
92 @SuppressWarnings("unchecked")
93 // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
94 private void initComponents() {
95
96 setLayout(new java.awt.GridBagLayout());
97 }// </editor-fold>//GEN-END:initComponents
98
99 @Override
100 public void setArtifact(BlackboardArtifact artifact) {
102
103 CallLogViewData callLogViewData = null;
104 try {
105 callLogViewData = getCallLogViewData(artifact);
106 } catch (NoCurrentCaseException | TskCoreException ex) {
107 logger.log(Level.SEVERE, String.format("Error getting attributes for Calllog artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
108 }
109 List<AccountPersonaSearcherData> personaSearchDataList = new ArrayList<>();
110 // update the view with the call log data
111 if (callLogViewData != null) {
112 personaSearchDataList.addAll(updateView(callLogViewData));
113
114 }
115 if (!personaSearchDataList.isEmpty()) {
116 currentAccountFetcher = new PersonaAccountFetcher(artifact, personaSearchDataList, this);
117 currentAccountFetcher.execute();
118 } else {
120 }
121
122 // repaint
123 this.revalidate();
124 this.repaint();
125 }
126
136 private CallLogViewData getCallLogViewData(BlackboardArtifact artifact) throws NoCurrentCaseException, TskCoreException {
137
138 if (artifact == null) {
139 return null;
140 }
141
142 BlackboardAttribute directionAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION));
143 BlackboardAttribute toAccountAttr = null;
144 BlackboardAttribute fromAccountAttr = null;
145 BlackboardAttribute localAccountAttr = null;
146
147 CallLogViewData callLogViewData = null;
148
149 String direction = null;
150 String fromAccountIdentifier = null;
151 String toAccountIdentifier = null;
152 List<String> otherParties = null;
153 List<String> toContactNames = null;
154 List<String> fromContactNames = null;
155
156 Content dataSource = artifact.getDataSource();
157 String deviceId = ((DataSource) dataSource).getDeviceId();
158
159 if (directionAttr != null) {
160 direction = directionAttr.getValueString();
161 if (direction.equalsIgnoreCase("Incoming")) {
162 fromAccountAttr = ObjectUtils.firstNonNull(
163 artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)),
164 artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
165 artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
166 );
167
168 toAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
169 localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
170 } else if (direction.equalsIgnoreCase("Outgoing")) {
171 toAccountAttr = ObjectUtils.firstNonNull(
172 artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)),
173 artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
174 artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
175 );
176
177 fromAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
178 localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
179 }
180 } else {
181 // if direction isn't known, check all the usual attributes that may have the number/address
182 // in the absence of sufficent data, any number available will be displayed as a From address.
183 if (fromAccountAttr == null) {
184 fromAccountAttr = ObjectUtils.firstNonNull(
185 artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)),
186 artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)),
187 artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
188 artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
189 );
190 }
191 }
192
193 // get the from account address
194 if (fromAccountAttr != null) {
195 String fromAccountAttrValue = fromAccountAttr.getValueString();
196 if (fromAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
197 fromAccountIdentifier = fromAccountAttrValue;
198 fromContactNames = ContactCache.getContactNameList(fromAccountIdentifier);
199 }
200 }
201
202 if (toAccountAttr != null) {
203 // TO may be a list of comma separated values.
204 String[] numbers = toAccountAttr.getValueString().split(",");
205 String toAccountAttrValue = StringUtils.trim(numbers[0]);
206 if (toAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
207 toAccountIdentifier = toAccountAttrValue;
208 toContactNames = ContactCache.getContactNameList(toAccountIdentifier);
209 }
210
211 // if more than one To address, then stick the rest of them in the
212 // "Other parties" list.
213 if (numbers.length > 1) {
214 otherParties = new ArrayList<>();
215 for (int i = 1; i < numbers.length; i++) {
216 otherParties.add(StringUtils.trim(numbers[i]));
217 }
218 }
219 }
220
221 // if we have at least one address attribute
222 if (null != fromAccountAttr || null != toAccountAttr) {
223 callLogViewData = new CallLogViewData(fromAccountIdentifier, toAccountIdentifier);
224 callLogViewData.setDirection(direction);
225
226 callLogViewData.setOtherParties(otherParties);
227
228 extractTimeAndDuration(artifact, callLogViewData);
229
230 callLogViewData.setDataSourceName(dataSource.getName());
231
232 // set local account, if it can be deduced.
233 if (localAccountAttr != null) {
234 String attrValue = localAccountAttr.getValueString();
235 // value must be a singular address and not a deviceId to be the local account id
236 if (attrValue.equalsIgnoreCase(deviceId) == false && attrValue.contains(",") == false) {
237 callLogViewData.setLocalAccountId(attrValue);
238 }
239 }
240
241 callLogViewData.setOtherAttributes(extractOtherAttributes(artifact));
242
243 callLogViewData.setFromContactNameList(fromContactNames);
244 callLogViewData.setToContactNameList(toContactNames);
245
246 String hostName = Optional.ofNullable(Case.getCurrentCaseThrows().getSleuthkitCase().getHostManager().getHostByDataSource((DataSource) dataSource))
247 .map(h -> h.getName())
248 .orElse(null);
249
250 callLogViewData.setHostName(hostName);
251 }
252
253 return callLogViewData;
254 }
255
266 private void extractTimeAndDuration(BlackboardArtifact artifact, CallLogViewData callLogViewData) throws TskCoreException {
267
268 BlackboardAttribute startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START));
269 if (startTimeAttr == null) {
270 startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME));
271 }
272 if (startTimeAttr != null) {
273 long startTime = startTimeAttr.getValueLong();
274 callLogViewData.setDateTimeStr(startTimeAttr.getDisplayString());
275
276 BlackboardAttribute endTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END));
277 if (endTimeAttr != null) {
278 long endTime = endTimeAttr.getValueLong();
279 if (endTime > 0 && (endTime - startTime) > 0) {
280 callLogViewData.setDuration(String.format("%d seconds", (endTime - startTime)));
281 }
282 }
283 }
284 }
285
296 private Map<String, String> extractOtherAttributes(BlackboardArtifact artifact) throws TskCoreException {
297 List<BlackboardAttribute> attributes = artifact.getAttributes();
298 Map<String, String> otherAttributes = new HashMap<>();
299
300 for (BlackboardAttribute attr : attributes) {
301 if (HANDLED_ATTRIBUTE_TYPES.contains(attr.getAttributeType().getTypeID()) == false) {
302 otherAttributes.put(attr.getAttributeType().getDisplayName(), attr.getDisplayString());
303 }
304 }
305
306 return otherAttributes;
307 }
308
316 @NbBundle.Messages({
317 "CallLogArtifactViewer_heading_parties=Parties",
318 "CallLogArtifactViewer_value_unknown=Unknown",
319 "CallLogArtifactViewer_label_from=From",
320 "CallLogArtifactViewer_label_to=To"
321 })
322 private List<AccountPersonaSearcherData> updateView(CallLogViewData callLogViewData) {
323
324 CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, 0, Bundle.CallLogArtifactViewer_heading_parties());
325
326 List<AccountPersonaSearcherData> dataList = new ArrayList<>();
327 // Display "From" if we have non-local device accounts
328 if (callLogViewData.getFromAccount() != null) {
329 CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_from());
330
331 // check if this is local account
332 String accountDisplayString = getAccountDisplayString(callLogViewData.getFromAccount(), callLogViewData);
333 CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
334
335 List<String> contactNames = callLogViewData.getFromContactNameList();
336 for (String name : contactNames) {
337 CommunicationArtifactViewerHelper.addContactRow(this, m_gridBagLayout, m_constraints, name);
338 }
339
340 // show persona
341 dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount()));
342 }
343
344 // Display "To" if we have non-local device accounts
345 if (callLogViewData.getToAccount() != null) {
346 CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_to());
347 String accountDisplayString = getAccountDisplayString(callLogViewData.getToAccount(), callLogViewData);
348 CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
349
350 List<String> contactNames = callLogViewData.getToContactNameList();
351 for (String name : contactNames) {
352 CommunicationArtifactViewerHelper.addContactRow(this, m_gridBagLayout, m_constraints, name);
353 }
354
355 dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount()));
356
357 }
358
359 // Display other parties
360 for (String otherParty : callLogViewData.getOtherParties()) {
361 CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_to());
362 CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, otherParty);
363
364 dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, otherParty));
365 }
366
367 updateMetadataView(callLogViewData);
368
369 updateOtherAttributesView(callLogViewData);
370
371 updateSourceView(callLogViewData);
372
373 if (CentralRepository.isEnabled() == false) {
375 }
376
377 CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
378
379 this.setLayout(m_gridBagLayout);
380 this.revalidate();
381 return dataList;
382 }
383
389 @NbBundle.Messages({
390 "CallLogArtifactViewer_heading_metadata=Metadata",
391 "CallLogArtifactViewer_label_direction=Direction",
392 "CallLogArtifactViewer_label_date=Date",
393 "CallLogArtifactViewer_label_duration=Duration"
394 })
395 private void updateMetadataView(CallLogViewData callLogViewData) {
396
397 CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, ContentViewerDefaults.getSectionSpacing(), Bundle.CallLogArtifactViewer_heading_metadata());
398
399 CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_direction());
400 if (callLogViewData.getDirection() != null) {
401 CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDirection());
402 } else {
403 CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown());
404 }
405
406 if (callLogViewData.getDateTimeStr() != null) {
407 CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_date());
408 CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDateTimeStr());
409 }
410
411 if (callLogViewData.getDuration() != null) {
412 CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_duration());
413 CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDuration());
414 }
415
416 }
417
423 @NbBundle.Messages({
424 "CallLogArtifactViewer_heading_Source=Source",
425 "CallLogArtifactViewer_label_datasource=Data Source",
426 "CallLogArtifactViewer_label_hostName=Host"})
427 private void updateSourceView(CallLogViewData callLogViewData) {
428 CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, ContentViewerDefaults.getSectionSpacing(), Bundle.CallLogArtifactViewer_heading_Source());
429
430 CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_hostName());
431 CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, StringUtils.defaultString(callLogViewData.getHostName()));
432
433 CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_datasource());
434 CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDataSourceName());
435 }
436
442 @NbBundle.Messages({
443 "CallLogArtifactViewer_heading_others=Other Attributes"
444 })
445 private void updateOtherAttributesView(CallLogViewData callLogViewData) {
446
447 if (callLogViewData.getOtherAttributes().isEmpty()) {
448 return;
449 }
450 CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, ContentViewerDefaults.getSectionSpacing(), Bundle.CallLogArtifactViewer_heading_others());
451
452 for (Map.Entry<String, String> entry : callLogViewData.getOtherAttributes().entrySet()) {
453 CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, entry.getKey());
454 CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, entry.getValue());
455 }
456 }
457
458 @NbBundle.Messages({
459 "CalllogArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas."
460 })
461 private void showCRDisabledMessage() {
462 Insets messageInsets = new Insets(ContentViewerDefaults.getSectionSpacing(), 0, ContentViewerDefaults.getLineSpacing(), 0);
463 CommunicationArtifactViewerHelper.addMessageRow(this, m_gridBagLayout, messageInsets, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
464 m_constraints.gridy++;
465 }
466
477 @NbBundle.Messages({
478 "CallLogArtifactViewer_suffix_local=(Local)",})
479 private String getAccountDisplayString(String accountIdentifier, CallLogViewData callLogViewDataNew) {
480 String accountDisplayValue = accountIdentifier;
481 if (callLogViewDataNew.getLocalAccountId() != null && callLogViewDataNew.getLocalAccountId().equalsIgnoreCase(accountIdentifier)) {
482 accountDisplayValue += " " + Bundle.CallLogArtifactViewer_suffix_local();
483 }
484 return accountDisplayValue;
485 }
486
487 @Override
488 public Component getComponent() {
489 return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
490 }
491
492 @Override
493 public boolean isSupported(BlackboardArtifact artifact) {
494
495 return (artifact != null)
496 && (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID());
497 }
498
502 private void resetComponent() {
503
504 // cancel any outstanding persona searching threads.
505 if (currentAccountFetcher != null && !currentAccountFetcher.isDone()) {
506 currentAccountFetcher.cancel(true);
508 }
509
510 // clear the panel
511 this.removeAll();
512 this.setLayout(null);
513
514 m_gridBagLayout = new GridBagLayout();
515 m_constraints = new GridBagConstraints();
516
517 m_constraints.anchor = GridBagConstraints.FIRST_LINE_START;
518 m_constraints.gridy = 0;
519 m_constraints.gridx = 0;
520 m_constraints.weighty = 0.0;
521 m_constraints.weightx = 0.0; // keep components fixed horizontally.
522 m_constraints.insets = new java.awt.Insets(0, ContentViewerDefaults.getSectionIndent(), 0, 0);
523 m_constraints.fill = GridBagConstraints.NONE;
524
525 }
526
527
528 // Variables declaration - do not modify//GEN-BEGIN:variables
529 // End of variables declaration//GEN-END:variables
530}
String getAccountDisplayString(String accountIdentifier, CallLogViewData callLogViewDataNew)
List< AccountPersonaSearcherData > updateView(CallLogViewData callLogViewData)
void extractTimeAndDuration(BlackboardArtifact artifact, CallLogViewData callLogViewData)
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static synchronized List< String > getContactNameList(String accountTypeSpecificID)

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