Autopsy  4.19.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.Set;
32 import java.util.logging.Level;
33 import javax.swing.JScrollPane;
34 import javax.swing.border.EmptyBorder;
35 import org.apache.commons.lang3.ObjectUtils;
36 import org.apache.commons.lang3.StringUtils;
37 import org.openide.util.NbBundle;
38 import org.openide.util.lookup.ServiceProvider;
43 import org.sleuthkit.datamodel.BlackboardArtifact;
44 import org.sleuthkit.datamodel.BlackboardAttribute;
45 import org.sleuthkit.datamodel.Content;
46 import org.sleuthkit.datamodel.DataSource;
47 import org.sleuthkit.datamodel.TskCoreException;
48 
54 @ServiceProvider(service = ArtifactContentViewer.class)
55 public class CallLogArtifactViewer extends javax.swing.JPanel implements ArtifactContentViewer {
56 
57  private final static Logger logger = Logger.getLogger(CallLogArtifactViewer.class.getName());
58  private static final long serialVersionUID = 1L;
59 
60  private static final Set<Integer> HANDLED_ATTRIBUTE_TYPES = new HashSet<Integer>(Arrays.asList(
61  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(),
62  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO.getTypeID(),
63  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM.getTypeID(),
64  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID.getTypeID(),
65  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(),
66  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
67  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(),
68  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID()
69  ));
70 
71  private GridBagLayout m_gridBagLayout = new GridBagLayout();
72  private GridBagConstraints m_constraints = new GridBagConstraints();
73 
74  private PersonaAccountFetcher currentAccountFetcher = null;
75 
80  initComponents();
81  this.setBorder(new EmptyBorder(ContentViewerDefaults.getPanelInsets()));
82  }
83 
89  @SuppressWarnings("unchecked")
90  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
91  private void initComponents() {
92 
93  setLayout(new java.awt.GridBagLayout());
94  }// </editor-fold>//GEN-END:initComponents
95 
96  @Override
97  public void setArtifact(BlackboardArtifact artifact) {
98  resetComponent();
99 
100  CallLogViewData callLogViewData = null;
101  try {
102  callLogViewData = getCallLogViewData(artifact);
103  } catch (TskCoreException ex) {
104  logger.log(Level.SEVERE, String.format("Error getting attributes for Calllog artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex);
105  }
106  List<AccountPersonaSearcherData> personaSearchDataList = new ArrayList<>();
107  // update the view with the call log data
108  if (callLogViewData != null) {
109  personaSearchDataList.addAll(updateView(callLogViewData));
110 
111  }
112  if (!personaSearchDataList.isEmpty()) {
113  currentAccountFetcher = new PersonaAccountFetcher(artifact, personaSearchDataList, this);
114  currentAccountFetcher.execute();
115  } else {
116  currentAccountFetcher = null;
117  }
118 
119  // repaint
120  this.revalidate();
121  this.repaint();
122  }
123 
133  private CallLogViewData getCallLogViewData(BlackboardArtifact artifact) throws TskCoreException {
134 
135  if (artifact == null) {
136  return null;
137  }
138 
139  BlackboardAttribute directionAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION));
140  BlackboardAttribute toAccountAttr = null;
141  BlackboardAttribute fromAccountAttr = null;
142  BlackboardAttribute localAccountAttr = null;
143 
144  CallLogViewData callLogViewData = null;
145 
146  String direction = null;
147  String fromAccountIdentifier = null;
148  String toAccountIdentifier = null;
149  List<String> otherParties = null;
150  List<String> toContactNames = null;
151  List<String> fromContactNames = null;
152 
153  Content dataSource = artifact.getDataSource();
154  String deviceId = ((DataSource) dataSource).getDeviceId();
155 
156  if (directionAttr != null) {
157  direction = directionAttr.getValueString();
158  if (direction.equalsIgnoreCase("Incoming")) {
159  fromAccountAttr = ObjectUtils.firstNonNull(
160  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)),
161  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
162  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
163  );
164 
165  toAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
166  localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
167  } else if (direction.equalsIgnoreCase("Outgoing")) {
168  toAccountAttr = ObjectUtils.firstNonNull(
169  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)),
170  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
171  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
172  );
173 
174  fromAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
175  localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
176  }
177  } else {
178  // if direction isn't known, check all the usual attributes that may have the number/address
179  // in the absence of sufficent data, any number available will be displayed as a From address.
180  if (fromAccountAttr == null) {
181  fromAccountAttr = ObjectUtils.firstNonNull(
182  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)),
183  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)),
184  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
185  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
186  );
187  }
188  }
189 
190  // get the from account address
191  if (fromAccountAttr != null) {
192  String fromAccountAttrValue = fromAccountAttr.getValueString();
193  if (fromAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
194  fromAccountIdentifier = fromAccountAttrValue;
195  fromContactNames = ContactCache.getContactNameList(fromAccountIdentifier);
196  }
197  }
198 
199  if (toAccountAttr != null) {
200  // TO may be a list of comma separated values.
201  String[] numbers = toAccountAttr.getValueString().split(",");
202  String toAccountAttrValue = StringUtils.trim(numbers[0]);
203  if (toAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
204  toAccountIdentifier = toAccountAttrValue;
205  toContactNames = ContactCache.getContactNameList(toAccountIdentifier);
206  }
207 
208  // if more than one To address, then stick the rest of them in the
209  // "Other parties" list.
210  if (numbers.length > 1) {
211  otherParties = new ArrayList<>();
212  for (int i = 1; i < numbers.length; i++) {
213  otherParties.add(StringUtils.trim(numbers[i]));
214  }
215  }
216  }
217 
218  // if we have at least one address attribute
219  if (null != fromAccountAttr || null != toAccountAttr) {
220  callLogViewData = new CallLogViewData(fromAccountIdentifier, toAccountIdentifier);
221  callLogViewData.setDirection(direction);
222 
223  callLogViewData.setOtherParties(otherParties);
224 
225  extractTimeAndDuration(artifact, callLogViewData);
226 
227  callLogViewData.setDataSourceName(dataSource.getName());
228 
229  // set local account, if it can be deduced.
230  if (localAccountAttr != null) {
231  String attrValue = localAccountAttr.getValueString();
232  // value must be a singular address and not a deviceId to be the local account id
233  if (attrValue.equalsIgnoreCase(deviceId) == false && attrValue.contains(",") == false) {
234  callLogViewData.setLocalAccountId(attrValue);
235  }
236  }
237 
238  callLogViewData.setOtherAttributes(extractOtherAttributes(artifact));
239 
240  callLogViewData.setFromContactNameList(fromContactNames);
241  callLogViewData.setToContactNameList(toContactNames);
242  }
243 
244  return callLogViewData;
245  }
246 
257  private void extractTimeAndDuration(BlackboardArtifact artifact, CallLogViewData callLogViewData) throws TskCoreException {
258 
259  BlackboardAttribute startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START));
260  if (startTimeAttr == null) {
261  startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME));
262  }
263  if (startTimeAttr != null) {
264  long startTime = startTimeAttr.getValueLong();
265  callLogViewData.setDateTimeStr(startTimeAttr.getDisplayString());
266 
267  BlackboardAttribute endTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END));
268  if (endTimeAttr != null) {
269  long endTime = endTimeAttr.getValueLong();
270  if (endTime > 0 && (endTime - startTime) > 0) {
271  callLogViewData.setDuration(String.format("%d seconds", (endTime - startTime)));
272  }
273  }
274  }
275  }
276 
287  private Map<String, String> extractOtherAttributes(BlackboardArtifact artifact) throws TskCoreException {
288  List<BlackboardAttribute> attributes = artifact.getAttributes();
289  Map<String, String> otherAttributes = new HashMap<>();
290 
291  for (BlackboardAttribute attr : attributes) {
292  if (HANDLED_ATTRIBUTE_TYPES.contains(attr.getAttributeType().getTypeID()) == false) {
293  otherAttributes.put(attr.getAttributeType().getDisplayName(), attr.getDisplayString());
294  }
295  }
296 
297  return otherAttributes;
298  }
299 
307  @NbBundle.Messages({
308  "CallLogArtifactViewer_heading_parties=Parties",
309  "CallLogArtifactViewer_value_unknown=Unknown",
310  "CallLogArtifactViewer_label_from=From",
311  "CallLogArtifactViewer_label_to=To"
312  })
313  private List<AccountPersonaSearcherData> updateView(CallLogViewData callLogViewData) {
314 
315  CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, 0, Bundle.CallLogArtifactViewer_heading_parties());
316 
317  List<AccountPersonaSearcherData> dataList = new ArrayList<>();
318  // Display "From" if we have non-local device accounts
319  if (callLogViewData.getFromAccount() != null) {
320  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_from());
321 
322  // check if this is local account
323  String accountDisplayString = getAccountDisplayString(callLogViewData.getFromAccount(), callLogViewData);
324  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
325 
326  List<String> contactNames = callLogViewData.getFromContactNameList();
327  for (String name : contactNames) {
328  CommunicationArtifactViewerHelper.addContactRow(this, m_gridBagLayout, m_constraints, name);
329  }
330 
331  // show persona
332  dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount()));
333  }
334 
335  // Display "To" if we have non-local device accounts
336  if (callLogViewData.getToAccount() != null) {
337  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_to());
338  String accountDisplayString = getAccountDisplayString(callLogViewData.getToAccount(), callLogViewData);
339  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
340 
341  List<String> contactNames = callLogViewData.getToContactNameList();
342  for (String name : contactNames) {
343  CommunicationArtifactViewerHelper.addContactRow(this, m_gridBagLayout, m_constraints, name);
344  }
345 
346  dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount()));
347 
348  }
349 
350  // Display other parties
351  for (String otherParty : callLogViewData.getOtherParties()) {
352  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_to());
353  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, otherParty);
354 
355  dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, otherParty));
356  }
357 
358  updateMetadataView(callLogViewData);
359 
360  updateOtherAttributesView(callLogViewData);
361 
362  updateSourceView(callLogViewData);
363 
364  if (CentralRepository.isEnabled() == false) {
365  showCRDisabledMessage();
366  }
367 
368  CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
369 
370  this.setLayout(m_gridBagLayout);
371  this.revalidate();
372  return dataList;
373  }
374 
380  @NbBundle.Messages({
381  "CallLogArtifactViewer_heading_metadata=Metadata",
382  "CallLogArtifactViewer_label_direction=Direction",
383  "CallLogArtifactViewer_label_date=Date",
384  "CallLogArtifactViewer_label_duration=Duration"
385  })
386  private void updateMetadataView(CallLogViewData callLogViewData) {
387 
388  CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, ContentViewerDefaults.getSectionSpacing(), Bundle.CallLogArtifactViewer_heading_metadata());
389 
390  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_direction());
391  if (callLogViewData.getDirection() != null) {
392  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDirection());
393  } else {
394  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown());
395  }
396 
397  if (callLogViewData.getDateTimeStr() != null) {
398  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_date());
399  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDateTimeStr());
400  }
401 
402  if (callLogViewData.getDuration() != null) {
403  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_duration());
404  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDuration());
405  }
406 
407  }
408 
414  @NbBundle.Messages({
415  "CallLogArtifactViewer_heading_Source=Source",
416  "CallLogArtifactViewer_label_datasource=Data Source",})
417  private void updateSourceView(CallLogViewData callLogViewData) {
418  CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, ContentViewerDefaults.getSectionSpacing(), Bundle.CallLogArtifactViewer_heading_Source());
419  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_datasource());
420  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDataSourceName());
421  }
422 
428  @NbBundle.Messages({
429  "CallLogArtifactViewer_heading_others=Other Attributes"
430  })
431  private void updateOtherAttributesView(CallLogViewData callLogViewData) {
432 
433  if (callLogViewData.getOtherAttributes().isEmpty()) {
434  return;
435  }
436  CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, ContentViewerDefaults.getSectionSpacing(), Bundle.CallLogArtifactViewer_heading_others());
437 
438  for (Map.Entry<String, String> entry : callLogViewData.getOtherAttributes().entrySet()) {
439  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, entry.getKey());
440  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, entry.getValue());
441  }
442  }
443 
444  @NbBundle.Messages({
445  "CalllogArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas."
446  })
447  private void showCRDisabledMessage() {
448  Insets messageInsets = new Insets(ContentViewerDefaults.getSectionSpacing(), 0, ContentViewerDefaults.getLineSpacing(), 0);
449  CommunicationArtifactViewerHelper.addMessageRow(this, m_gridBagLayout, messageInsets, m_constraints, Bundle.ContactArtifactViewer_cr_disabled_message());
450  m_constraints.gridy++;
451  }
452 
463  @NbBundle.Messages({
464  "CallLogArtifactViewer_suffix_local=(Local)",})
465  private String getAccountDisplayString(String accountIdentifier, CallLogViewData callLogViewDataNew) {
466  String accountDisplayValue = accountIdentifier;
467  if (callLogViewDataNew.getLocalAccountId() != null && callLogViewDataNew.getLocalAccountId().equalsIgnoreCase(accountIdentifier)) {
468  accountDisplayValue += " " + Bundle.CallLogArtifactViewer_suffix_local();
469  }
470  return accountDisplayValue;
471  }
472 
473  @Override
474  public Component getComponent() {
475  return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
476  }
477 
478  @Override
479  public boolean isSupported(BlackboardArtifact artifact) {
480 
481  return (artifact != null)
482  && (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG.getTypeID());
483  }
484 
488  private void resetComponent() {
489 
490  // cancel any outstanding persona searching threads.
491  if (currentAccountFetcher != null && !currentAccountFetcher.isDone()) {
492  currentAccountFetcher.cancel(true);
493  currentAccountFetcher = null;
494  }
495 
496  // clear the panel
497  this.removeAll();
498  this.setLayout(null);
499 
500  m_gridBagLayout = new GridBagLayout();
501  m_constraints = new GridBagConstraints();
502 
503  m_constraints.anchor = GridBagConstraints.FIRST_LINE_START;
504  m_constraints.gridy = 0;
505  m_constraints.gridx = 0;
506  m_constraints.weighty = 0.0;
507  m_constraints.weightx = 0.0; // keep components fixed horizontally.
508  m_constraints.insets = new java.awt.Insets(0, ContentViewerDefaults.getSectionIndent(), 0, 0);
509  m_constraints.fill = GridBagConstraints.NONE;
510 
511  }
512 
513 
514  // Variables declaration - do not modify//GEN-BEGIN:variables
515  // End of variables declaration//GEN-END:variables
516 }
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-2021 Basis Technology. Generated on: Fri Aug 6 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.