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)