Autopsy  4.19.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
XRYCallsFileParser.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019-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.datasourceprocessors.xry;
20 
21 import java.time.format.DateTimeParseException;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.List;
25 import java.util.logging.Level;
27 import org.sleuthkit.datamodel.AbstractFile;
28 import org.sleuthkit.datamodel.Account;
29 import org.sleuthkit.datamodel.Blackboard.BlackboardException;
30 import org.sleuthkit.datamodel.BlackboardArtifact;
31 import org.sleuthkit.datamodel.BlackboardAttribute;
32 import org.sleuthkit.datamodel.Content;
33 import org.sleuthkit.datamodel.InvalidAccountIDException;
34 import org.sleuthkit.datamodel.SleuthkitCase;
35 import org.sleuthkit.datamodel.TskCoreException;
36 import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
37 import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CallMediaType;
38 import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CommunicationDirection;
39 
43 final class XRYCallsFileParser extends AbstractSingleEntityParser {
44 
45  private static final Logger logger = Logger.getLogger(XRYCallsFileParser.class.getName());
46 
51  private enum XryKey {
52  NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME),
53  TIME("time", null),
54  DIRECTION("direction", null),
55  CALL_TYPE("call type", null),
56  NUMBER("number", null),
57  TEL("tel", null),
58  TO("to", null),
59  FROM("from", null),
60  DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
61  DURATION("duration", null),
62  STORAGE("storage", null),
63  INDEX("index", null),
64  TYPE("type", null),
65  NAME("name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
66 
67  private final String name;
68  private final BlackboardAttribute.ATTRIBUTE_TYPE type;
69 
70  XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type) {
71  this.name = name;
72  this.type = type;
73  }
74 
75  public BlackboardAttribute.ATTRIBUTE_TYPE getType() {
76  return type;
77  }
78 
82  public static boolean contains(String key) {
83  try {
85  return true;
86  } catch (IllegalArgumentException ex) {
87  return false;
88  }
89  }
90 
98  public static XryKey fromDisplayName(String key) {
99  String normalizedKey = key.trim().toLowerCase();
100  for (XryKey keyChoice : XryKey.values()) {
101  if (normalizedKey.equals(keyChoice.name)) {
102  return keyChoice;
103  }
104  }
105 
106  throw new IllegalArgumentException(String.format("Key [%s] was not found."
107  + " All keys should be tested with contains.", key));
108  }
109  }
110 
114  private enum XryNamespace {
115  TO("to"),
116  FROM("from"),
117  NONE(null);
118 
119  private final String name;
120 
121  XryNamespace(String name) {
122  this.name = name;
123  }
124 
129  public static boolean contains(String xryNamespace) {
130  try {
131  XryNamespace.fromDisplayName(xryNamespace);
132  return true;
133  } catch (IllegalArgumentException ex) {
134  return false;
135  }
136  }
137 
146  public static XryNamespace fromDisplayName(String xryNamespace) {
147  String normalizedNamespace = xryNamespace.trim().toLowerCase();
148  for (XryNamespace keyChoice : XryNamespace.values()) {
149  if (normalizedNamespace.equals(keyChoice.name)) {
150  return keyChoice;
151  }
152  }
153 
154  throw new IllegalArgumentException(String.format("Key [%s] was not found."
155  + " All keys should be tested with contains.", xryNamespace));
156  }
157  }
158 
159  @Override
160  boolean canProcess(XRYKeyValuePair pair) {
161  return XryKey.contains(pair.getKey());
162  }
163 
164  @Override
165  boolean isNamespace(String nameSpace) {
166  return XryNamespace.contains(nameSpace);
167  }
168 
169  @Override
170  void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent, SleuthkitCase currentCase) throws TskCoreException, BlackboardException {
171  // Transform all the data from XRY land into the appropriate CommHelper
172  // data types.
173  String callerId = null;
174  final Collection<String> calleeList = new ArrayList<>();
175  CommunicationDirection direction = CommunicationDirection.UNKNOWN;
176  long startTime = 0L;
177  final long endTime = 0L;
178  final CallMediaType callType = CallMediaType.UNKNOWN;
179  final Collection<BlackboardAttribute> otherAttributes = new ArrayList<>();
180 
181  for (XRYKeyValuePair pair : keyValuePairs) {
182  XryKey xryKey = XryKey.fromDisplayName(pair.getKey());
183  XryNamespace xryNamespace = XryNamespace.NONE;
184  if (XryNamespace.contains(pair.getNamespace())) {
185  xryNamespace = XryNamespace.fromDisplayName(pair.getNamespace());
186  }
187 
188  switch (xryKey) {
189  case TEL:
190  case NUMBER:
191  if(!XRYUtils.isPhoneValid(pair.getValue())) {
192  continue;
193  }
194 
195  // Apply namespace or direction
196  if (xryNamespace == XryNamespace.FROM || direction == CommunicationDirection.INCOMING) {
197  callerId = pair.getValue();
198  } else if (xryNamespace == XryNamespace.TO || direction == CommunicationDirection.OUTGOING) {
199  calleeList.add(pair.getValue());
200  } else {
201  otherAttributes.add(new BlackboardAttribute(
202  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
203  PARSER_NAME, pair.getValue()));
204  }
205  break;
206  // Although confusing, as these are also 'name spaces', it appears
207  // later versions of XRY just made these standardized lines.
208  case TO:
209  if(!XRYUtils.isPhoneValid(pair.getValue())) {
210  continue;
211  }
212 
213  calleeList.add(pair.getValue());
214  break;
215  case FROM:
216  if(!XRYUtils.isPhoneValid(pair.getValue())) {
217  continue;
218  }
219 
220  callerId = pair.getValue();
221  break;
222  case TIME:
223  try {
224  //Tranform value to seconds since epoch
225  long dateTimeSinceEpoch = XRYUtils.calculateSecondsSinceEpoch(pair.getValue());
226  startTime = dateTimeSinceEpoch;
227  } catch (DateTimeParseException ex) {
228  logger.log(Level.WARNING, String.format("[XRY DSP] Assumption"
229  + " about the date time formatting of call logs is "
230  + "not right. Here is the value [ %s ]", pair.getValue()), ex);
231  }
232  break;
233  case DIRECTION:
234  String directionString = pair.getValue().toLowerCase();
235  if (directionString.equals("incoming")) {
236  direction = CommunicationDirection.INCOMING;
237  } else {
238  direction = CommunicationDirection.OUTGOING;
239  }
240  break;
241  case TYPE:
242  String typeString = pair.getValue();
243  if (typeString.equalsIgnoreCase("received")) {
244  direction = CommunicationDirection.INCOMING;
245  } else if (typeString.equalsIgnoreCase("dialed")) {
246  direction = CommunicationDirection.OUTGOING;
247  }
248  break;
249  default:
250  //Otherwise, the XryKey enum contains the correct BlackboardAttribute
251  //type.
252  if (xryKey.getType() != null) {
253  otherAttributes.add(new BlackboardAttribute(xryKey.getType(),
254  PARSER_NAME, pair.getValue()));
255  }
256 
257  logger.log(Level.INFO, String.format("[XRY DSP] Key value pair "
258  + "(in brackets) [ %s ] was recognized but "
259  + "more data or time is needed to finish implementation. Discarding... ",
260  pair));
261  }
262  }
263 
264  // Make sure we have the required fields, otherwise the CommHelper will
265  // complain about illegal arguments.
266 
267  // These are all the invalid combinations.
268  if (callerId == null && calleeList.isEmpty()
269  || direction == CommunicationDirection.INCOMING && callerId == null
270  || direction == CommunicationDirection.OUTGOING && calleeList.isEmpty()) {
271 
272  // If the combo is invalid, just make an artifact with what we've got.
273  if (direction != CommunicationDirection.UNKNOWN) {
274  otherAttributes.add(new BlackboardAttribute(
275  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION,
276  PARSER_NAME, direction.getDisplayName()));
277  }
278 
279  if (startTime > 0L) {
280  otherAttributes.add(new BlackboardAttribute(
281  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START,
282  PARSER_NAME, startTime));
283  }
284 
285  // If the DIRECTION check failed, just manually create accounts
286  // for these phones. Note, there is no need to create relationships.
287  // If both callerId and calleeList were non-null/non-empty, then
288  // it would have been a valid combination.
289  if (callerId != null) {
290  try {
291  currentCase.getCommunicationsManager().createAccountFileInstance(
292  Account.Type.PHONE, callerId, PARSER_NAME, parent);
293  } catch (InvalidAccountIDException ex) {
294  logger.log(Level.WARNING, String.format("Invalid account identifier %s", callerId), ex);
295  }
296 
297  otherAttributes.add(new BlackboardAttribute(
298  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
299  PARSER_NAME, callerId));
300  }
301 
302  for (String phone : calleeList) {
303  try {
304  currentCase.getCommunicationsManager().createAccountFileInstance(
305  Account.Type.PHONE, phone, PARSER_NAME, parent);
306  } catch (InvalidAccountIDException ex) {
307  logger.log(Level.WARNING, String.format("Invalid account identifier %s", phone), ex);
308  }
309 
310 
311  otherAttributes.add(new BlackboardAttribute(
312  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
313  PARSER_NAME, phone));
314  }
315 
316  if (!otherAttributes.isEmpty()) {
317  BlackboardArtifact artifact = parent.newDataArtifact(new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG), otherAttributes);
318 
319  currentCase.getBlackboard().postArtifact(artifact, PARSER_NAME);
320  }
321  } else {
322 
323  // Otherwise we can safely use the helper.
324  CommunicationArtifactsHelper helper = new CommunicationArtifactsHelper(
325  currentCase, PARSER_NAME, parent, Account.Type.PHONE);
326 
327  helper.addCalllog(direction, callerId, calleeList, startTime,
328  endTime, callType, otherAttributes);
329  }
330  }
331 }
XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type)

Copyright © 2012-2021 Basis Technology. Generated on: Thu Sep 30 2021
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.