19 package org.sleuthkit.autopsy.modules.leappanalyzers;
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;
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;
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;
57 import org.openide.util.NbBundle.Messages;
91 import org.w3c.dom.Document;
92 import org.w3c.dom.NamedNodeMap;
93 import org.w3c.dom.NodeList;
94 import org.xml.sax.SAXException;
135 String getColumnName() {
142 boolean isRequired() {
162 .put(
"TSK_IP_DHCP",
"DHCP Information")
166 .put(
"zapya.tsv",
"message")
167 .put(
"sms messages.tsv",
"message")
168 .put(
"mms messages.tsv",
"message")
169 .put(
"viber - messages.tsv",
"message")
170 .put(
"viber - contacts.tsv",
"contact")
171 .put(
"viber - call logs.tsv",
"calllog")
172 .put(
"xender file transfer - messages.tsv",
"message")
173 .put(
"xender file transfer - contacts.tsv",
"contact")
174 .put(
"whatsapp - contacts.tsv",
"contact")
175 .put(
"whatsapp - group call logs.tsv",
"calllog")
176 .put(
"whatsapp - single call logs.tsv",
"calllog")
177 .put(
"whatsapp - messages logs.tsv",
"message")
178 .put(
"shareit file transfer.tsv",
"message")
179 .put(
"tangomessages messages.tsv",
"message")
180 .put(
"contacts.tsv",
"contact")
181 .put(
"imo - accountid.tsv",
"contact")
182 .put(
"imo - messages.tsv",
"message")
183 .put(
"textnow - contacts.tsv",
"contact")
184 .put(
"textnow - messages.tsv",
"message")
185 .put(
"line - messages.tsv",
"message")
186 .put(
"line - contacts.tsv",
"contact")
187 .put(
"line - calllogs.tsv",
"calllog")
188 .put(
"skype - messages logs.tsv",
"message")
189 .put(
"skype - contacts.tsv",
"contact")
190 .put(
"skype - call logs.tsv",
"calllog")
191 .put(
"facebook messenger - chats.tsv",
"message")
192 .put(
"facebook messenger - contacts.tsv",
"contact")
193 .put(
"facebook messenger - calls.tsv",
"calllog")
194 .put(
"call logs2.tsv",
"calllog")
195 .put(
"call logs.tsv",
"calllog")
196 .put(
"oruxmaps tracks.tsv",
"trackpoint")
197 .put(
"google map locations.tsv",
"route")
198 .put(
"Contacts.tsv",
"contact")
199 .put(
"sms - imessage.tsv",
"message")
200 .put(
"call history.tsv",
"calllog")
206 this.tsvFiles =
new HashMap<>();
208 this.tsvFileArtifactComments =
new HashMap<>();
209 this.tsvFileAttributes =
new HashMap<>();
232 return StringUtils.defaultString(origKey).trim().toLowerCase();
236 "LeappFileProcessor.error.running.Leapp=Error running Leapp, see log file.",
237 "LeappFileProcessor.error.creating.output.dir=Error creating Leapp module output directory.",
238 "LeappFileProcessor.starting.Leapp=Starting Leapp",
239 "LeappFileProcessor.running.Leapp=Running Leapp",
240 "LeappFileProcessor.has.run=Leapp",
241 "LeappFileProcessor.Leapp.cancelled=Leapp run was canceled",
242 "LeappFileProcessor.completed=Leapp Processing Completed",
243 "LeappFileProcessor.findTsv=Finding all Leapp ouput",
244 "LeappFileProcessor.error.reading.Leapp.directory=Error reading Leapp Output Directory"
252 progress.
progress(Bundle.LeappFileProcessor_findTsv());
253 List<String> LeappTsvOutputFiles =
findTsvFiles(moduleOutputPath);
255 }
catch (IngestModuleException ex) {
256 logger.log(Level.SEVERE, String.format(
"Error trying to process Leapp output files in directory %s. ", moduleOutputPath.toString()), ex);
269 progress.
progress(Bundle.LeappFileProcessor_findTsv());
270 List<String> LeappTsvOutputFiles =
findTsvFiles(moduleOutputPath);
272 }
catch (IngestModuleException ex) {
273 logger.log(Level.SEVERE, String.format(
"Error trying to process Leapp output files in directory %s. ", moduleOutputPath.toString()), ex);
284 private List<String>
findTsvFiles(Path LeappOutputDir)
throws IngestModuleException {
285 List<String> allTsvFiles;
286 List<String> foundTsvFiles =
new ArrayList<>();
288 try (Stream<Path> walk = Files.walk(LeappOutputDir)) {
290 allTsvFiles = walk.map(x -> x.toString())
291 .filter(f -> f.toLowerCase().endsWith(
".tsv")).collect(Collectors.toList());
293 for (String tsvFile : allTsvFiles) {
294 if (tsvFiles.containsKey(
normalizeKey(FilenameUtils.getName(tsvFile)))) {
295 foundTsvFiles.add(tsvFile);
299 }
catch (IOException | UncheckedIOException e) {
300 throw new IngestModuleException(Bundle.LeappFileProcessor_error_reading_Leapp_directory() + LeappOutputDir.toString(), e);
303 return foundTsvFiles;
309 logger.log(Level.INFO,
"Leapp File processing module run was cancelled");
328 "LeappFileProcessor.tsvProcessed=Processing LEAPP output file: {0}"
333 for (
int i = 0; i < LeappFilesToProcess.size(); i++) {
338 String LeappFileName = LeappFilesToProcess.get(i);
339 String fileName = FilenameUtils.getName(LeappFileName);
340 progress.
progress(Bundle.LeappFileProcessor_tsvProcessed(fileName), i);
342 File LeappFile =
new File(LeappFileName);
343 String fileKey = fileName.toLowerCase().trim();
344 if (tsvFileAttributes.containsKey(
normalizeKey(fileKey))) {
345 List<TsvColumn> attrList = tsvFileAttributes.get(
normalizeKey(fileKey));
349 processFile(LeappFile, attrList, fileName, artifactType, dataSource);
351 logger.log(Level.SEVERE, String.format(
"Error processing file at %s", LeappFile.toString()), ex);
358 private void processFile(File LeappFile, List<TsvColumn> attrList, String fileName,
362 String trackpointSegmentName = null;
366 if (LeappFile == null || !LeappFile.
exists() || fileName == null) {
367 logger.log(Level.WARNING, String.format(
"Leap file: %s is null or does not exist", LeappFile != null ? LeappFile.
toString() :
"<null>"));
369 }
else if (attrList == null || artifactType == null || dataSource == null) {
370 logger.log(Level.WARNING, String.format(
"attribute list, artifact type or dataSource not provided for %s", LeappFile.
toString()));
374 List<BlackboardArtifact> bbartifacts =
new ArrayList<>();
377 try (MappingIterator<List<String>> iterator =
new CsvMapper()
378 .enable(CsvParser.Feature.WRAP_AS_ARRAY)
379 .readerFor(List.class)
380 .with(CsvSchema.emptySchema().withColumnSeparator(
'\t'))
381 .readValues(LeappFile)) {
383 if (iterator.hasNext()) {
384 List<String> headerItems = iterator.next();
385 Map<String, Integer> columnIndexes = IntStream.range(0, headerItems.size())
386 .mapToObj(idx -> idx)
387 .collect(Collectors.toMap(
388 idx -> headerItems.get(idx) == null ? null : headerItems.get(idx).trim().toLowerCase(),
390 (val1, val2) -> val1));
393 while (iterator.hasNext()) {
394 List<String> columnItems = iterator.next();
395 Collection<BlackboardAttribute> bbattributes =
processReadLine(columnItems, columnIndexes, attrList, fileName, lineNum);
397 if (!bbattributes.isEmpty()) {
398 switch (ACCOUNT_RELATIONSHIPS.getOrDefault(fileName.toLowerCase(),
"norelationship").toLowerCase()) {
412 geoAbstractFile =
createTrackpoint(bbattributes, dataSource, fileName, trackpointSegmentName, pointList);
416 if (bbartifact != null) {
417 bbartifacts.add(bbartifact);
429 if (ACCOUNT_RELATIONSHIPS.getOrDefault(fileName.toLowerCase(),
"norelationship").toLowerCase().equals(
"trackpoint")) {
433 throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_message_relationship() + ex.getLocalizedMessage(), ex);
436 if (!bbartifacts.isEmpty()) {
437 postArtifacts(bbartifacts);
442 "LeappFileProcessor.cannot.create.waypoint.relationship=Cannot create TSK_WAYPOINT artifact."
444 private void createRoute(Collection<BlackboardAttribute> bbattributes,
Content dataSource, String fileName)
throws IngestModuleException {
446 Double startLatitude = Double.valueOf(0);
447 Double startLongitude = Double.valueOf(0);
448 Double endLatitude = Double.valueOf(0);
449 Double endLongitude = Double.valueOf(0);
450 Double zeroValue = Double.valueOf(0);
451 String destinationName =
"";
452 String locationName =
"";
453 Long dateTime = Long.valueOf(0);
454 Collection<BlackboardAttribute> otherAttributes =
new ArrayList<>();
455 String sourceFile = null;
461 switch (bba.getAttributeType().getTypeName()) {
462 case "TSK_GEO_LATITUDE_START":
463 startLatitude = bba.getValueDouble();
465 case "TSK_GEO_LONGITUDE_START":
466 startLongitude = bba.getValueDouble();
468 case "TSK_GEO_LATITUDE_END":
469 startLatitude = bba.getValueDouble();
471 case "TSK_GEO_LONGITUDE_END":
472 startLongitude = bba.getValueDouble();
475 dateTime = bba.getValueLong();
478 destinationName = bba.getValueString();
481 locationName = bba.getValueString();
483 case "TSK_TEXT_FILE":
484 sourceFile = bba.getValueString();
487 comment = bba.getValueString();
490 otherAttributes.add(bba);
495 if (absFile == null) {
499 waypointList.
addPoint(
new Waypoint(startLatitude, startLongitude, zeroValue,
""));
500 waypointList.
addPoint(
new Waypoint(endLatitude, endLongitude, zeroValue, locationName));
504 throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_waypoint_relationship() + ex.getLocalizedMessage(), ex);
510 "LeappFileProcessor.cannot.create.trackpoint.relationship=Cannot create TSK_TRACK_POINT artifact."
514 Double latitude = Double.valueOf(0);
515 Double longitude = Double.valueOf(0);
516 Double altitude = Double.valueOf(0);
517 Double zeroValue = Double.valueOf(0);
518 String segmentName = null;
519 Long dateTime = Long.valueOf(0);
520 Collection<BlackboardAttribute> otherAttributes =
new ArrayList<>();
521 String sourceFile = null;
522 String comment = null;
527 switch (bba.getAttributeType().getTypeName()) {
528 case "TSK_GEO_LATITUDE":
529 latitude = bba.getValueDouble();
531 case "TSK_GEO_LONGITUDE":
532 longitude = bba.getValueDouble();
534 case "TSK_GEO_ALTITUDE":
535 altitude = bba.getValueDouble();
538 dateTime = bba.getValueLong();
541 segmentName = bba.getValueString();
543 case "TSK_TEXT_FILE":
544 sourceFile = bba.getValueString();
547 comment = bba.getValueString();
548 otherAttributes.add(bba);
551 otherAttributes.add(bba);
556 if (absFile == null) {
559 if ((trackpointSegmentName == null) || (trackpointSegmentName.equals(segmentName))) {
560 pointList.
addPoint(
new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime));
563 pointList.
addPoint(
new TrackPoint(latitude, longitude, altitude, segmentName, zeroValue, zeroValue, zeroValue, dateTime));
567 throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_trackpoint_relationship() + ex.getLocalizedMessage(), ex);
575 "LeappFileProcessor.cannot.create.message.relationship=Cannot create TSK_MESSAGE Relationship."
579 String messageType = null;
580 String alternateId = null;
582 String senderId = null;
583 String receipentId = null;
584 String[] receipentIdList = null;
585 Long dateTime = Long.valueOf(0);
587 String subject = null;
588 String messageText = null;
589 String threadId = null;
590 List<BlackboardAttribute> otherAttributes =
new ArrayList<>();
591 List<FileAttachment> fileAttachments =
new ArrayList<>();
592 String sourceFile = null;
597 switch (bba.getAttributeType().getTypeName()) {
598 case "TSK_DIRECTION":
599 if (bba.getValueString().toLowerCase().equals(
"outgoing")) {
601 }
else if (bba.getValueString().toLowerCase().equals(
"incoming")) {
605 case "TSK_PHONE_NUMBER_FROM":
606 if (!bba.getValueString().isEmpty()) {
607 senderId = bba.getValueString();
610 case "TSK_PHONE_NUMBER_TO":
611 if (!bba.getValueString().isEmpty()) {
612 receipentIdList = bba.getValueString().split(
",", 0);
616 dateTime = bba.getValueLong();
619 messageType = bba.getValueString();
621 case "TSK_ATTACHMENTS":
622 if (!bba.getValueString().isEmpty()) {
626 case "TSK_TEXT_FILE":
627 sourceFile = bba.getValueString();
629 case "TSK_READ_STATUS":
630 if (bba.getValueInt() == 1) {
637 messageText = bba.getValueString();
640 subject = bba.getValueString();
643 alternateId = bba.getValueString();
644 otherAttributes.add(bba);
647 otherAttributes.add(bba);
652 if (absFile == null) {
657 if (alternateId == null) {
665 receipentId, dateTime, messageStatus, subject,
666 messageText, threadId, otherAttributes);
667 if (!fileAttachments.isEmpty()) {
669 accountHelper.
addAttachments(messageArtifact, messageAttachments);
672 throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_message_relationship() + ex.getLocalizedMessage(), ex);
678 "LeappFileProcessor.cannot.create.contact.relationship=Cannot create TSK_CONTACT Relationship."
682 String alternateId = null;
683 String contactName = null;
684 String phoneNumber = null;
685 String homePhoneNumber = null;
686 String mobilePhoneNumber = null;
687 String emailAddr = null;
688 List<BlackboardAttribute> otherAttributes =
new ArrayList<>();
689 String sourceFile = null;
693 switch (bba.getAttributeType().getTypeName()) {
694 case "TSK_PHONE_NUMBER":
695 if (!bba.getValueString().isEmpty()) {
696 phoneNumber = bba.getValueString();
700 if (!bba.getValueString().isEmpty()) {
701 contactName = bba.getValueString();
704 case "TSK_TEXT_FILE":
705 sourceFile = bba.getValueString();
707 case "TSK_PHONE_NUMBER_HOME":
708 homePhoneNumber = bba.getValueString();
710 case "TSK_PHONE_NUMBER_MOBILE":
711 mobilePhoneNumber = bba.getValueString();
714 emailAddr = bba.getValueString();
717 alternateId = bba.getValueString();
718 otherAttributes.add(bba);
721 otherAttributes.add(bba);
726 if (absFile == null) {
730 if (accountType != null) {
733 if (alternateId == null) {
740 BlackboardArtifact messageArtifact = accountHelper.
addContact(contactName, phoneNumber, homePhoneNumber, mobilePhoneNumber, emailAddr, otherAttributes);
743 throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_contact_relationship() + ex.getLocalizedMessage(), ex);
748 "LeappFileProcessor.cannot.create.calllog.relationship=Cannot create TSK_CALLLOG Relationship."
752 String callerId = null;
753 String alternateId = null;
754 List<String> calleeId = Arrays.asList();
756 Long startDateTime = Long.valueOf(0);
757 Long endDateTime = Long.valueOf(0);
759 List<BlackboardAttribute> otherAttributes =
new ArrayList<>();
760 String sourceFile = null;
764 switch (bba.getAttributeType().getTypeName()) {
765 case "TSK_TEXT_FILE":
766 sourceFile = bba.getValueString();
768 case "TSK_DATETIME_START":
769 startDateTime = bba.getValueLong();
771 case "TSK_DATETIME_END":
772 startDateTime = bba.getValueLong();
774 case "TSK_DIRECTION":
775 if (bba.getValueString().toLowerCase().equals(
"outgoing")) {
777 }
else if (bba.getValueString().toLowerCase().equals(
"incoming")) {
781 case "TSK_PHONE_NUMBER_FROM":
782 if (!bba.getValueString().isEmpty()) {
783 callerId = bba.getValueString();
786 case "TSK_PHONE_NUMBER_TO":
787 if (!bba.getValueString().isEmpty()) {
788 String[] calleeTempList = bba.getValueString().split(
",", 0);
789 calleeId = Arrays.asList(calleeTempList);
793 alternateId = bba.getValueString();
794 otherAttributes.add(bba);
797 otherAttributes.add(bba);
803 String[] calleeTempList = callerId.split(
",", 0);
804 calleeId = Arrays.asList(calleeTempList);
808 if (absFile == null) {
813 if (accountType != null) {
820 accountHelper.
addCalllog(communicationDirection, callerId, calleeId, startDateTime, endDateTime, mediaType, otherAttributes);
822 throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_create_calllog_relationship() + ex.getLocalizedMessage(), ex);
828 switch (AccountTypeName.toLowerCase()) {
831 case "sms messages.tsv":
835 case "imo - accountid.tsv":
837 case "imo - messages.tsv":
839 case "textnow - contacts.tsv":
841 case "textnow - messages.tsv":
843 case "mms messages.tsv":
845 case "viber - call logs.tsv":
847 case "viber - contacts.tsv":
849 case "viber - messages.tsv":
851 case "xender file transfer - messages.tsv":
853 case "xender file transfer - contacts.tsv":
855 case "whatsapp - single call logs.tsv":
857 case "whatsapp - messages logs.tsv":
859 case "whatsapp - group call logs.tsv":
861 case "whatsapp - contacts.tsv":
863 case "tangomessages messages.tsv":
865 case "shareit file transfer.tsv":
867 case "line - calllogs.tsv":
869 case "line - contacts.tsv":
871 case "line - messages.tsv":
873 case "skype - call logs.tsv":
875 case "skype - contacts.tsv":
877 case "skype - messages logs.tsv":
879 case "facebook messenger - calls.tsv":
881 case "facebook messenger - contacts.tsv":
883 case "facebook messenger - chats.tsv":
885 case "call logs2.tsv":
887 case "call logs.tsv":
889 case "sms - imessage.tsv":
913 private Collection<BlackboardAttribute>
processReadLine(List<String> lineValues, Map<String, Integer> columnIndexes,
914 List<TsvColumn> attrList, String fileName,
int lineNum)
throws IngestModuleException {
917 if (MapUtils.isEmpty(columnIndexes) || CollectionUtils.isEmpty(lineValues)
918 || (lineValues.size() == 1 && StringUtils.isEmpty(lineValues.get(0)))) {
919 return Collections.emptyList();
922 List<BlackboardAttribute> attrsToRet =
new ArrayList<>();
925 if (colAttr.getAttributeType() == null) {
930 Integer columnIdx = columnIndexes.get(colAttr.getColumnName());
931 if (columnIdx == null) {
932 logger.log(Level.WARNING, String.format(
"No column mapping found for %s in file %s. Omitting column.", colAttr.getColumnName(), fileName));
936 String value = (columnIdx >= lineValues.size() || columnIdx < 0) ? null : lineValues.get(columnIdx);
939 if (colAttr.isRequired()) {
940 logger.log(Level.WARNING, String.format(
"No value found for required column %s at line %d in file %s. Omitting row.", colAttr.getColumnName(), lineNum, fileName));
941 return Collections.emptyList();
944 logger.log(Level.WARNING, String.format(
"No value found for column %s at line %d in file %s. Omitting column.", colAttr.getColumnName(), lineNum, fileName));
953 attrsToRet.add(attr);
954 }
else if (colAttr.isRequired()) {
955 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));
956 return Collections.emptyList();
960 if (tsvFileArtifactComments.containsKey(
normalizeKey(fileName))) {
977 if (colAttr.getAttributeType().getTypeName().
equals(
"TSK_DOMAIN")) {
987 private static final DateFormat
TIMESTAMP_FORMAT =
new SimpleDateFormat(
"yyyy-MM-d HH:mm:ss", US);
1001 if (attrType == null || value == null) {
1002 logger.log(Level.WARNING, String.format(
"Unable to parse attribute type %s for value '%s' in fileName %s",
1003 attrType == null ?
"<null>" : attrType.toString(),
1004 value == null ?
"<null>" : value,
1005 fileName == null ?
"<null>" : fileName));
1009 switch (attrType.getValueType()) {
1015 return parseAttrValue(value.trim(), attrType, fileName,
true,
false,
1018 return parseAttrValue(value.trim(), attrType, fileName,
true,
false,
1021 return parseAttrValue(value.trim(), attrType, fileName,
true,
false,
1024 return parseAttrValue(value.trim(), attrType, fileName,
true,
false,
1027 return parseAttrValue(value.trim(), attrType, fileName,
true,
true,
1028 (v) ->
new BlackboardAttribute(attrType, moduleName, TIMESTAMP_FORMAT.parse(v).getTime() / 1000));
1031 logger.log(Level.WARNING, String.format(
"Attribute Type %s for file %s not defined.", attrType, fileName));
1073 String sanitizedValue = value.replaceAll(
"\\p{C}",
"");
1075 if (blankIsNull && StringUtils.isBlank(sanitizedValue)) {
1079 if (zeroIsNull && sanitizedValue.matches(
"^\\s*[0\\.]*\\s*$")) {
1084 return valueConverter.
apply(sanitizedValue);
1085 }
catch (NumberFormatException | ParseException ex) {
1086 logger.log(Level.WARNING, String.format(
"Unable to format '%s' as value type %s while converting to attributes from %s.", sanitizedValue, attrType.getValueType().getLabel(), fileName), ex);
1098 if (
new File(userPath).exists()) {
1106 @NbBundle.Messages({
1107 "LeappFileProcessor.cannot.load.artifact.xml=Cannot load xml artifact file.",
1108 "LeappFileProcessor.cannotBuildXmlParser=Cannot buld an XML parser.",
1109 "LeappFileProcessor_cannotParseXml=Cannot Parse XML file.",
1110 "LeappFileProcessor.postartifacts_error=Error posting Blackboard Artifact",
1111 "LeappFileProcessor.error.creating.new.artifacts=Error creating new artifacts."
1116 File f =
new File(path);
1117 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
1118 DocumentBuilder db = dbf.newDocumentBuilder();
1119 xmlinput = db.parse(f);
1121 }
catch (IOException e) {
1122 throw new IngestModuleException(Bundle.LeappFileProcessor_cannot_load_artifact_xml() + e.getLocalizedMessage(), e);
1123 }
catch (ParserConfigurationException pce) {
1124 throw new IngestModuleException(Bundle.LeappFileProcessor_cannotBuildXmlParser() + pce.getLocalizedMessage(), pce);
1125 }
catch (SAXException sxe) {
1126 throw new IngestModuleException(Bundle.LeappFileProcessor_cannotParseXml() + sxe.getLocalizedMessage(), sxe);
1137 NodeList nlist = xmlinput.getElementsByTagName(
"FileName");
1139 for (
int i = 0; i < nlist.getLength(); i++) {
1140 NamedNodeMap nnm = nlist.item(i).getAttributes();
1141 tsvFiles.put(
normalizeKey(nnm.getNamedItem(
"filename").getNodeValue()), nnm.getNamedItem(
"description").getNodeValue());
1149 NodeList artifactNlist = xmlinput.getElementsByTagName(
"ArtifactName");
1150 for (
int k = 0; k < artifactNlist.getLength(); k++) {
1151 NamedNodeMap nnm = artifactNlist.item(k).getAttributes();
1152 String artifactName = nnm.getNamedItem(
"artifactname").getNodeValue();
1153 String comment = nnm.getNamedItem(
"comment").getNodeValue();
1154 String parentName = artifactNlist.item(k).getParentNode().getAttributes().getNamedItem(
"filename").getNodeValue();
1160 logger.log(Level.SEVERE, String.format(
"There was an issue that arose while trying to fetch artifact type for %s.", artifactName), ex);
1163 if (foundArtifactType == null) {
1164 logger.log(Level.SEVERE, String.format(
"No known artifact mapping found for [artifact: %s, %s]",
1170 if (!comment.toLowerCase().matches(
"null")) {
1171 tsvFileArtifactComments.put(
normalizeKey(parentName), comment);
1178 return String.format(
"file: %s, filename: %s",
1179 this.xmlFile == null ?
"<null>" : this.xmlFile,
1180 fileName == null ?
"<null>" : fileName);
1184 return String.format(
"attribute: %s %s",
1185 attributeName == null ?
"<null>" : attributeName,
1191 NodeList attributeNlist = xmlinput.getElementsByTagName(
"AttributeName");
1192 for (
int k = 0; k < attributeNlist.getLength(); k++) {
1193 NamedNodeMap nnm = attributeNlist.item(k).getAttributes();
1194 String attributeName = nnm.getNamedItem(
"attributename").getNodeValue();
1196 if (!attributeName.toLowerCase().matches(
"null")) {
1197 String columnName = nnm.getNamedItem(
"columnName").getNodeValue();
1198 String required = nnm.getNamedItem(
"required").getNodeValue();
1199 String parentName = attributeNlist.item(k).getParentNode().getParentNode().getAttributes().getNamedItem(
"filename").getNodeValue();
1205 logger.log(Level.SEVERE, String.format(
"There was an issue that arose while trying to fetch attribute type for %s.", attributeName), ex);
1208 if (foundAttrType == null) {
1209 logger.log(Level.SEVERE, String.format(
"No known attribute mapping found for [%s]",
getXmlAttrIdentifier(parentName, attributeName)));
1212 if (required != null && required.compareToIgnoreCase(
"yes") != 0 && required.compareToIgnoreCase(
"no") != 0) {
1213 logger.log(Level.SEVERE, String.format(
"Required value %s did not match 'yes' or 'no' for [%s]",
1217 if (columnName == null) {
1218 logger.log(Level.SEVERE, String.format(
"No column name provided for [%s]",
getXmlAttrIdentifier(parentName, attributeName)));
1220 }
else if (columnName.trim().length() != columnName.length()) {
1221 logger.log(Level.SEVERE, String.format(
"Column name '%s' starts or ends with whitespace for [%s]", columnName,
getXmlAttrIdentifier(parentName, attributeName)));
1223 }
else if (columnName.matches(
"[^ \\S]")) {
1224 logger.log(Level.SEVERE, String.format(
"Column name '%s' contains invalid characters [%s]", columnName,
getXmlAttrIdentifier(parentName, attributeName)));
1230 columnName.trim().toLowerCase(),
1231 "yes".compareToIgnoreCase(required) == 0);
1233 if (tsvFileAttributes.containsKey(
normalizeKey(parentName))) {
1234 List<TsvColumn> attrList = tsvFileAttributes.get(
normalizeKey(parentName));
1235 attrList.add(thisCol);
1236 tsvFileAttributes.replace(parentName, attrList);
1238 List<TsvColumn> attrList =
new ArrayList<>();
1239 attrList.add(thisCol);
1240 tsvFileAttributes.put(
normalizeKey(parentName), attrList);
1261 switch (artType.getCategory()) {
1264 case ANALYSIS_RESULT:
1267 logger.log(Level.SEVERE, String.format(
"Unknown category type: %s", artType.getCategory().getDisplayName()));
1271 logger.log(Level.WARNING, Bundle.LeappFileProcessor_error_creating_new_artifacts(), ex);
1282 void postArtifacts(Collection<BlackboardArtifact> artifacts) {
1283 if (artifacts == null || artifacts.isEmpty()) {
1288 Case.getCurrentCase().getSleuthkitCase().getBlackboard().postArtifacts(artifacts, moduleName, context.
getJobId());
1289 }
catch (Blackboard.BlackboardException ex) {
1290 logger.log(Level.SEVERE, Bundle.LeappFileProcessor_postartifacts_error(), ex);
1313 static List<AbstractFile> findLeappFilesToProcess(
Content dataSource) {
1315 List<AbstractFile> leappFiles =
new ArrayList<>();
1317 FileManager fileManager = getCurrentCase().getServices().getFileManager();
1321 leappFiles = fileManager.
findFiles(dataSource,
"%",
"/");
1323 logger.log(Level.WARNING,
"No files found to process");
1327 List<AbstractFile> leappFilesToProcess =
new ArrayList<>();
1329 if (((leappFile.getLocalAbsPath() != null)
1330 && !leappFile.isVirtual())
1331 && leappFile.getNameExtension() != null
1332 && ALLOWED_EXTENSIONS.contains(leappFile.getNameExtension().toLowerCase())) {
1333 leappFilesToProcess.add(leappFile);
1337 return leappFilesToProcess;
1346 for (Map.Entry<String, String> customArtifact : CUSTOM_ARTIFACT_MAP.entrySet()) {
1347 String artifactName = customArtifact.getKey();
1348 String artifactDescription = customArtifact.getValue();
1353 if (customFilePath.exists()) {
1354 try (MappingIterator<List<String>> iterator =
new CsvMapper()
1355 .enable(CsvParser.Feature.WRAP_AS_ARRAY)
1356 .readerFor(List.class)
1357 .with(CsvSchema.emptySchema().withColumnSeparator(
','))
1358 .readValues(customFilePath)) {
1360 if (iterator.hasNext()) {
1362 List<String> headerItems = iterator.next();
1364 while (iterator.hasNext()) {
1365 List<String> columnItems = iterator.next();
1366 if (columnItems.size() > 3) {
1373 }
catch (IOException ex) {
1374 logger.log(Level.WARNING, String.format(
"Failed to read/open file %s.", customFilePath), ex);
1385 if (atType.toLowerCase().equals(
"artifact")) {
1389 logger.log(Level.WARNING, String.format(
"Failed to create custom artifact type %s.", atName), ex);
1394 switch (attrType.toLowerCase()) {
1400 logger.log(Level.WARNING, String.format(
"Failed to create custom attribute type %s.", atName), ex);
1407 logger.log(Level.WARNING, String.format(
"Failed to create custom attribute type %s.", atName), ex);
1414 logger.log(Level.WARNING, String.format(
"Failed to create custom attribute type %s.", atName), ex);
1421 logger.log(Level.WARNING, String.format(
"Failed to create custom attribute type %s.", atName), ex);
1428 logger.log(Level.WARNING, String.format(
"Failed to create custom attribute type %s.", atName), ex);
1435 logger.log(Level.WARNING, String.format(
"Failed to create custom attribute type %s.", atName), ex);
1439 logger.log(Level.WARNING, String.format(
"Attribute Type %s for file %s not defined.", attrType, atName));
1451 for (Map.Entry<String, String> customArtifact : CUSTOM_ARTIFACT_MAP.entrySet()) {
1452 String artifactName = customArtifact.getKey();
1453 String artifactDescription = customArtifact.getValue();
1458 logger.log(Level.WARNING, String.format(
"Failed to create custom artifact type %s.", artifactName), ex);
1465 if (fileNamePath == null) {
1469 List<AbstractFile> files;
1471 String fileName = FilenameUtils.
getName(fileNamePath);
1472 String filePath = FilenameUtils.normalize(FilenameUtils.getPath(fileNamePath),
true);
1477 files = fileManager.
findFiles(dataSource, fileName);
1480 logger.log(Level.WARNING,
"Unable to find prefetch files.", ex);
1486 if (pFile.getParentPath().toLowerCase().endsWith(filePath.toLowerCase())) {
void createMessageRelationship(Collection< BlackboardAttribute > bbattributes, Content dataSource, String fileName)
final IngestJobContext context
BlackboardArtifact createArtifactWithAttributes(BlackboardArtifact.Type artType, Content dataSource, Collection< BlackboardAttribute > bbattributes)
static final Account.Type TANGO
String getXmlFileIdentifier(String fileName)
final String ARTIFACT_ATTRIBUTE_REFERENCE_USER
static final Score SCORE_UNKNOWN
final BlackboardAttribute.Type attributeType
FileManager getFileManager()
void createContactRelationship(Collection< BlackboardAttribute > bbattributes, Content dataSource, String fileName)
BlackboardArtifact addContact(String contactName, String phoneNumber, String homePhoneNumber, String mobilePhoneNumber, String emailAddr)
Blackboard getBlackboard()
static String extractDomain(String urlString)
static final Account.Type TEXTNOW
List< String > findTsvFiles(Path LeappOutputDir)
List< AbstractFile > findFiles(String fileName)
static final Account.Type WHATSAPP
DataArtifact newDataArtifact(BlackboardArtifact.Type artifactType, Collection< BlackboardAttribute > attributesList)
static final Account.Type FACEBOOK
void createCalllogRelationship(Collection< BlackboardAttribute > bbattributes, Content dataSource, String fileName)
static final Account.Type ZAPYA
static final Logger logger
void processFile(File LeappFile, List< TsvColumn > attrList, String fileName, BlackboardArtifact.Type artifactType, Content dataSource)
void loadCustomArtifactsAttributes(Blackboard blkBoard, String leapModule)
AnalysisResult getAnalysisResult()
static final DateFormat TIMESTAMP_FORMAT
static final Account.Type PHONE
final Map< String, BlackboardArtifact.Type > tsvFileArtifacts
static final Set< String > ALLOWED_EXTENSIONS
void getArtifactNode(Document xmlinput)
AbstractFile findAbstractFile(Content dataSource, String fileNamePath)
void createRoute(Collection< BlackboardAttribute > bbattributes, Content dataSource, String fileName)
BlackboardArtifact addMessage(String messageType, CommunicationDirection direction, String senderId, String recipientId, long dateTime, MessageReadStatus readStatus, String subject, String messageText, String threadId)
static final Map< String, String > CUSTOM_ARTIFACT_MAP
static final Account.Type IMO
String toString(boolean preserveState)
synchronized BlackboardAttribute.Type getOrAddAttributeType(String typeName, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE valueType, String displayName)
Collection< BlackboardAttribute > processReadLine(List< String > lineValues, Map< String, Integer > columnIndexes, List< TsvColumn > attrList, String fileName, int lineNum)
void switchToIndeterminate()
static final Account.Type XENDER
BlackboardAttribute parseAttrValue(String value, BlackboardAttribute.Type attrType, String fileName, boolean blankIsNull, boolean zeroIsNull, ParseExceptionFunction valueConverter)
void processLeappFiles(List< String > LeappFilesToProcess, Content dataSource, DataSourceIngestModuleProgress progress)
void createCustomAttributesArtifacts(Blackboard blkBoard, String atType, String atName, String atDescription, String attrType)
final Map< String, String > tsvFiles
BlackboardArtifact.Type getArtifactType(String artTypeName)
static final Account.Type LINE
void loadIndividualConfigFile(String path)
final String CUSTOM_ARTIFACTS_ATTRIBUTES_FILE
static final Map< String, String > ACCOUNT_RELATIONSHIPS
SleuthkitCase getSleuthkitCase()
final Map< String, String > tsvFileArtifactComments
BlackboardAttribute getAttribute(BlackboardAttribute.Type attrType, String value, String fileName)
final Map< String, List< TsvColumn > > tsvFileAttributes
boolean dataSourceIngestIsCancelled()
static String normalizeKey(String origKey)
boolean equals(Object that)
ProcessResult processFileSystem(Content dataSource, Path moduleOutputPath, DataSourceIngestModuleProgress progress)
final Blackboard blkBoard
static final Account.Type VIBER
AnalysisResultAdded newAnalysisResult(BlackboardArtifact.Type artifactType, Score score, String conclusion, String configuration, String justification, Collection< BlackboardAttribute > attributesList)
BlackboardArtifact.Type getOrAddArtifactType(String typeName, String displayName)
void addPoint(Waypoint wayPoint)
static final Account.Type SHAREIT
void switchToDeterminate(int workUnits)
void createCustomArtifacts(Blackboard blkBoard)
static final Account.Type SKYPE
static Case getCurrentCase()
synchronized static Logger getLogger(String name)
static Case getCurrentCaseThrows()
BlackboardAttribute apply(String orig)
AbstractFile createTrackpoint(Collection< BlackboardAttribute > bbattributes, Content dataSource, String fileName, String trackpointSegmentName, GeoTrackPoints pointList)
void addPoint(TrackPoint trackPoint)
Account.Type getAccountType(String AccountTypeName)
BlackboardAttribute.Type getAttributeType(String attrTypeName)
BlackboardArtifact addCalllog(CommunicationDirection direction, String callerId, String calleeId, long startDateTime, long endDateTime, CallMediaType mediaType)
void addAttachments(BlackboardArtifact message, MessageAttachments attachments)
void getAttributeNodes(Document xmlinput)
LeappFileProcessor(String xmlFile, String moduleName, String leapModule, IngestJobContext context)
void progress(int workUnits)
String getXmlAttrIdentifier(String fileName, String attributeName)
ProcessResult processFiles(Content dataSource, Path moduleOutputPath, AbstractFile LeappFile, DataSourceIngestModuleProgress progress)
String formatValueBasedOnAttrType(TsvColumn colAttr, String value)
void getFileNode(Document xmlinput)