Autopsy 4.22.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-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 */
19package org.sleuthkit.autopsy.datasourceprocessors.xry;
20
21import java.time.format.DateTimeParseException;
22import java.util.ArrayList;
23import java.util.Collection;
24import java.util.List;
25import java.util.logging.Level;
26import org.sleuthkit.autopsy.coreutils.Logger;
27import org.sleuthkit.datamodel.Account;
28import org.sleuthkit.datamodel.Blackboard.BlackboardException;
29import org.sleuthkit.datamodel.BlackboardArtifact;
30import org.sleuthkit.datamodel.BlackboardAttribute;
31import org.sleuthkit.datamodel.Content;
32import org.sleuthkit.datamodel.InvalidAccountIDException;
33import org.sleuthkit.datamodel.SleuthkitCase;
34import org.sleuthkit.datamodel.TskCoreException;
35import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
36import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CallMediaType;
37import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CommunicationDirection;
38
42final 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 {
83 XryKey.fromDisplayName(key);
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
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-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.