19 package org.sleuthkit.autopsy.datasourceprocessors.xry;
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;
43 final class XRYCallsFileParser
extends AbstractSingleEntityParser {
45 private static final Logger logger = Logger.getLogger(XRYCallsFileParser.class.getName());
49 private static final DateTimeFormatter DATE_TIME_PARSER
50 = DateTimeFormatter.ofPattern(
"[(XXX) ][O ][(O) ]a h:m:s M/d/y");
52 private static final String DEVICE_LOCALE =
"(device)";
53 private static final String NETWORK_LOCALE =
"(network)";
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),
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),
73 NAME(
"name", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
76 private final BlackboardAttribute.ATTRIBUTE_TYPE
type;
78 XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type) {
83 public BlackboardAttribute.ATTRIBUTE_TYPE
getType() {
94 }
catch (IllegalArgumentException ex) {
107 String normalizedKey = key.trim().toLowerCase();
109 if (normalizedKey.equals(keyChoice.name)) {
114 throw new IllegalArgumentException(String.format(
"Key [%s] was not found."
115 +
" All keys should be tested with contains.", key));
137 public static boolean contains(String xryNamespace) {
141 }
catch (IllegalArgumentException ex) {
155 String normalizedNamespace = xryNamespace.trim().toLowerCase();
157 if (normalizedNamespace.equals(keyChoice.name)) {
162 throw new IllegalArgumentException(String.format(
"Key [%s] was not found."
163 +
" All keys should be tested with contains.", xryNamespace));
168 boolean canProcess(XRYKeyValuePair pair) {
169 return XryKey.
contains(pair.getKey());
173 boolean isNamespace(String nameSpace) {
174 return XryNamespace.contains(nameSpace);
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());
186 if(!attributes.isEmpty()) {
187 BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_CALLLOG);
188 artifact.addAttributes(attributes);
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());
207 switch (xryNamespace) {
209 return Optional.of(
new BlackboardAttribute(
210 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM,
211 PARSER_NAME, pair.getValue()));
213 return Optional.of(
new BlackboardAttribute(
214 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO,
215 PARSER_NAME, pair.getValue()));
217 return Optional.of(
new BlackboardAttribute(
218 BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER,
219 PARSER_NAME, pair.getValue()));
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();
236 if (xryKey.getType() != null) {
237 return Optional.of(
new BlackboardAttribute(xryKey.getType(),
238 PARSER_NAME, pair.getValue()));
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... ",
245 return Optional.empty();
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);
263 int networkIndex = result.toLowerCase().indexOf(NETWORK_LOCALE);
264 if (networkIndex != -1) {
265 result = result.substring(0, networkIndex);
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,
306 OffsetDateTime::from);
308 if (result.query(TemporalQueries.zoneId()) == null) {
310 return ZonedDateTime.of(LocalDateTime.from(result),
311 ZoneId.of(
"GMT")).toEpochSecond();
313 return Instant.from(result).getEpochSecond();
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);
331 return reversedDateTime.toString().trim();
BlackboardAttribute.ATTRIBUTE_TYPE getType()
static boolean contains(String xryNamespace)
final BlackboardAttribute.ATTRIBUTE_TYPE type
static XryKey fromDisplayName(String key)
static XryNamespace fromDisplayName(String xryNamespace)
XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type)
XryNamespace(String name)
static boolean contains(String key)