Autopsy  4.17.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 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.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.logging.Level;
32 import javax.swing.JScrollPane;
33 import org.apache.commons.lang3.ObjectUtils;
34 import org.apache.commons.lang3.StringUtils;
35 import org.openide.util.NbBundle;
36 import org.openide.util.lookup.ServiceProvider;
40 import org.sleuthkit.datamodel.BlackboardArtifact;
41 import org.sleuthkit.datamodel.BlackboardAttribute;
42 import org.sleuthkit.datamodel.Content;
43 import org.sleuthkit.datamodel.DataSource;
44 import org.sleuthkit.datamodel.TskCoreException;
45 
51 @ServiceProvider(service = ArtifactContentViewer.class)
52 public class CallLogArtifactViewer extends javax.swing.JPanel implements ArtifactContentViewer {
53 
54  private final static Logger logger = Logger.getLogger(CallLogArtifactViewer.class.getName());
55  private static final long serialVersionUID = 1L;
56 
57  private static final Set<Integer> HANDLED_ATTRIBUTE_TYPES = new HashSet<Integer>(Arrays.asList(
58  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER.getTypeID(),
59  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO.getTypeID(),
60  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM.getTypeID(),
61  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID.getTypeID(),
62  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION.getTypeID(),
63  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME.getTypeID(),
64  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START.getTypeID(),
65  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END.getTypeID()
66  ));
67 
68  private GridBagLayout m_gridBagLayout = new GridBagLayout();
69  private GridBagConstraints m_constraints = new GridBagConstraints();
70 
71  private PersonaAccountFetcher currentAccountFetcher = null;
72 
77  initComponents();
78  }
79 
85  @SuppressWarnings("unchecked")
86  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
87  private void initComponents() {
88 
89  setLayout(new java.awt.GridBagLayout());
90  }// </editor-fold>//GEN-END:initComponents
91 
92  @Override
93  public void setArtifact(BlackboardArtifact artifact) {
94  resetComponent();
95 
96  if (artifact == null) {
97  return;
98  }
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 
107  // update the view with the call log data
108  if (callLogViewData != null) {
109  List<AccountPersonaSearcherData> personaSearchDataList = updateView(callLogViewData);
110  if (!personaSearchDataList.isEmpty()) {
111  currentAccountFetcher = new PersonaAccountFetcher(artifact, personaSearchDataList, this);
112  currentAccountFetcher.execute();
113  } else {
114  currentAccountFetcher = null;
115  }
116  }
117  // repaint
118  this.revalidate();
119  }
120 
130  private CallLogViewData getCallLogViewData(BlackboardArtifact artifact) throws TskCoreException {
131 
132  if (artifact == null) {
133  return null;
134  }
135 
136  BlackboardAttribute directionAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION));
137  BlackboardAttribute toAccountAttr = null;
138  BlackboardAttribute fromAccountAttr = null;
139  BlackboardAttribute localAccountAttr = null;
140 
141  CallLogViewData callLogViewData = null;
142 
143  String direction = null;
144  String fromAccountIdentifier = null;
145  String toAccountIdentifier = null;
146  List<String> otherParties = null;
147  List<String> toContactNames = null;
148  List<String> fromContactNames = null;
149 
150  Content dataSource = artifact.getDataSource();
151  String deviceId = ((DataSource) dataSource).getDeviceId();
152 
153  if (directionAttr != null) {
154  direction = directionAttr.getValueString();
155  if (direction.equalsIgnoreCase("Incoming")) {
156  fromAccountAttr = ObjectUtils.firstNonNull(
157  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)),
158  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
159  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
160  );
161 
162  toAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
163  localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO));
164  } else if (direction.equalsIgnoreCase("Outgoing")) {
165  toAccountAttr = ObjectUtils.firstNonNull(
166  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)),
167  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
168  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
169  );
170 
171  fromAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
172  localAccountAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM));
173  }
174  } else {
175  // if direction isn't known, check all the usual attributes that may have the number/address
176  // in the absence of sufficent data, any number available will be displayed as a From address.
177  if (fromAccountAttr == null) {
178  fromAccountAttr = ObjectUtils.firstNonNull(
179  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM)),
180  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO)),
181  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER)),
182  artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ID))
183  );
184  }
185  }
186 
187  // get the from account address
188  if (fromAccountAttr != null) {
189  String fromAccountAttrValue = fromAccountAttr.getValueString();
190  if (fromAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
191  fromAccountIdentifier = fromAccountAttrValue;
192  fromContactNames = ContactCache.getContactNameList(fromAccountIdentifier);
193  }
194  }
195 
196  if (toAccountAttr != null) {
197  // TO may be a list of comma separated values.
198  String[] numbers = toAccountAttr.getValueString().split(",");
199  String toAccountAttrValue = StringUtils.trim(numbers[0]);
200  if (toAccountAttrValue.equalsIgnoreCase(deviceId) == false) {
201  toAccountIdentifier = toAccountAttrValue;
202  toContactNames = ContactCache.getContactNameList(toAccountIdentifier);
203  }
204 
205  // if more than one To address, then stick the rest of them in the
206  // "Other parties" list.
207  if (numbers.length > 1) {
208  otherParties = new ArrayList<>();
209  for (int i = 1; i < numbers.length; i++) {
210  otherParties.add(StringUtils.trim(numbers[i]));
211  }
212  }
213  }
214 
215  // if we have at least one address attribute
216  if (null != fromAccountAttr || null != toAccountAttr) {
217  callLogViewData = new CallLogViewData(fromAccountIdentifier, toAccountIdentifier);
218  callLogViewData.setDirection(direction);
219 
220  callLogViewData.setOtherParties(otherParties);
221 
222  extractTimeAndDuration(artifact, callLogViewData);
223 
224  callLogViewData.setDataSourceName(dataSource.getName());
225 
226  // set local account, if it can be deduced.
227  if (localAccountAttr != null) {
228  String attrValue = localAccountAttr.getValueString();
229  // value must be a singular address and not a deviceId to be the local account id
230  if (attrValue.equalsIgnoreCase(deviceId) == false && attrValue.contains(",") == false) {
231  callLogViewData.setLocalAccountId(attrValue);
232  }
233  }
234 
235  callLogViewData.setOtherAttributes(extractOtherAttributes(artifact));
236 
237  callLogViewData.setFromContactNameList(fromContactNames);
238  callLogViewData.setToContactNameList(toContactNames);
239  }
240 
241  return callLogViewData;
242  }
243 
254  private void extractTimeAndDuration(BlackboardArtifact artifact, CallLogViewData callLogViewData) throws TskCoreException {
255 
256  BlackboardAttribute startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START));
257  if (startTimeAttr == null) {
258  startTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME));
259  }
260  if (startTimeAttr != null) {
261  long startTime = startTimeAttr.getValueLong();
262  callLogViewData.setDateTimeStr(startTimeAttr.getDisplayString());
263 
264  BlackboardAttribute endTimeAttr = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END));
265  if (endTimeAttr != null) {
266  long endTime = endTimeAttr.getValueLong();
267  if (endTime > 0 && (endTime - startTime) > 0) {
268  callLogViewData.setDuration(String.format("%d seconds", (endTime - startTime)));
269  }
270  }
271  }
272  }
273 
284  private Map<String, String> extractOtherAttributes(BlackboardArtifact artifact) throws TskCoreException {
285  List<BlackboardAttribute> attributes = artifact.getAttributes();
286  Map<String, String> otherAttributes = new HashMap<>();
287 
288  for (BlackboardAttribute attr : attributes) {
289  if (HANDLED_ATTRIBUTE_TYPES.contains(attr.getAttributeType().getTypeID()) == false) {
290  otherAttributes.put(attr.getAttributeType().getDisplayName(), attr.getDisplayString());
291  }
292  }
293 
294  return otherAttributes;
295  }
296 
304  @NbBundle.Messages({
305  "CallLogArtifactViewer_heading_parties=Parties",
306  "CallLogArtifactViewer_value_unknown=Unknown",
307  "CallLogArtifactViewer_label_from=From",
308  "CallLogArtifactViewer_label_to=To"
309  })
310  private List<AccountPersonaSearcherData> updateView(CallLogViewData callLogViewData) {
311 
312  CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_parties());
313 
314  List<AccountPersonaSearcherData> dataList = new ArrayList<>();
315  // Display "From" if we have non-local device accounts
316  if (callLogViewData.getFromAccount() != null) {
317  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_from());
318 
319  // check if this is local account
320  String accountDisplayString = getAccountDisplayString(callLogViewData.getFromAccount(), callLogViewData);
321  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
322 
323  List<String> contactNames = callLogViewData.getFromContactNameList();
324  for (String name : contactNames) {
325  CommunicationArtifactViewerHelper.addContactRow(this, m_gridBagLayout, m_constraints, name);
326  }
327 
328  // show persona
329  dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getFromAccount()));
330  }
331 
332  // Display "To" if we have non-local device accounts
333  if (callLogViewData.getToAccount() != null) {
334  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_to());
335  String accountDisplayString = getAccountDisplayString(callLogViewData.getToAccount(), callLogViewData);
336  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, accountDisplayString);
337 
338  List<String> contactNames = callLogViewData.getToContactNameList();
339  for (String name : contactNames) {
340  CommunicationArtifactViewerHelper.addContactRow(this, m_gridBagLayout, m_constraints, name);
341  }
342 
343  dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, callLogViewData.getToAccount()));
344 
345  }
346 
347  // Display other parties
348  for (String otherParty : callLogViewData.getOtherParties()) {
349  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_to());
350  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, otherParty);
351 
352  dataList.addAll(CommunicationArtifactViewerHelper.addPersonaRow(this, m_gridBagLayout, this.m_constraints, otherParty));
353  }
354 
355  updateMetadataView(callLogViewData);
356 
357  updateOtherAttributesView(callLogViewData);
358 
359  updateSourceView(callLogViewData);
360 
361  if (CentralRepository.isEnabled() == false) {
362  showCRDisabledMessage();
363  }
364 
365  CommunicationArtifactViewerHelper.addPageEndGlue(this, m_gridBagLayout, this.m_constraints);
366 
367  this.setLayout(m_gridBagLayout);
368  this.revalidate();
369  this.repaint();
370 
371  return dataList;
372  }
373 
379  @NbBundle.Messages({
380  "CallLogArtifactViewer_heading_metadata=Metadata",
381  "CallLogArtifactViewer_label_direction=Direction",
382  "CallLogArtifactViewer_label_date=Date",
383  "CallLogArtifactViewer_label_duration=Duration"
384  })
385  private void updateMetadataView(CallLogViewData callLogViewData) {
386 
387  CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_metadata());
388 
389  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_direction());
390  if (callLogViewData.getDirection() != null) {
391  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDirection());
392  } else {
393  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_value_unknown());
394  }
395 
396  if (callLogViewData.getDateTimeStr() != null) {
397  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_date());
398  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDateTimeStr());
399  }
400 
401  if (callLogViewData.getDuration() != null) {
402  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_duration());
403  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDuration());
404  }
405 
406  }
407 
413  @NbBundle.Messages({
414  "CallLogArtifactViewer_heading_Source=Source",
415  "CallLogArtifactViewer_label_datasource=Data Source",})
416  private void updateSourceView(CallLogViewData callLogViewData) {
417  CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_Source());
418  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_label_datasource());
419  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, callLogViewData.getDataSourceName());
420  }
421 
427  @NbBundle.Messages({
428  "CallLogArtifactViewer_heading_others=Other Attributes"
429  })
430  private void updateOtherAttributesView(CallLogViewData callLogViewData) {
431 
432  if (callLogViewData.getOtherAttributes().isEmpty()) {
433  return;
434  }
435  CommunicationArtifactViewerHelper.addHeader(this, m_gridBagLayout, this.m_constraints, Bundle.CallLogArtifactViewer_heading_others());
436 
437  for (Map.Entry<String, String> entry : callLogViewData.getOtherAttributes().entrySet()) {
438  CommunicationArtifactViewerHelper.addKey(this, m_gridBagLayout, this.m_constraints, entry.getKey());
439  CommunicationArtifactViewerHelper.addValue(this, m_gridBagLayout, this.m_constraints, entry.getValue());
440  }
441  }
442 
443  @NbBundle.Messages({
444  "CalllogArtifactViewer_cr_disabled_message=Enable Central Repository to view, create and edit personas."
445  })
446  private void showCRDisabledMessage() {
447  CommunicationArtifactViewerHelper.addBlankLine(this, m_gridBagLayout, m_constraints);
448  m_constraints.gridy++;
449  CommunicationArtifactViewerHelper.addMessageRow(this, m_gridBagLayout, 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, CommunicationArtifactViewerHelper.LEFT_INSET, 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: Tue Jan 19 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.