Autopsy  4.20.0
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  */
19 package org.sleuthkit.autopsy.contentviewers.artifactviewers;
20 
21 import java.awt.Component;
22 import java.awt.GridBagConstraints;
23 import java.awt.GridBagLayout;
24 import java.awt.Insets;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Optional;
32 import java.util.Set;
33 import java.util.logging.Level;
34 import javax.swing.JScrollPane;
35 import javax.swing.border.EmptyBorder;
36 import org.apache.commons.lang3.ObjectUtils;
37 import org.apache.commons.lang3.StringUtils;
38 import org.openide.util.NbBundle;
39 import org.openide.util.lookup.ServiceProvider;
46 import org.sleuthkit.datamodel.BlackboardArtifact;
47 import org.sleuthkit.datamodel.BlackboardAttribute;
48 import org.sleuthkit.datamodel.Content;
49 import org.sleuthkit.datamodel.DataSource;
50 import org.sleuthkit.datamodel.TskCoreException;
51 
57 @ServiceProvider(service = ArtifactContentViewer.class)
58 public 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 
83  initComponents();
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) {
101  resetComponent();
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 {
119  currentAccountFetcher = null;
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) {
374  showCRDisabledMessage();
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);
507  currentAccountFetcher = null;
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 }
static synchronized List< String > getContactNameList(String accountTypeSpecificID)
void extractTimeAndDuration(BlackboardArtifact artifact, CallLogViewData callLogViewData)
List< AccountPersonaSearcherData > updateView(CallLogViewData callLogViewData)
String getAccountDisplayString(String accountIdentifier, CallLogViewData callLogViewDataNew)
Map< String, String > extractOtherAttributes(BlackboardArtifact artifact)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

Copyright © 2012-2022 Basis Technology. Generated on: Tue Aug 1 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.