Autopsy  4.19.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
LeappFileProcessor.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 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.modules.leappanalyzers;
20 
21 import com.fasterxml.jackson.databind.MappingIterator;
22 import com.fasterxml.jackson.dataformat.csv.CsvMapper;
23 import com.fasterxml.jackson.dataformat.csv.CsvParser;
24 import com.fasterxml.jackson.dataformat.csv.CsvSchema;
25 import com.google.common.collect.ImmutableMap;
26 import java.io.File;
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 import java.io.UncheckedIOException;
30 import java.nio.file.Files;
31 import java.nio.file.Path;
32 import java.text.DateFormat;
33 import java.text.ParseException;
34 import java.text.SimpleDateFormat;
35 import java.util.List;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collection;
39 import java.util.Collections;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import static java.util.Locale.US;
43 import java.util.Map;
44 import java.util.Set;
45 import java.util.logging.Level;
46 import java.util.stream.Collectors;
47 import java.util.stream.IntStream;
48 import java.util.stream.Stream;
49 import javax.xml.parsers.DocumentBuilder;
50 import javax.xml.parsers.DocumentBuilderFactory;
51 import javax.xml.parsers.ParserConfigurationException;
52 import org.apache.commons.collections4.CollectionUtils;
53 import org.apache.commons.collections4.MapUtils;
54 import org.apache.commons.io.FilenameUtils;
55 import org.apache.commons.lang3.StringUtils;
56 import org.openide.util.NbBundle;
66 import org.sleuthkit.datamodel.AbstractFile;
67 import org.sleuthkit.datamodel.Account;
68 import org.sleuthkit.datamodel.Blackboard;
69 import org.sleuthkit.datamodel.Blackboard.BlackboardException;
70 import org.sleuthkit.datamodel.BlackboardArtifact;
71 import org.sleuthkit.datamodel.BlackboardAttribute;
72 import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE;
73 import org.sleuthkit.datamodel.Content;
74 import org.sleuthkit.datamodel.Score;
75 import org.sleuthkit.datamodel.TskCoreException;
76 import org.sleuthkit.datamodel.TskException;
77 import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper;
78 import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CallMediaType;
79 import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.CommunicationDirection;
80 import org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper.MessageReadStatus;
81 import org.sleuthkit.datamodel.blackboardutils.GeoArtifactsHelper;
82 import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints;
83 import org.sleuthkit.datamodel.blackboardutils.attributes.GeoTrackPoints.TrackPoint;
84 import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoints;
85 import org.sleuthkit.datamodel.blackboardutils.attributes.GeoWaypoints.Waypoint;
86 import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments;
87 import org.sleuthkit.datamodel.blackboardutils.attributes.MessageAttachments.FileAttachment;
88 import org.w3c.dom.Document;
89 import org.w3c.dom.NamedNodeMap;
90 import org.w3c.dom.NodeList;
91 import org.xml.sax.SAXException;
92 
96 public final class LeappFileProcessor {
97 
101  private static class TsvColumn {
102 
103  private final BlackboardAttribute.Type attributeType;
104  private final String columnName;
105  private final boolean required;
106 
116  TsvColumn(BlackboardAttribute.Type attributeType, String columnName, boolean required) {
118  this.columnName = columnName;
119  this.required = required;
120  }
121 
125  BlackboardAttribute.Type getAttributeType() {
126  return attributeType;
127  }
128 
132  String getColumnName() {
133  return columnName;
134  }
135 
139  boolean isRequired() {
140  return required;
141  }
142  }
143 
144  private static final Logger logger = Logger.getLogger(LeappFileProcessor.class.getName());
145  private final String xmlFile; //NON-NLS
146  private final String moduleName;
147 
148  private final Map<String, String> tsvFiles;
149  private final Map<String, BlackboardArtifact.Type> tsvFileArtifacts;
150  private final Map<String, String> tsvFileArtifactComments;
151  private final Map<String, List<TsvColumn>> tsvFileAttributes;
152 
153  private static final Map<String, String> CUSTOM_ARTIFACT_MAP = ImmutableMap.<String, String>builder()
154  .put("TSK_IP_DHCP", "DHCP Information")
155  .build();
156 
157  private static final Map<String, String> ACCOUNT_RELATIONSHIPS = ImmutableMap.<String, String>builder()
158  .put("zapya.tsv", "message")
159  .put("sms messages.tsv", "message")
160  .put("mms messages.tsv", "message")
161  .put("viber - messages.tsv", "message")
162  .put("viber - contacts.tsv", "contact")
163  .put("viber - call logs.tsv", "calllog")
164  .put("xender file transfer - messages.tsv", "message")
165  .put("xender file transfer - contacts.tsv", "contact")
166  .put("whatsapp - contacts.tsv", "contact")
167  .put("whatsapp - group call logs.tsv", "calllog")
168  .put("whatsapp - single call logs.tsv", "calllog")
169  .put("whatsapp - messages logs.tsv", "message")
170  .put("shareit file transfer.tsv", "message")
171  .put("tangomessages messages.tsv", "message")
172  .put("contacts.tsv", "contact")
173  .put("imo - accountid.tsv", "contact")
174  .put("imo - messages.tsv", "message")
175  .put("textnow - contacts.tsv", "contact")
176  .put("textnow - messages.tsv", "message")
177  .put("line - messages.tsv", "message")
178  .put("line - contacts.tsv", "contact")
179  .put("line - calllogs.tsv", "calllog")
180  .put("skype - messages logs.tsv", "message")
181  .put("skype - contacts.tsv", "contact")
182  .put("skype - call logs.tsv", "calllog")
183  .put("facebook messenger - chats.tsv", "message")
184  .put("facebook messenger - contacts.tsv", "contact")
185  .put("facebook messenger - calls.tsv", "calllog")
186  .put("call logs2.tsv", "calllog")
187  .put("call logs.tsv", "calllog")
188  .put("oruxmaps tracks.tsv", "trackpoint")
189  .put("google map locations.tsv", "route")
190  .put("Contacts.tsv", "contact")
191  .put("sms - imessage.tsv", "message")
192  .put("call history.tsv", "calllog")
193  .build();
194 
195  Blackboard blkBoard;
196 
197  public LeappFileProcessor(String xmlFile, String moduleName) throws IOException, IngestModuleException, NoCurrentCaseException {
198  this.tsvFiles = new HashMap<>();
199  this.tsvFileArtifacts = new HashMap<>();
200  this.tsvFileArtifactComments = new HashMap<>();
201  this.tsvFileAttributes = new HashMap<>();
202  this.xmlFile = xmlFile;
203  this.moduleName = moduleName;
204 
205  blkBoard = Case.getCurrentCaseThrows().getSleuthkitCase().getBlackboard();
206 
207  createCustomArtifacts(blkBoard);
208  configExtractor();
209  loadConfigFile();
210 
211  }
212 
213  @NbBundle.Messages({
214  "LeappFileProcessor.error.running.Leapp=Error running Leapp, see log file.",
215  "LeappFileProcessor.error.creating.output.dir=Error creating Leapp module output directory.",
216  "LeappFileProcessor.starting.Leapp=Starting Leapp",
217  "LeappFileProcessor.running.Leapp=Running Leapp",
218  "LeappFileProcessor.has.run=Leapp",
219  "LeappFileProcessor.Leapp.cancelled=Leapp run was canceled",
220  "LeappFileProcessor.completed=Leapp Processing Completed",
221  "LeappFileProcessor.error.reading.Leapp.directory=Error reading Leapp Output Directory"})
222  public ProcessResult processFiles(Content dataSource, Path moduleOutputPath, AbstractFile LeappFile) {
223  try {
224  List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
225  processLeappFiles(LeappTsvOutputFiles, LeappFile);
226  } catch (IOException | IngestModuleException ex) {
227  logger.log(Level.SEVERE, String.format("Error trying to process Leapp output files in directory %s. ", moduleOutputPath.toString()), ex); //NON-NLS
228  return ProcessResult.ERROR;
229  }
230 
231  return ProcessResult.OK;
232  }
233 
234  public ProcessResult processFileSystem(Content dataSource, Path moduleOutputPath) {
235 
236  try {
237  List<String> LeappTsvOutputFiles = findTsvFiles(moduleOutputPath);
238  processLeappFiles(LeappTsvOutputFiles, dataSource);
239  } catch (IngestModuleException ex) {
240  logger.log(Level.SEVERE, String.format("Error trying to process Leapp output files in directory %s. ", moduleOutputPath.toString()), ex); //NON-NLS
241  return ProcessResult.ERROR;
242  }
243 
244  return ProcessResult.OK;
245  }
246 
251  private List<String> findTsvFiles(Path LeappOutputDir) throws IngestModuleException {
252  List<String> allTsvFiles = new ArrayList<>();
253  List<String> foundTsvFiles = new ArrayList<>();
254 
255  try (Stream<Path> walk = Files.walk(LeappOutputDir)) {
256 
257  allTsvFiles = walk.map(x -> x.toString())
258  .filter(f -> f.toLowerCase().endsWith(".tsv")).collect(Collectors.toList());
259 
260  for (String tsvFile : allTsvFiles) {
261  if (tsvFiles.containsKey(FilenameUtils.getName(tsvFile.toLowerCase()))) {
262  foundTsvFiles.add(tsvFile);
263  }
264  }
265 
266  } catch (IOException | UncheckedIOException e) {
267  throw new IngestModuleException(Bundle.LeappFileProcessor_error_reading_Leapp_directory() + LeappOutputDir.toString(), e);
268  }
269 
270  return foundTsvFiles;
271 
272  }
273 
283  private void processLeappFiles(List<String> LeappFilesToProcess, AbstractFile LeappImageFile) throws FileNotFoundException, IOException, IngestModuleException {
284  List<BlackboardArtifact> bbartifacts = new ArrayList<>();
285 
286  for (String LeappFileName : LeappFilesToProcess) {
287  String fileName = FilenameUtils.getName(LeappFileName);
288  File LeappFile = new File(LeappFileName);
289  if (tsvFileAttributes.containsKey(fileName)) {
290  BlackboardArtifact.Type artifactType = null;
291  try {
292  List<TsvColumn> attrList = tsvFileAttributes.get(fileName);
293  artifactType = tsvFileArtifacts.get(fileName);
294  processFile(LeappFile, attrList, fileName, artifactType, bbartifacts, LeappImageFile);
295  } catch (TskCoreException ex) {
296  throw new IngestModuleException(String.format("Error getting Blackboard Artifact Type for %s", artifactType == null ? "<null>" : artifactType.toString()), ex);
297  }
298  }
299  }
300 
301  if (!bbartifacts.isEmpty()) {
302  postArtifacts(bbartifacts);
303  }
304 
305  }
306 
316  private void processLeappFiles(List<String> LeappFilesToProcess, Content dataSource) throws IngestModuleException {
317  List<BlackboardArtifact> bbartifacts = new ArrayList<>();
318 
319  for (String LeappFileName : LeappFilesToProcess) {
320  String fileName = FilenameUtils.getName(LeappFileName);
321  File LeappFile = new File(LeappFileName);
322  if (tsvFileAttributes.containsKey(fileName)) {
323  List<TsvColumn> attrList = tsvFileAttributes.get(fileName);
324  BlackboardArtifact.Type artifactType = tsvFileArtifacts.get(fileName);
325 
326  try {
327  processFile(LeappFile, attrList, fileName, artifactType, bbartifacts, dataSource);
328  } catch (TskCoreException | IOException ex) {
329  logger.log(Level.SEVERE, String.format("Error processing file at %s", LeappFile.toString()), ex);
330  }
331  }
332 
333  }
334 
335  if (!bbartifacts.isEmpty()) {
336  postArtifacts(bbartifacts);
337  }
338 
339  }
340 
341  private void processFile(File LeappFile, List<TsvColumn> attrList, String fileName, BlackboardArtifact.Type artifactType,
342  List<BlackboardArtifact> bbartifacts, Content dataSource) throws FileNotFoundException, IOException, IngestModuleException,
343  TskCoreException {
344 
345  String trackpointSegmentName = null;
346  GeoTrackPoints pointList = new GeoTrackPoints();
347  AbstractFile geoAbstractFile = null;
348 
349  if (LeappFile == null || !LeappFile.exists() || fileName == null) {
350  logger.log(Level.WARNING, String.format("Leap file: %s is null or does not exist", LeappFile == null ? LeappFile.toString() : "<null>"));
351  return;
352  } else if (attrList == null || artifactType == null || dataSource == null) {
353  logger.log(Level.WARNING, String.format("attribute list, artifact type or dataSource not provided for %s", LeappFile == null ? LeappFile.toString() : "<null>"));
354  return;
355  }
356 
357  // based on https://stackoverflow.com/questions/56921465/jackson-csv-schema-for-array
358  try (MappingIterator<List<String>> iterator = new CsvMapper()
359  .enable(CsvParser.Feature.WRAP_AS_ARRAY)
360  .readerFor(List.class)
361  .with(CsvSchema.emptySchema().withColumnSeparator('\t'))
362  .readValues(LeappFile)) {
363 
364  if (iterator.hasNext()) {
365  List<String> headerItems = iterator.next();
366  Map<String, Integer> columnIndexes = IntStream.range(0, headerItems.size())
367  .mapToObj(idx -> idx)
368  .collect(Collectors.toMap(
369  idx -> headerItems.get(idx) == null ? null : headerItems.get(idx).trim().toLowerCase(),
370  idx -> idx,
371  (val1, val2) -> val1));
372 
373  int lineNum = 2;
374  while (iterator.hasNext()) {
375  List<String> columnItems = iterator.next();
376  Collection<BlackboardAttribute> bbattributes = processReadLine(columnItems, columnIndexes, attrList, fileName, lineNum);
377 
378  if (!bbattributes.isEmpty()) {
379  switch (ACCOUNT_RELATIONSHIPS.getOrDefault(fileName.toLowerCase(), "norelationship").toLowerCase()) {
380  case "message":
381  createMessageRelationship(bbattributes, dataSource, fileName);
382  break;
383  case "contact":
384  createContactRelationship(bbattributes, dataSource, fileName);
385  break;
386  case "calllog":
387  createCalllogRelationship(bbattributes, dataSource, fileName);
388  break;
389  case "route":
390  createRoute(bbattributes, dataSource, fileName);
391  break;
392  case "trackpoint":
393  geoAbstractFile = createTrackpoint(bbattributes, dataSource, fileName, trackpointSegmentName, pointList);
394  break;
395  default: // There is no relationship defined so just process the artifact normally
396  BlackboardArtifact bbartifact = createArtifactWithAttributes(artifactType, dataSource, bbattributes);
397  if (bbartifact != null) {
398  bbartifacts.add(bbartifact);
399  }
400  break;
401  }
402  }
403 
404  lineNum++;
405  }
406  }
407  }
408 
409  try {
410  if (ACCOUNT_RELATIONSHIPS.getOrDefault(fileName.toLowerCase(), "norelationship").toLowerCase() == "trackpoint") {
411  (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, "", geoAbstractFile)).addTrack(trackpointSegmentName, pointList, new ArrayList<>());
412 
413  }
414  } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) {
415  throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_message_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS
416  }
417 
418  }
419 
420  @NbBundle.Messages({
421  "LeappFileProcessor.cannot.create.waypoint.relationship=Cannot create TSK_WAYPOINT artifact.",
422  })
423 
424  private void createRoute (Collection<BlackboardAttribute> bbattributes, Content dataSource, String fileName) throws IngestModuleException {
425 
426  Double startLatitude = Double.valueOf(0);
427  Double startLongitude = Double.valueOf(0);
428  Double endLatitude = Double.valueOf(0);
429  Double endLongitude = Double.valueOf(0);
430  Double zeroValue = Double.valueOf(0);
431  String destinationName = "";
432  String locationName = "";
433  Long dateTime = Long.valueOf(0);
434  Collection<BlackboardAttribute> otherAttributes = new ArrayList<>();
435  String sourceFile = null;
436  AbstractFile absFile = null;
437  String comment = "";
438 
439  try {
440  for (BlackboardAttribute bba : bbattributes) {
441  switch (bba.getAttributeType().getTypeName()) {
442  case "TSK_GEO_LATITUDE_START":
443  startLatitude = bba.getValueDouble();
444  break;
445  case "TSK_GEO_LONGITUDE_START":
446  startLongitude = bba.getValueDouble();
447  break;
448  case "TSK_GEO_LATITUDE_END":
449  startLatitude = bba.getValueDouble();
450  break;
451  case "TSK_GEO_LONGITUDE_END":
452  startLongitude = bba.getValueDouble();
453  break;
454  case "TSK_DATETIME":
455  dateTime = bba.getValueLong();
456  break;
457  case "TSK_NAME":
458  destinationName = bba.getValueString();
459  break;
460  case "TSK_LOCATION":
461  locationName = bba.getValueString();
462  break;
463  case "TSK_TEXT_FILE":
464  sourceFile = bba.getValueString();
465  break;
466  case "TSK_COMMENT":
467  comment = bba.getValueString();
468  break;
469  default:
470  otherAttributes.add(bba);
471  break;
472  }
473  }
474  absFile = findAbstractFile(dataSource, sourceFile);
475  if (absFile == null) {
476  absFile = (AbstractFile) dataSource;
477  }
478  GeoWaypoints waypointList = new GeoWaypoints();
479  waypointList.addPoint(new Waypoint(startLatitude, startLongitude, zeroValue, ""));
480  waypointList.addPoint(new Waypoint(endLatitude, endLongitude, zeroValue, locationName));
481  (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, comment, absFile)).addRoute(destinationName, dateTime, waypointList, new ArrayList<>());
482 
483  } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) {
484  throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_waypoint_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS
485  }
486 
487 
488  }
489 
490  @NbBundle.Messages({
491  "LeappFileProcessor.cannot.create.trackpoint.relationship=Cannot create TSK_TRACK_POINT artifact.",
492  })
493 
494  private AbstractFile createTrackpoint(Collection<BlackboardAttribute> bbattributes, Content dataSource, String fileName, String trackpointSegmentName, GeoTrackPoints pointList) throws IngestModuleException {
495 
496  Double latitude = Double.valueOf(0);
497  Double longitude = Double.valueOf(0);
498  Double altitude = Double.valueOf(0);
499  Double zeroValue = Double.valueOf(0);
500  String segmentName = null;
501  Long dateTime = Long.valueOf(0);
502  Collection<BlackboardAttribute> otherAttributes = new ArrayList<>();
503  String sourceFile = null;
504  String comment = null;
505  AbstractFile absFile = null;
506 
507  try {
508  for (BlackboardAttribute bba : bbattributes) {
509  switch (bba.getAttributeType().getTypeName()) {
510  case "TSK_GEO_LATITUDE":
511  latitude = bba.getValueDouble();
512  break;
513  case "TSK_GEO_LONGITUDE":
514  longitude = bba.getValueDouble();
515  break;
516  case "TSK_GEO_ALTITUDE":
517  altitude = bba.getValueDouble();
518  break;
519  case "TSK_DATETIME":
520  dateTime = bba.getValueLong();
521  break;
522  case "TSK_NAME":
523  segmentName = bba.getValueString();
524  break;
525  case "TSK_TEXT_FILE":
526  sourceFile = bba.getValueString();
527  break;
528  case "TSK_COMMENT":
529  comment = bba.getValueString();
530  otherAttributes.add(bba);
531  break;
532  default:
533  otherAttributes.add(bba);
534  break;
535  }
536  }
537  absFile = findAbstractFile(dataSource, sourceFile);
538  if (absFile == null) {
539  absFile = (AbstractFile) dataSource;
540  }
541  if ((trackpointSegmentName == null) || (trackpointSegmentName == segmentName)) {
542  trackpointSegmentName = segmentName;
543  pointList.addPoint(new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime));
544  } else {
545  (new GeoArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(), moduleName, comment, absFile)).addTrack(segmentName, pointList, new ArrayList<>());
546  trackpointSegmentName = segmentName;
547  pointList = new GeoTrackPoints();
548  pointList.addPoint(new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime));
549 
550  }
551  } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) {
552  throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_trackpoint_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS
553  }
554 
555  return absFile;
556 
557  }
558 
559 
560  @NbBundle.Messages({
561  "LeappFileProcessor.cannot.create.message.relationship=Cannot create TSK_MESSAGE Relationship.",
562  })
563 
564  private void createMessageRelationship(Collection<BlackboardAttribute> bbattributes, Content dataSource, String fileName) throws IngestModuleException {
565 
566  String messageType = null;
567  String alternateId = null;
568  CommunicationDirection communicationDirection = CommunicationDirection.UNKNOWN;
569  String senderId = null;
570  String receipentId = null;
571  String[] receipentIdList = null;
572  Long dateTime = Long.valueOf(0);
573  MessageReadStatus messageStatus = MessageReadStatus.UNKNOWN;
574  String subject = null;
575  String messageText = null;
576  String threadId = null;
577  List<BlackboardAttribute> otherAttributes = new ArrayList<>();
578  List<FileAttachment> fileAttachments = new ArrayList<>();
579  String sourceFile = null;
580  MessageAttachments messageAttachments = null;
581 
582  try {
583  for (BlackboardAttribute bba : bbattributes) {
584  switch (bba.getAttributeType().getTypeName()) {
585  case "TSK_DIRECTION":
586  if (bba.getValueString().toLowerCase().equals("outgoing")) {
587  communicationDirection = CommunicationDirection.OUTGOING;
588  } else if (bba.getValueString().toLowerCase().equals("incoming")) {
589  communicationDirection = CommunicationDirection.INCOMING;
590  }
591  break;
592  case "TSK_PHONE_NUMBER_FROM":
593  if (!bba.getValueString().isEmpty()) {
594  senderId = bba.getValueString();
595  }
596  break;
597  case "TSK_PHONE_NUMBER_TO":
598  if (!bba.getValueString().isEmpty()) {
599  receipentIdList = bba.getValueString().split(",", 0);
600  }
601  break;
602  case "TSK_DATETIME":
603  dateTime = bba.getValueLong();
604  break;
605  case "TSK_COMMENT":
606  messageType = bba.getValueString();
607  break;
608  case "TSK_ATTACHMENTS":
609  if (!bba.getValueString().isEmpty()) {
610  fileAttachments.add(new FileAttachment(Case.getCurrentCaseThrows().getSleuthkitCase(), dataSource, bba.getValueString()));
611  }
612  break;
613  case "TSK_TEXT_FILE":
614  sourceFile = bba.getValueString();
615  break;
616  case "TSK_READ_STATUS":
617  if (bba.getValueInt() == 1 ) {
618  messageStatus = MessageReadStatus.READ;
619  } else {
620  messageStatus = MessageReadStatus.UNREAD;
621  }
622  break;
623  case "TSK_TEXT":
624  messageText = bba.getValueString();
625  break;
626  case "TSK_SUBJECT":
627  subject = bba.getValueString();
628  break;
629  case "TSK_ID":
630  alternateId = bba.getValueString();
631  otherAttributes.add(bba);
632  break;
633  default:
634  otherAttributes.add(bba);
635  break;
636  }
637  }
638  AbstractFile absFile = findAbstractFile(dataSource, sourceFile);
639  if (absFile == null) {
640  absFile = (AbstractFile) dataSource;
641  }
642  CommunicationArtifactsHelper accountArtifact;
643  Account.Type accountType = getAccountType(fileName);
644  if (alternateId == null) {
645  accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(),
646  moduleName, absFile, accountType);
647  } else {
648  accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(),
649  moduleName, absFile, accountType, accountType, alternateId);
650  }
651  BlackboardArtifact messageArtifact = accountArtifact.addMessage(messageType, communicationDirection, senderId,
652  receipentId, dateTime, messageStatus, subject,
653  messageText, threadId, otherAttributes);
654  if (!fileAttachments.isEmpty()) {
655  messageAttachments = new MessageAttachments(fileAttachments, new ArrayList<>());
656  accountArtifact.addAttachments(messageArtifact, messageAttachments);
657  }
658  } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) {
659  throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_message_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS
660  }
661 
662  }
663 
664  @NbBundle.Messages({
665  "LeappFileProcessor.cannot.create.contact.relationship=Cannot create TSK_CONTACT Relationship.",
666  })
667  private void createContactRelationship(Collection<BlackboardAttribute> bbattributes, Content dataSource, String fileName) throws IngestModuleException {
668 
669  String alternateId = null;
670  String contactName = null;
671  String phoneNumber = null;
672  String homePhoneNumber = null;
673  String mobilePhoneNumber = null;
674  String emailAddr = null;
675  List<BlackboardAttribute> otherAttributes = new ArrayList<>();
676  String sourceFile = null;
677 
678  try {
679  for (BlackboardAttribute bba : bbattributes) {
680  switch (bba.getAttributeType().getTypeName()) {
681  case "TSK_PHONE_NUMBER":
682  if (!bba.getValueString().isEmpty()) {
683  phoneNumber = bba.getValueString();
684  }
685  break;
686  case "TSK_NAME":
687  if (!bba.getValueString().isEmpty()) {
688  contactName = bba.getValueString();
689  }
690  break;
691  case "TSK_TEXT_FILE":
692  sourceFile = bba.getValueString();
693  break;
694  case "TSK_PHONE_NUMBER_HOME":
695  homePhoneNumber = bba.getValueString();
696  break;
697  case "TSK_PHONE_NUMBER_MOBILE":
698  mobilePhoneNumber = bba.getValueString();
699  break;
700  case "TSK_EMAIL":
701  emailAddr = bba.getValueString();
702  break;
703  case "TSK_ID":
704  alternateId = bba.getValueString();
705  otherAttributes.add(bba);
706  break;
707  default:
708  otherAttributes.add(bba);
709  break;
710  }
711  }
712  AbstractFile absFile = findAbstractFile(dataSource, sourceFile);
713  if (absFile == null) {
714  absFile = (AbstractFile) dataSource;
715  }
716  Account.Type accountType = getAccountType(fileName);
717  if (accountType != null) {
718 
719  CommunicationArtifactsHelper accountArtifact;
720  if (alternateId == null) {
721  accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(),
722  moduleName, absFile, accountType);
723  } else {
724  accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(),
725  moduleName, absFile, accountType, accountType, alternateId);
726  }
727  BlackboardArtifact messageArtifact = accountArtifact.addContact(contactName, phoneNumber, homePhoneNumber, mobilePhoneNumber, emailAddr, otherAttributes);
728  }
729  } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) {
730  throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_contact_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS
731  }
732  }
733 
734  @NbBundle.Messages({
735  "LeappFileProcessor.cannot.create.calllog.relationship=Cannot create TSK_CALLLOG Relationship.",
736  })
737 
738  private void createCalllogRelationship(Collection<BlackboardAttribute> bbattributes, Content dataSource, String fileName) throws IngestModuleException {
739 
740  String callerId = null;
741  String alternateId = null;
742  List<String> calleeId = Arrays.asList();
743  CommunicationDirection communicationDirection = CommunicationDirection.UNKNOWN;
744  Long startDateTime = Long.valueOf(0);
745  Long endDateTime = Long.valueOf(0);
746  CallMediaType mediaType = CallMediaType.UNKNOWN;
747  List<BlackboardAttribute> otherAttributes = new ArrayList<>();
748  String sourceFile = null;
749 
750  try {
751  for (BlackboardAttribute bba : bbattributes) {
752  switch (bba.getAttributeType().getTypeName()) {
753  case "TSK_TEXT_FILE":
754  sourceFile = bba.getValueString();
755  break;
756  case "TSK_DATETIME_START":
757  startDateTime = bba.getValueLong();
758  break;
759  case "TSK_DATETIME_END":
760  startDateTime = bba.getValueLong();
761  break;
762  case "TSK_DIRECTION":
763  if (bba.getValueString().toLowerCase().equals("outgoing")) {
764  communicationDirection = CommunicationDirection.OUTGOING;
765  } else if (bba.getValueString().toLowerCase().equals("incoming")) {
766  communicationDirection = CommunicationDirection.INCOMING;
767  }
768  break;
769  case "TSK_PHONE_NUMBER_FROM":
770  if (!bba.getValueString().isEmpty()) {
771  callerId = bba.getValueString();
772  }
773  break;
774  case "TSK_PHONE_NUMBER_TO":
775  if (!bba.getValueString().isEmpty()) {
776  String [] calleeTempList = bba.getValueString().split(",", 0);
777  calleeId = Arrays.asList(calleeTempList);
778  }
779  break;
780  case "TSK_ID":
781  alternateId = bba.getValueString();
782  otherAttributes.add(bba);
783  break;
784  default:
785  otherAttributes.add(bba);
786  break;
787  }
788  }
789 
790  if (calleeId.isEmpty() && communicationDirection == CommunicationDirection.OUTGOING) {
791  String [] calleeTempList = callerId.split(",", 0);
792  calleeId = Arrays.asList(calleeTempList);
793  callerId = null;
794  }
795  AbstractFile absFile = findAbstractFile(dataSource, sourceFile);
796  if (absFile == null) {
797  absFile = (AbstractFile) dataSource;
798  }
799  Account.Type accountType = getAccountType(fileName);
800  CommunicationArtifactsHelper accountArtifact;
801  if (accountType != null) {
802  accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(),
803  moduleName, absFile, accountType);
804  } else {
805  accountArtifact = new CommunicationArtifactsHelper(Case.getCurrentCaseThrows().getSleuthkitCase(),
806  moduleName, absFile, accountType, accountType, alternateId);
807  }
808  BlackboardArtifact callLogArtifact = accountArtifact.addCalllog(communicationDirection, callerId, calleeId, startDateTime, endDateTime, mediaType, otherAttributes);
809  } catch (NoCurrentCaseException | TskCoreException | BlackboardException ex) {
810  throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_calllog_relationship() + ex.getLocalizedMessage(), ex); //NON-NLS
811  }
812 
813  }
814 
815  private Account.Type getAccountType(String AccountTypeName) {
816  switch (AccountTypeName.toLowerCase()) {
817  case "zapya.tsv":
818  return Account.Type.ZAPYA;
819  case "sms messages.tsv":
820  return Account.Type.PHONE;
821  case "contacts.tsv":
822  return Account.Type.PHONE;
823  case "imo - accountid.tsv":
824  return Account.Type.IMO;
825  case "imo - messages.tsv":
826  return Account.Type.IMO;
827  case "textnow - contacts.tsv":
828  return Account.Type.TEXTNOW;
829  case "textnow - messages.tsv":
830  return Account.Type.TEXTNOW;
831  case "mms messages.tsv":
832  return Account.Type.PHONE;
833  case "viber - call logs.tsv":
834  return Account.Type.VIBER;
835  case "viber - contacts.tsv":
836  return Account.Type.VIBER;
837  case "viber - messages.tsv":
838  return Account.Type.VIBER;
839  case "xender file transfer - messages.tsv":
840  return Account.Type.XENDER;
841  case "xender file transfer - contacts.tsv":
842  return Account.Type.XENDER;
843  case "whatsapp - single call logs.tsv":
844  return Account.Type.WHATSAPP;
845  case "whatsapp - messages logs.tsv":
846  return Account.Type.WHATSAPP;
847  case "whatsapp - group call logs.tsv":
848  return Account.Type.WHATSAPP;
849  case "whatsapp - contacts.tsv":
850  return Account.Type.WHATSAPP;
851  case "tangomessages messages.tsv":
852  return Account.Type.TANGO;
853  case "shareit file transfer.tsv":
854  return Account.Type.SHAREIT;
855  case "line - calllogs.tsv":
856  return Account.Type.LINE;
857  case "line - contacts.tsv":
858  return Account.Type.LINE;
859  case "line - messages.tsv":
860  return Account.Type.LINE;
861  case "skype - call logs.tsv":
862  return Account.Type.SKYPE;
863  case "skype - contacts.tsv":
864  return Account.Type.SKYPE;
865  case "skype - messages logs.tsv":
866  return Account.Type.SKYPE;
867  case "facebook messenger - calls.tsv":
868  return Account.Type.FACEBOOK;
869  case "facebook messenger - contacts.tsv":
870  return Account.Type.FACEBOOK;
871  case "facebook messenger - chats.tsv":
872  return Account.Type.FACEBOOK;
873  case "call logs2.tsv":
874  return Account.Type.PHONE;
875  case "call logs.tsv":
876  return Account.Type.PHONE;
877  case "sms - imessage.tsv":
878  return Account.Type.PHONE;
879  default:
880  return Account.Type.PHONE;
881  }
882  }
883 
899  private Collection<BlackboardAttribute> processReadLine(List<String> lineValues, Map<String, Integer> columnIndexes,
900  List<TsvColumn> attrList, String fileName, int lineNum) throws IngestModuleException {
901 
902  if (MapUtils.isEmpty(columnIndexes) || CollectionUtils.isEmpty(lineValues)
903  || (lineValues.size() == 1 && StringUtils.isEmpty(lineValues.get(0)))) {
904  return Collections.emptyList();
905  } else if (lineValues.size() != columnIndexes.size()) {
906  logger.log(Level.WARNING, String.format(
907  "Row at line number %d in file %s has %d columns when %d were expected based on the header row.",
908  lineNum, fileName, lineValues.size(), columnIndexes.size()));
909  return Collections.emptyList();
910  }
911 
912  List<BlackboardAttribute> attrsToRet = new ArrayList<>();
913  for (TsvColumn colAttr : attrList) {
914  if (colAttr.getAttributeType() == null) {
915  // this handles columns that are currently ignored.
916  continue;
917  }
918 
919  Integer columnIdx = columnIndexes.get(colAttr.getColumnName());
920  if (columnIdx == null) {
921  logger.log(Level.WARNING, String.format("No column mapping found for %s in file %s. Omitting column.", colAttr.getColumnName(), fileName));
922  continue;
923  }
924 
925  String value = (columnIdx >= lineValues.size() || columnIdx < 0) ? null : lineValues.get(columnIdx);
926  if (value == null) {
927  logger.log(Level.WARNING, String.format("No value found for column %s at line %d in file %s. Omitting row.", colAttr.getColumnName(), lineNum, fileName));
928  return Collections.emptyList();
929  }
930 
931  String formattedValue = formatValueBasedOnAttrType(colAttr, value);
932 
933  BlackboardAttribute attr = (value == null) ? null : getAttribute(colAttr.getAttributeType(), formattedValue, fileName);
934  if (attr == null) {
935  logger.log(Level.WARNING, String.format("Blackboard attribute could not be parsed column %s at line %d in file %s. Omitting row.", colAttr.getColumnName(), lineNum, fileName));
936  return Collections.emptyList();
937  }
938  attrsToRet.add(attr);
939  }
940 
941  if (tsvFileArtifactComments.containsKey(fileName)) {
942  attrsToRet.add(new BlackboardAttribute(ATTRIBUTE_TYPE.TSK_COMMENT, moduleName, tsvFileArtifactComments.get(fileName)));
943  }
944 
945  return attrsToRet;
946  }
947 
956  private String formatValueBasedOnAttrType(TsvColumn colAttr, String value) {
957  if (colAttr.getAttributeType().getTypeName().equals("TSK_DOMAIN")) {
958  return NetworkUtils.extractDomain(value);
959  }
960 
961  return value;
962  }
963 
967  private static final DateFormat TIMESTAMP_FORMAT = new SimpleDateFormat("yyyy-MM-d HH:mm:ss", US);
968 
979  private BlackboardAttribute getAttribute(BlackboardAttribute.Type attrType, String value, String fileName) {
980  if (attrType == null || value == null) {
981  logger.log(Level.WARNING, String.format("Unable to parse attribute type %s for value '%s' in fileName %s",
982  attrType == null ? "<null>" : attrType.toString(),
983  value == null ? "<null>" : value,
984  fileName == null ? "<null>" : fileName));
985  return null;
986  }
987 
988  switch (attrType.getValueType()) {
989  case JSON:
990  case STRING:
991  return parseAttrValue(value, attrType, fileName, false, false,
992  (v) -> new BlackboardAttribute(attrType, moduleName, v));
993  case INTEGER:
994  return parseAttrValue(value.trim(), attrType, fileName, true, false,
995  (v) -> new BlackboardAttribute(attrType, moduleName, Double.valueOf(v).intValue()));
996  case LONG:
997  return parseAttrValue(value.trim(), attrType, fileName, true, false,
998  (v) -> new BlackboardAttribute(attrType, moduleName, Double.valueOf(v).longValue()));
999  case DOUBLE:
1000  return parseAttrValue(value.trim(), attrType, fileName, true, false,
1001  (v) -> new BlackboardAttribute(attrType, moduleName, (double) Double.valueOf(v)));
1002  case BYTE:
1003  return parseAttrValue(value.trim(), attrType, fileName, true, false,
1004  (v) -> new BlackboardAttribute(attrType, moduleName, new byte[]{Byte.valueOf(v)}));
1005  case DATETIME:
1006  return parseAttrValue(value.trim(), attrType, fileName, true, true,
1007  (v) -> new BlackboardAttribute(attrType, moduleName, TIMESTAMP_FORMAT.parse(v).getTime() / 1000));
1008  default:
1009  // Log this and continue on with processing
1010  logger.log(Level.WARNING, String.format("Attribute Type %s for file %s not defined.", attrType, fileName)); //NON-NLS
1011  return null;
1012 
1013  }
1014  }
1015 
1019  private interface ParseExceptionFunction {
1020 
1029  BlackboardAttribute apply(String orig) throws ParseException, NumberFormatException;
1030  }
1031 
1045  private BlackboardAttribute parseAttrValue(String value, BlackboardAttribute.Type attrType, String fileName, boolean blankIsNull, boolean zeroIsNull, ParseExceptionFunction valueConverter) {
1046  // remove non-printable characters from tsv input
1047  // https://stackoverflow.com/a/6199346
1048  value = value.replaceAll("\\p{C}", "");
1049 
1050  if (blankIsNull && StringUtils.isBlank(value)) {
1051  return null;
1052  }
1053 
1054  if (zeroIsNull && value.matches("^\\s*[0\\.]*\\s*$")) {
1055  return null;
1056  }
1057 
1058  try {
1059  return valueConverter.apply(value);
1060  } catch (NumberFormatException | ParseException ex) {
1061  logger.log(Level.WARNING, String.format("Unable to format '%s' as value type %s while converting to attributes from %s.", value, attrType.getValueType().getLabel(), fileName), ex);
1062  return null;
1063  }
1064  }
1065 
1066  @NbBundle.Messages({
1067  "LeappFileProcessor.cannot.load.artifact.xml=Cannot load xml artifact file.",
1068  "LeappFileProcessor.cannotBuildXmlParser=Cannot buld an XML parser.",
1069  "LeappFileProcessor_cannotParseXml=Cannot Parse XML file.",
1070  "LeappFileProcessor.postartifacts_error=Error posting Blackboard Artifact",
1071  "LeappFileProcessor.error.creating.new.artifacts=Error creating new artifacts."
1072  })
1073 
1077  private void loadConfigFile() throws IngestModuleException {
1078  Document xmlinput;
1079  try {
1080  String path = PlatformUtil.getUserConfigDirectory() + File.separator + xmlFile;
1081  File f = new File(path);
1082  DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
1083  DocumentBuilder db = dbf.newDocumentBuilder();
1084  xmlinput = db.parse(f);
1085 
1086  } catch (IOException e) {
1087  throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_load_artifact_xml() + e.getLocalizedMessage(), e); //NON-NLS
1088  } catch (ParserConfigurationException pce) {
1089  throw new IngestModuleException(Bundle.LeappFileProcessor_cannotBuildXmlParser() + pce.getLocalizedMessage(), pce); //NON-NLS
1090  } catch (SAXException sxe) {
1091  throw new IngestModuleException(Bundle.LeappFileProcessor_cannotParseXml() + sxe.getLocalizedMessage(), sxe); //NON-NLS
1092  }
1093 
1094  getFileNode(xmlinput);
1095  getArtifactNode(xmlinput);
1096  getAttributeNodes(xmlinput);
1097 
1098  }
1099 
1100  private void getFileNode(Document xmlinput) {
1101 
1102  NodeList nlist = xmlinput.getElementsByTagName("FileName"); //NON-NLS
1103 
1104  for (int i = 0; i < nlist.getLength(); i++) {
1105  NamedNodeMap nnm = nlist.item(i).getAttributes();
1106  tsvFiles.put(nnm.getNamedItem("filename").getNodeValue().toLowerCase(), nnm.getNamedItem("description").getNodeValue());
1107 
1108  }
1109 
1110  }
1111 
1112  private void getArtifactNode(Document xmlinput) {
1113 
1114  NodeList artifactNlist = xmlinput.getElementsByTagName("ArtifactName"); //NON-NLS
1115  for (int k = 0; k < artifactNlist.getLength(); k++) {
1116  NamedNodeMap nnm = artifactNlist.item(k).getAttributes();
1117  String artifactName = nnm.getNamedItem("artifactname").getNodeValue();
1118  String comment = nnm.getNamedItem("comment").getNodeValue();
1119  String parentName = artifactNlist.item(k).getParentNode().getAttributes().getNamedItem("filename").getNodeValue();
1120 
1121  BlackboardArtifact.Type foundArtifactType = null;
1122  try {
1123  foundArtifactType = Case.getCurrentCase().getSleuthkitCase().getArtifactType(artifactName);
1124  } catch (TskCoreException ex) {
1125  logger.log(Level.SEVERE, String.format("There was an issue that arose while trying to fetch artifact type for %s.", artifactName), ex);
1126  }
1127 
1128  if (foundArtifactType == null) {
1129  logger.log(Level.SEVERE, String.format("No known artifact mapping found for [artifact: %s, %s]",
1130  artifactName, getXmlFileIdentifier(parentName)));
1131  } else {
1132  tsvFileArtifacts.put(parentName, foundArtifactType);
1133  }
1134 
1135  if (!comment.toLowerCase().matches("null")) {
1136  tsvFileArtifactComments.put(parentName, comment);
1137  }
1138  }
1139 
1140  }
1141 
1142  private String getXmlFileIdentifier(String fileName) {
1143  return String.format("file: %s, filename: %s",
1144  this.xmlFile == null ? "<null>" : this.xmlFile,
1145  fileName == null ? "<null>" : fileName);
1146  }
1147 
1148  private String getXmlAttrIdentifier(String fileName, String attributeName) {
1149  return String.format("attribute: %s %s",
1150  attributeName == null ? "<null>" : attributeName,
1151  getXmlFileIdentifier(fileName));
1152  }
1153 
1154  private void getAttributeNodes(Document xmlinput) {
1155 
1156  NodeList attributeNlist = xmlinput.getElementsByTagName("AttributeName"); //NON-NLS
1157  for (int k = 0; k < attributeNlist.getLength(); k++) {
1158  NamedNodeMap nnm = attributeNlist.item(k).getAttributes();
1159  String attributeName = nnm.getNamedItem("attributename").getNodeValue();
1160 
1161  if (!attributeName.toLowerCase().matches("null")) {
1162  String columnName = nnm.getNamedItem("columnName").getNodeValue();
1163  String required = nnm.getNamedItem("required").getNodeValue();
1164  String parentName = attributeNlist.item(k).getParentNode().getParentNode().getAttributes().getNamedItem("filename").getNodeValue();
1165 
1166  BlackboardAttribute.Type foundAttrType = null;
1167  try {
1168  foundAttrType = Case.getCurrentCase().getSleuthkitCase().getAttributeType(attributeName.toUpperCase());
1169  } catch (TskCoreException ex) {
1170  logger.log(Level.SEVERE, String.format("There was an issue that arose while trying to fetch attribute type for %s.", attributeName), ex);
1171  }
1172 
1173  if (foundAttrType == null) {
1174  logger.log(Level.SEVERE, String.format("No known attribute mapping found for [%s]", getXmlAttrIdentifier(parentName, attributeName)));
1175  }
1176 
1177  if (required != null && required.compareToIgnoreCase("yes") != 0 && required.compareToIgnoreCase("no") != 0) {
1178  logger.log(Level.SEVERE, String.format("Required value %s did not match 'yes' or 'no' for [%s]",
1179  required, getXmlAttrIdentifier(parentName, attributeName)));
1180  }
1181 
1182  if (columnName == null) {
1183  logger.log(Level.SEVERE, String.format("No column name provided for [%s]", getXmlAttrIdentifier(parentName, attributeName)));
1184  } else if (columnName.trim().length() != columnName.length()) {
1185  logger.log(Level.SEVERE, String.format("Column name '%s' starts or ends with whitespace for [%s]", columnName, getXmlAttrIdentifier(parentName, attributeName)));
1186  } else if (columnName.matches("[^ \\S]")) {
1187  logger.log(Level.SEVERE, String.format("Column name '%s' contains invalid characters [%s]", columnName, getXmlAttrIdentifier(parentName, attributeName)));
1188  }
1189 
1190  TsvColumn thisCol = new TsvColumn(
1191  foundAttrType,
1192  columnName.trim().toLowerCase(),
1193  "yes".compareToIgnoreCase(required) == 0);
1194 
1195  if (tsvFileAttributes.containsKey(parentName)) {
1196  List<TsvColumn> attrList = tsvFileAttributes.get(parentName);
1197  attrList.add(thisCol);
1198  tsvFileAttributes.replace(parentName, attrList);
1199  } else {
1200  List<TsvColumn> attrList = new ArrayList<>();
1201  attrList.add(thisCol);
1202  tsvFileAttributes.put(parentName, attrList);
1203  }
1204  }
1205 
1206  }
1207  }
1208 
1220  private BlackboardArtifact createArtifactWithAttributes(BlackboardArtifact.Type artType, Content dataSource, Collection<BlackboardAttribute> bbattributes) {
1221  try {
1222  switch (artType.getCategory()) {
1223  case DATA_ARTIFACT:
1224  return dataSource.newDataArtifact(artType, bbattributes);
1225  case ANALYSIS_RESULT:
1226  return dataSource.newAnalysisResult(artType, Score.SCORE_UNKNOWN, null, null, null, bbattributes).getAnalysisResult();
1227  default:
1228  logger.log(Level.SEVERE, "Unknown category type: " + artType.getCategory().getDisplayName());
1229  return null;
1230  }
1231  } catch (TskException ex) {
1232  logger.log(Level.WARNING, Bundle.LeappFileProcessor_error_creating_new_artifacts(), ex); //NON-NLS
1233  }
1234  return null;
1235  }
1236 
1243  void postArtifacts(Collection<BlackboardArtifact> artifacts) {
1244  if (artifacts == null || artifacts.isEmpty()) {
1245  return;
1246  }
1247 
1248  try {
1249  Case.getCurrentCase().getSleuthkitCase().getBlackboard().postArtifacts(artifacts, moduleName);
1250  } catch (Blackboard.BlackboardException ex) {
1251  logger.log(Level.SEVERE, Bundle.LeappFileProcessor_postartifacts_error(), ex); //NON-NLS
1252  }
1253  }
1254 
1260  private void configExtractor() throws IOException {
1262  xmlFile, true);
1263  }
1264 
1265  private static final Set<String> ALLOWED_EXTENSIONS = new HashSet<>(Arrays.asList("zip", "tar", "tgz"));
1266 
1274  static List<AbstractFile> findLeappFilesToProcess(Content dataSource) {
1275 
1276  List<AbstractFile> leappFiles = new ArrayList<>();
1277 
1278  FileManager fileManager = getCurrentCase().getServices().getFileManager();
1279 
1280  // findFiles use the SQL wildcard % in the file name
1281  try {
1282  leappFiles = fileManager.findFiles(dataSource, "%", "/"); //NON-NLS
1283  } catch (TskCoreException ex) {
1284  logger.log(Level.WARNING, "No files found to process"); //NON-NLS
1285  return leappFiles;
1286  }
1287 
1288  List<AbstractFile> leappFilesToProcess = new ArrayList<>();
1289  for (AbstractFile leappFile : leappFiles) {
1290  if (((leappFile.getLocalAbsPath() != null)
1291  && !leappFile.isVirtual())
1292  && leappFile.getNameExtension() != null
1293  && ALLOWED_EXTENSIONS.contains(leappFile.getNameExtension().toLowerCase())) {
1294  leappFilesToProcess.add(leappFile);
1295  }
1296  }
1297 
1298  return leappFilesToProcess;
1299  }
1300 
1305  private void createCustomArtifacts(Blackboard blkBoard) {
1306 
1307  for (Map.Entry<String, String> customArtifact : CUSTOM_ARTIFACT_MAP.entrySet()) {
1308  String artifactName = customArtifact.getKey();
1309  String artifactDescription = customArtifact.getValue();
1310 
1311  try {
1312  BlackboardArtifact.Type customArtifactType = blkBoard.getOrAddArtifactType(artifactName, artifactDescription);
1313  } catch (Blackboard.BlackboardException ex) {
1314  logger.log(Level.WARNING, String.format("Failed to create custom artifact type %s.", artifactName), ex);
1315  }
1316 
1317  }
1318  }
1319 
1320  private AbstractFile findAbstractFile(Content dataSource, String fileNamePath) {
1321  if (fileNamePath == null) {
1322  return null;
1323  }
1324 
1325  List<AbstractFile> files;
1326 
1327  String fileName = FilenameUtils.getName(fileNamePath);
1328  String filePath = FilenameUtils.normalize(FilenameUtils.getPath(fileNamePath), true);
1329 
1331 
1332  try {
1333  files = fileManager.findFiles(dataSource, fileName); //NON-NLS
1334 
1335  } catch (TskCoreException ex) {
1336  logger.log(Level.WARNING, "Unable to find prefetch files.", ex); //NON-NLS
1337  return null; // No need to continue
1338  }
1339 
1340  for (AbstractFile pFile : files) {
1341 
1342  if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase())) {
1343  return pFile;
1344  }
1345  }
1346 
1347  return null;
1348 
1349  }
1350  }
void createMessageRelationship(Collection< BlackboardAttribute > bbattributes, Content dataSource, String fileName)
BlackboardArtifact createArtifactWithAttributes(BlackboardArtifact.Type artType, Content dataSource, Collection< BlackboardAttribute > bbattributes)
void createContactRelationship(Collection< BlackboardAttribute > bbattributes, Content dataSource, String fileName)
static String extractDomain(String urlString)
List< AbstractFile > findFiles(String fileName)
void processLeappFiles(List< String > LeappFilesToProcess, AbstractFile LeappImageFile)
void createCalllogRelationship(Collection< BlackboardAttribute > bbattributes, Content dataSource, String fileName)
final Map< String, BlackboardArtifact.Type > tsvFileArtifacts
AbstractFile findAbstractFile(Content dataSource, String fileNamePath)
void createRoute(Collection< BlackboardAttribute > bbattributes, Content dataSource, String fileName)
Collection< BlackboardAttribute > processReadLine(List< String > lineValues, Map< String, Integer > columnIndexes, List< TsvColumn > attrList, String fileName, int lineNum)
BlackboardAttribute parseAttrValue(String value, BlackboardAttribute.Type attrType, String fileName, boolean blankIsNull, boolean zeroIsNull, ParseExceptionFunction valueConverter)
ProcessResult processFileSystem(Content dataSource, Path moduleOutputPath)
ProcessResult processFiles(Content dataSource, Path moduleOutputPath, AbstractFile LeappFile)
static< T > boolean extractResourceToUserConfigDir(final Class< T > resourceClass, final String resourceFileName, boolean overWrite)
void processFile(File LeappFile, List< TsvColumn > attrList, String fileName, BlackboardArtifact.Type artifactType, List< BlackboardArtifact > bbartifacts, Content dataSource)
BlackboardAttribute getAttribute(BlackboardAttribute.Type attrType, String value, String fileName)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
AbstractFile createTrackpoint(Collection< BlackboardAttribute > bbattributes, Content dataSource, String fileName, String trackpointSegmentName, GeoTrackPoints pointList)
void processLeappFiles(List< String > LeappFilesToProcess, Content dataSource)
String getXmlAttrIdentifier(String fileName, String attributeName)
String formatValueBasedOnAttrType(TsvColumn colAttr, String value)

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.