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

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.