Autopsy  4.13.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 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.Instant;
22 import java.time.LocalDateTime;
23 import java.time.OffsetDateTime;
24 import java.time.ZoneId;
25 import java.time.ZonedDateTime;
26 import java.time.format.DateTimeFormatter;
27 import java.time.format.DateTimeParseException;
28 import java.time.temporal.TemporalAccessor;
29 import java.time.temporal.TemporalQueries;
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.Optional;
33 import java.util.logging.Level;
35 import org.sleuthkit.datamodel.BlackboardArtifact;
36 import org.sleuthkit.datamodel.BlackboardAttribute;
37 import org.sleuthkit.datamodel.Content;
38 import org.sleuthkit.datamodel.TskCoreException;
39 
43 final class XRYCallsFileParser extends AbstractSingleEntityParser {
44 
45  private static final Logger logger = Logger.getLogger(XRYCallsFileParser.class.getName());
46 
47  //Pattern is in reverse due to a Java 8 bug, see calculateSecondsSinceEpoch()
48  //function for more details.
49  private static final DateTimeFormatter DATE_TIME_PARSER
50  = DateTimeFormatter.ofPattern("[(XXX) ][O ][(O) ]a h:m:s M/d/y");
51 
52  private static final String DEVICE_LOCALE = "(device)";
53  private static final String NETWORK_LOCALE = "(network)";
54 
59  private enum XryKey {
60  NAME_MATCHED("name (matched)", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME),
61  TIME("time", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME),
62  DIRECTION("direction", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION),
63  CALL_TYPE("call type", null),
64  NUMBER("number", null),
65  TEL("tel", null),
66  TO("to", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO),
67  FROM("from", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM),
68  DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED),
69  DURATION("duration", null),
70  STORAGE("storage", null),
71  INDEX("index", null),
72  TYPE("type", null),
73  NAME("name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
74 
75  private final String name;
76  private final BlackboardAttribute.ATTRIBUTE_TYPE type;
77 
78  XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type) {
79  this.name = name;
80  this.type = type;
81  }
82 
83  public BlackboardAttribute.ATTRIBUTE_TYPE getType() {
84  return type;
85  }
86 
90  public static boolean contains(String key) {
91  try {
93  return true;
94  } catch (IllegalArgumentException ex) {
95  return false;
96  }
97  }
98 
106  public static XryKey fromDisplayName(String key) {
107  String normalizedKey = key.trim().toLowerCase();
108  for (XryKey keyChoice : XryKey.values()) {
109  if (normalizedKey.equals(keyChoice.name)) {
110  return keyChoice;
111  }
112  }
113 
114  throw new IllegalArgumentException(String.format("Key [%s] was not found."
115  + " All keys should be tested with contains.", key));
116  }
117  }
118 
122  private enum XryNamespace {
123  TO("to"),
124  FROM("from"),
125  NONE(null);
126 
127  private final String name;
128 
129  XryNamespace(String name) {
130  this.name = name;
131  }
132 
137  public static boolean contains(String xryNamespace) {
138  try {
139  XryNamespace.fromDisplayName(xryNamespace);
140  return true;
141  } catch (IllegalArgumentException ex) {
142  return false;
143  }
144  }
145 
154  public static XryNamespace fromDisplayName(String xryNamespace) {
155  String normalizedNamespace = xryNamespace.trim().toLowerCase();
156  for (XryNamespace keyChoice : XryNamespace.values()) {
157  if (normalizedNamespace.equals(keyChoice.name)) {
158  return keyChoice;
159  }
160  }
161 
162  throw new IllegalArgumentException(String.format("Key [%s] was not found."
163  + " All keys should be tested with contains.", xryNamespace));
164  }
165  }
166 
167  @Override
168  boolean canProcess(XRYKeyValuePair pair) {
169  return XryKey.contains(pair.getKey());
170  }
171 
172  @Override
173  boolean isNamespace(String nameSpace) {
174  return XryNamespace.contains(nameSpace);
175  }
176 
177  @Override
178  void makeArtifact(List<XRYKeyValuePair> keyValuePairs, Content parent) throws TskCoreException {
179  List<BlackboardAttribute> attributes = new ArrayList<>();
180  for(XRYKeyValuePair pair : keyValuePairs) {
181  Optional<BlackboardAttribute> attribute = getBlackboardAttribute(pair);
182  if(attribute.isPresent()) {
183  attributes.add(attribute.get());
184  }
185  }
186  if(!attributes.isEmpty()) {
187  BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG);
188  artifact.addAttributes(attributes);
189  }
190  }
191 
196  private Optional<BlackboardAttribute> getBlackboardAttribute(XRYKeyValuePair pair) {
197  XryKey xryKey = XryKey.fromDisplayName(pair.getKey());
198  XryNamespace xryNamespace = XryNamespace.NONE;
199  if (XryNamespace.contains(pair.getNamespace())) {
200  xryNamespace = XryNamespace.fromDisplayName(pair.getNamespace());
201  }
202 
203  switch (xryKey) {
204  case TEL:
205  case NUMBER:
206  //Apply the namespace
207  switch (xryNamespace) {
208  case FROM:
209  return Optional.of(new BlackboardAttribute(
210  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM,
211  PARSER_NAME, pair.getValue()));
212  case TO:
213  return Optional.of(new BlackboardAttribute(
214  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO,
215  PARSER_NAME, pair.getValue()));
216  default:
217  return Optional.of(new BlackboardAttribute(
218  BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
219  PARSER_NAME, pair.getValue()));
220  }
221  case TIME:
222  try {
223  //Tranform value to seconds since epoch
224  long dateTimeSinceEpoch = calculateSecondsSinceEpoch(pair.getValue());
225  return Optional.of(new BlackboardAttribute(xryKey.getType(),
226  PARSER_NAME, 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  return Optional.empty();
232  }
233  default:
234  //Otherwise, the XryKey enum contains the correct BlackboardAttribute
235  //type.
236  if (xryKey.getType() != null) {
237  return Optional.of(new BlackboardAttribute(xryKey.getType(),
238  PARSER_NAME, pair.getValue()));
239  }
240 
241  logger.log(Level.INFO, String.format("[XRY DSP] Key value pair "
242  + "(in brackets) [ %s ] was recognized but "
243  + "more data or time is needed to finish implementation. Discarding... ",
244  pair));
245  return Optional.empty();
246  }
247  }
248 
257  private String removeDateTimeLocale(String dateTime) {
258  String result = dateTime;
259  int deviceIndex = result.toLowerCase().indexOf(DEVICE_LOCALE);
260  if (deviceIndex != -1) {
261  result = result.substring(0, deviceIndex);
262  }
263  int networkIndex = result.toLowerCase().indexOf(NETWORK_LOCALE);
264  if (networkIndex != -1) {
265  result = result.substring(0, networkIndex);
266  }
267  return result;
268  }
269 
276  private long calculateSecondsSinceEpoch(String dateTime) {
277  String dateTimeWithoutLocale = removeDateTimeLocale(dateTime).trim();
294  String reversedDateTime = reverseOrderOfDateTimeComponents(dateTimeWithoutLocale);
302  String reversedDateTimeWithGMT = reversedDateTime.replace("UTC", "GMT");
303  TemporalAccessor result = DATE_TIME_PARSER.parseBest(reversedDateTimeWithGMT,
304  ZonedDateTime::from,
305  LocalDateTime::from,
306  OffsetDateTime::from);
307  //Query for the ZoneID
308  if (result.query(TemporalQueries.zoneId()) == null) {
309  //If none, assumed GMT+0.
310  return ZonedDateTime.of(LocalDateTime.from(result),
311  ZoneId.of("GMT")).toEpochSecond();
312  } else {
313  return Instant.from(result).getEpochSecond();
314  }
315  }
316 
325  private String reverseOrderOfDateTimeComponents(String dateTime) {
326  StringBuilder reversedDateTime = new StringBuilder(dateTime.length());
327  String[] dateTimeComponents = dateTime.split(" ");
328  for (String component : dateTimeComponents) {
329  reversedDateTime.insert(0, " ").insert(0, component);
330  }
331  return reversedDateTime.toString().trim();
332  }
333 }
XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type)

Copyright © 2012-2019 Basis Technology. Generated on: Tue Jan 7 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.