From d81c2bc22d64dd84333fa71e5191eb1b50bb7a0d Mon Sep 17 00:00:00 2001 From: "U-BASIS\\dsmyda" Date: Fri, 13 Dec 2019 15:46:24 -0500 Subject: [PATCH] Added comments and general code clean up --- .../xry/XRYCallsFileParser.java | 151 ++++---- .../xry/XRYMessagesFileParser.java | 341 +++++++++--------- 2 files changed, 244 insertions(+), 248 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYCallsFileParser.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYCallsFileParser.java index fde9b397aa..fd70a1da17 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYCallsFileParser.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYCallsFileParser.java @@ -47,13 +47,13 @@ final class XRYCallsFileParser extends AbstractSingleKeyValueParser { //function for more details. private static final DateTimeFormatter DATE_TIME_PARSER = DateTimeFormatter.ofPattern("[(XXX) ][O ][(O) ]a h:m:s M/d/y"); - + private static final String DEVICE_LOCALE = "(device)"; private static final String NETWORK_LOCALE = "(network)"; - + /** - * All of the known XRY keys for call reports and the blackboard - * attribute types they map to. + * All of the known XRY keys for call reports and their corresponding + * blackboard attribute types, if any. */ private enum XryKey { NUMBER("number", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER), @@ -66,24 +66,24 @@ final class XRYCallsFileParser extends AbstractSingleKeyValueParser { STORAGE("storage", null), INDEX("index", null), NAME("name", null); - + private final String name; private final BlackboardAttribute.ATTRIBUTE_TYPE type; - + XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type) { this.name = name; this.type = type; } - + public BlackboardAttribute.ATTRIBUTE_TYPE getType() { return type; } - + /** * Indicates if the display name of the XRY key is a recognized type. - * + * * @param xryKey - * @return + * @return */ public static boolean contains(XRYKeyValuePair pair) { try { @@ -93,24 +93,24 @@ final class XRYCallsFileParser extends AbstractSingleKeyValueParser { return false; } } - + /** * Matches the display name of the xry key to the appropriate enum type. - * - * It is assumed that XRY key string is recognized. Otherwise, - * an IllegalArgumentException is thrown. Test all membership - * with contains() before hand. - * + * + * It is assumed that XRY key string is recognized. Otherwise, an + * IllegalArgumentException is thrown. Test all membership with + * contains() before hand. + * * @param xryKey - * @return + * @return */ public static XryKey fromPair(XRYKeyValuePair pair) { - for(XryKey keyChoice : XryKey.values()) { - if(pair.hasKey(keyChoice.name)) { + for (XryKey keyChoice : XryKey.values()) { + if (pair.hasKey(keyChoice.name)) { return keyChoice; } } - + throw new IllegalArgumentException(String.format("Key [%s] was not found." + " All keys should be tested with contains.", pair.getKey())); } @@ -123,47 +123,50 @@ final class XRYCallsFileParser extends AbstractSingleKeyValueParser { TO("to"), FROM("from"), NONE(null); - + private final String name; + XryNamespace(String name) { this.name = name; } - + /** - * Indicates if the display name of the XRY namespace is a recognized type. - * + * Indicates if the display name of the XRY namespace is a recognized + * type. + * * @param xryNamespace - * @return + * @return */ public static boolean contains(String xryNamespace) { String normalizedNamespace = xryNamespace.trim().toLowerCase(); - for(XryNamespace keyChoice : XryNamespace.values()) { - if(normalizedNamespace.equals(keyChoice.name)) { + for (XryNamespace keyChoice : XryNamespace.values()) { + if (normalizedNamespace.equals(keyChoice.name)) { return true; } } - + return false; } - + /** - * Matches the display name of the xry namespace to the appropriate enum type. - * - * It is assumed that XRY namespace string is recognized. Otherwise, - * an IllegalArgumentException is thrown. Test all membership - * with contains() before hand. - * + * Matches the display name of the xry namespace to the appropriate enum + * type. + * + * It is assumed that XRY namespace string is recognized. Otherwise, an + * IllegalArgumentException is thrown. Test all membership with + * contains() before hand. + * * @param xryNamespace - * @return + * @return */ public static XryNamespace fromDisplayName(String xryNamespace) { String normalizedNamespace = xryNamespace.trim().toLowerCase(); - for(XryNamespace keyChoice : XryNamespace.values()) { - if(normalizedNamespace.equals(keyChoice.name)) { + for (XryNamespace keyChoice : XryNamespace.values()) { + if (normalizedNamespace.equals(keyChoice.name)) { return keyChoice; } } - + throw new IllegalArgumentException(String.format("Key [%s] was not found." + " All keys should be tested with contains.", xryNamespace)); } @@ -183,7 +186,7 @@ final class XRYCallsFileParser extends AbstractSingleKeyValueParser { Optional getBlackboardAttribute(String nameSpace, XRYKeyValuePair pair) { XryKey xryKey = XryKey.fromPair(pair); XryNamespace xryNamespace = XryNamespace.NONE; - if(XryNamespace.contains(nameSpace)) { + if (XryNamespace.contains(nameSpace)) { xryNamespace = XryNamespace.fromDisplayName(nameSpace); } @@ -208,7 +211,7 @@ final class XRYCallsFileParser extends AbstractSingleKeyValueParser { try { //Tranform value to seconds since epoch long dateTimeSinceEpoch = calculateSecondsSinceEpoch(pair.getValue()); - return Optional.of(new BlackboardAttribute(xryKey.getType(), + return Optional.of(new BlackboardAttribute(xryKey.getType(), PARSER_NAME, dateTimeSinceEpoch)); } catch (DateTimeParseException ex) { logger.log(Level.WARNING, String.format("[XRY DSP] Assumption" @@ -219,15 +222,15 @@ final class XRYCallsFileParser extends AbstractSingleKeyValueParser { default: //Otherwise, the XryKey enum contains the correct BlackboardAttribute //type. - if(xryKey.getType() != null) { - return Optional.of(new BlackboardAttribute(xryKey.getType(), - PARSER_NAME, pair.getValue())); + if (xryKey.getType() != null) { + return Optional.of(new BlackboardAttribute(xryKey.getType(), + PARSER_NAME, pair.getValue())); } - + logger.log(Level.WARNING, String.format("[XRY DSP] Key value pair " - + "(in brackets) [ %s ] was recognized but " - + "more data or time is needed to finish implementation. Discarding... ", - pair)); + + "(in brackets) [ %s ] was recognized but " + + "more data or time is needed to finish implementation. Discarding... ", + pair)); return Optional.empty(); } } @@ -253,7 +256,7 @@ final class XRYCallsFileParser extends AbstractSingleKeyValueParser { result = result.substring(0, deviceIndex); } int networkIndex = result.toLowerCase().indexOf(NETWORK_LOCALE); - if(networkIndex != -1) { + if (networkIndex != -1) { result = result.substring(0, networkIndex); } return result; @@ -269,28 +272,27 @@ final class XRYCallsFileParser extends AbstractSingleKeyValueParser { String dateTimeWithoutLocale = removeDateTimeLocale(dateTime).trim(); /** * The format of time in XRY Messages reports is of the form: - * - * 1/3/1990 1:23:54 AM UTC+4 - * - * In our current version of Java (openjdk-1.8.0.222), there is - * a bug with having the timezone offset (UTC+4 or GMT-7) at the - * end of the date time input. This is fixed in later versions - * of the JDK (9 and beyond). - * https://bugs.openjdk.java.net/browse/JDK-8154050 - * Rather than update the JDK to accommodate this, the components of - * the date time string are reversed: - * - * UTC+4 AM 1:23:54 1/3/1990 - * + * + * 1/3/1990 1:23:54 AM UTC+4 + * + * In our current version of Java (openjdk-1.8.0.222), there is a bug + * with having the timezone offset (UTC+4 or GMT-7) at the end of the + * date time input. This is fixed in later versions of the JDK (9 and + * beyond). https://bugs.openjdk.java.net/browse/JDK-8154050 Rather than + * update the JDK to accommodate this, the components of the date time + * string are reversed: + * + * UTC+4 AM 1:23:54 1/3/1990 + * * The java time package will correctly parse this date time format. */ String reversedDateTime = reverseOrderOfDateTimeComponents(dateTimeWithoutLocale); /** - * Furthermore, the DateTimeFormatter's timezone offset letter ('O') does - * not recognize UTC but recognizes GMT. According to - * https://en.wikipedia.org/wiki/Coordinated_Universal_Time, - * GMT only differs from UTC by at most 1 second and so substitution - * will only introduce a trivial amount of error. + * Furthermore, the DateTimeFormatter's timezone offset letter ('O') + * does not recognize UTC but recognizes GMT. According to + * https://en.wikipedia.org/wiki/Coordinated_Universal_Time, GMT only + * differs from UTC by at most 1 second and so substitution will only + * introduce a trivial amount of error. */ String reversedDateTimeWithGMT = reversedDateTime.replace("UTC", "GMT"); TemporalAccessor result = DATE_TIME_PARSER.parseBest(reversedDateTimeWithGMT, @@ -298,23 +300,20 @@ final class XRYCallsFileParser extends AbstractSingleKeyValueParser { LocalDateTime::from, OffsetDateTime::from); //Query for the ZoneID - if(result.query(TemporalQueries.zoneId()) == null) { + if (result.query(TemporalQueries.zoneId()) == null) { //If none, assumed GMT+0. - return ZonedDateTime.of(LocalDateTime.from(result), + return ZonedDateTime.of(LocalDateTime.from(result), ZoneId.of("GMT")).toEpochSecond(); } else { return Instant.from(result).getEpochSecond(); } } - + /** * Reverses the order of the date time components. - * - * Example: - * 1/3/1990 1:23:54 AM UTC+4 - * becomes - * UTC+4 AM 1:23:54 1/3/1990 - * + * + * Example: 1/3/1990 1:23:54 AM UTC+4 becomes UTC+4 AM 1:23:54 1/3/1990 + * * @param dateTime * @return */ diff --git a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYMessagesFileParser.java b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYMessagesFileParser.java index 37c2ecacc9..ed29c178a6 100755 --- a/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYMessagesFileParser.java +++ b/Core/src/org/sleuthkit/autopsy/datasourceprocessors/xry/XRYMessagesFileParser.java @@ -49,23 +49,23 @@ final class XRYMessagesFileParser implements XRYFileParser { private static final Logger logger = Logger.getLogger( XRYMessagesFileParser.class.getName()); - + private static final String PARSER_NAME = "XRY DSP"; - + //Pattern is in reverse due to a Java 8 bug, see calculateSecondsSinceEpoch() //function for more details. private static final DateTimeFormatter DATE_TIME_PARSER = DateTimeFormatter.ofPattern("[(XXX) ][O ][(O) ]a h:m:s M/d/y"); - + private static final String DEVICE_LOCALE = "(device)"; private static final String NETWORK_LOCALE = "(network)"; - + private static final int READ = 1; private static final int UNREAD = 0; - + /** - * All of the known XRY keys for message reports and the blackboard - * attribute types they map to. + * All of the known XRY keys for message reports and their corresponding + * blackboard attribute types, if any. */ private enum XryKey { DELETED("deleted", BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ISDELETED), @@ -86,28 +86,28 @@ final class XRYMessagesFileParser implements XRYFileParser { NAME("name", null), INDEX("index", null), STATUS("status", null); - + private final String name; private final BlackboardAttribute.ATTRIBUTE_TYPE type; - + XryKey(String name, BlackboardAttribute.ATTRIBUTE_TYPE type) { this.name = name; this.type = type; } - + public BlackboardAttribute.ATTRIBUTE_TYPE getType() { return type; } - + public String getDisplayName() { return name; } - + /** * Indicates if the display name of the XRY key is a recognized type. - * + * * @param xryKey - * @return + * @return */ public static boolean contains(String name) { try { @@ -117,30 +117,30 @@ final class XRYMessagesFileParser implements XRYFileParser { return false; } } - + /** * Matches the display name of the xry key to the appropriate enum type. - * - * It is assumed that XRY key string is recognized. Otherwise, - * an IllegalArgumentException is thrown. Test all membership - * with contains() before hand. - * + * + * It is assumed that XRY key string is recognized. Otherwise, an + * IllegalArgumentException is thrown. Test all membership with + * contains() before hand. + * * @param xryKey - * @return + * @return */ public static XryKey fromDisplayName(String name) { String normalizedName = name.trim().toLowerCase(); - for(XryKey keyChoice : XryKey.values()) { - if(normalizedName.equals(keyChoice.name)) { + for (XryKey keyChoice : XryKey.values()) { + if (normalizedName.equals(keyChoice.name)) { return keyChoice; } } - + throw new IllegalArgumentException(String.format("Key [ %s ] was not found." + " All keys should be tested with contains.", name)); } } - + /** * All of the known XRY namespaces for message reports. */ @@ -149,18 +149,19 @@ final class XRYMessagesFileParser implements XRYFileParser { PARTICIPANT("participant"), TO("to"), NONE(null); - + private final String name; - + XryNamespace(String name) { this.name = name; } - + /** - * Indicates if the display name of the XRY namespace is a recognized type. - * + * Indicates if the display name of the XRY namespace is a recognized + * type. + * * @param xryNamespace - * @return + * @return */ public static boolean contains(String xryNamespace) { try { @@ -170,30 +171,31 @@ final class XRYMessagesFileParser implements XRYFileParser { return false; } } - + /** - * Matches the display name of the xry namespace to the appropriate enum type. - * - * It is assumed that XRY namespace string is recognized. Otherwise, - * an IllegalArgumentException is thrown. Test all membership - * with contains() before hand. - * + * Matches the display name of the xry namespace to the appropriate enum + * type. + * + * It is assumed that XRY namespace string is recognized. Otherwise, an + * IllegalArgumentException is thrown. Test all membership with + * contains() before hand. + * * @param xryNamespace - * @return + * @return */ public static XryNamespace fromDisplayName(String xryNamespace) { String normalizedNamespace = xryNamespace.trim().toLowerCase(); - for(XryNamespace keyChoice : XryNamespace.values()) { - if(normalizedNamespace.equals(keyChoice.name)) { + for (XryNamespace keyChoice : XryNamespace.values()) { + if (normalizedNamespace.equals(keyChoice.name)) { return keyChoice; } } - + throw new IllegalArgumentException(String.format("Namespace [%s] was not found." + " All namespaces should be tested with contains.", xryNamespace)); } } - + /** * All known XRY meta keys for message reports. */ @@ -201,23 +203,22 @@ final class XRYMessagesFileParser implements XRYFileParser { REFERENCE_NUMBER("reference number"), SEGMENT_COUNT("segments"), SEGMENT_NUMBER("segment number"); - + private final String name; - + XryMetaKey(String name) { this.name = name; } - + public String getDisplayName() { return name; } - - + /** * Indicates if the display name of the XRY key is a recognized type. - * + * * @param xryKey - * @return + * @return */ public static boolean contains(String name) { try { @@ -227,25 +228,25 @@ final class XRYMessagesFileParser implements XRYFileParser { return false; } } - + /** * Matches the display name of the xry key to the appropriate enum type. - * - * It is assumed that XRY key string is recognized. Otherwise, - * an IllegalArgumentException is thrown. Test all membership - * with contains() before hand. - * + * + * It is assumed that XRY key string is recognized. Otherwise, an + * IllegalArgumentException is thrown. Test all membership with + * contains() before hand. + * * @param xryKey - * @return + * @return */ public static XryMetaKey fromDisplayName(String name) { String normalizedName = name.trim().toLowerCase(); - for(XryMetaKey keyChoice : XryMetaKey.values()) { - if(normalizedName.equals(keyChoice.name)) { + for (XryMetaKey keyChoice : XryMetaKey.values()) { + if (normalizedName.equals(keyChoice.name)) { return keyChoice; } } - + throw new IllegalArgumentException(String.format("Key [ %s ] was not found." + " All keys should be tested with contains.", name)); } @@ -253,11 +254,11 @@ final class XRYMessagesFileParser implements XRYFileParser { /** * Message-SMS report artifacts can span multiple XRY entities and their - * attributes can span multiple lines. The "Text" and "Message" keys are the only known key - * value pair that can span multiple lines. Messages can be segmented, - * meaning that their "Text" and "Message" content can appear in multiple XRY entities. - * Our goal for a segmented message is to aggregate all of the text pieces and - * create 1 artifact. + * attributes can span multiple lines. The "Text" and "Message" keys are the + * only known key value pair that can span multiple lines. Messages can be + * segmented, meaning that their "Text" and "Message" content can appear in + * multiple XRY entities. Our goal for a segmented message is to aggregate + * all of the text pieces and create 1 artifact. * * This parse implementation assumes that segments are contiguous and that * they ascend incrementally. There are checks in place to verify this @@ -283,31 +284,27 @@ final class XRYMessagesFileParser implements XRYFileParser { String xryEntity = reader.nextEntity(); List attributes = getBlackboardAttributes(xryEntity, reader, referenceNumbersSeen); //Only create artifacts with non-empty attributes. - if(!attributes.isEmpty()) { + if (!attributes.isEmpty()) { BlackboardArtifact artifact = parent.newArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE); artifact.addAttributes(attributes); } } } - + /** - * - * @param xryEntity - * @param reader - * @param referenceValues - * @return - * @throws IOException + * Extracts all blackboard attributes from the XRY Entity. This function will + * unify any segmented text, if need be. */ - private List getBlackboardAttributes(String xryEntity, + private List getBlackboardAttributes(String xryEntity, XRYFileReader reader, Set referenceValues) throws IOException { String[] xryLines = xryEntity.split("\n"); //First line of the entity is the title, each XRY entity is non-empty. logger.log(Level.INFO, String.format("[XRY DSP] Processing [ %s ]", xryLines[0])); - + List attributes = new ArrayList<>(); - + //Count the key value pairs in the XRY entity. - int keyCount = countKeys(xryLines); + int keyCount = getCountOfKeyValuePairs(xryLines); for (int i = 1; i <= keyCount; i++) { //Get the ith key value pair in the entity. Always expect to have //a valid value. @@ -333,10 +330,10 @@ final class XRYMessagesFileParser implements XRYFileParser { //Assume text and message are the only fields that can be segmented //among multiple XRY entities. - if (pair.hasKey(XryKey.TEXT.getDisplayName()) || - pair.hasKey(XryKey.MESSAGE.getDisplayName())) { - String segmentedText = getSegmentedText(xryLines, reader, referenceValues); - pair = new XRYKeyValuePair(pair.getKey(), + if (pair.hasKey(XryKey.TEXT.getDisplayName()) + || pair.hasKey(XryKey.MESSAGE.getDisplayName())) { + String segmentedText = getSegmentedText(xryLines, reader, referenceValues); + pair = new XRYKeyValuePair(pair.getKey(), //Assume text is segmented by word. pair.getValue() + " " + segmentedText, pair.getNamespace()); @@ -348,18 +345,18 @@ final class XRYMessagesFileParser implements XRYFileParser { attributes.add(attribute.get()); } } - + return attributes; } - + /** - * Counts the key value pairs in an XRY entity. - * Skips counting the first line as it is assumed to be the title. + * Counts the key value pairs in an XRY entity. Skips counting the first + * line as it is assumed to be the title. */ - private Integer countKeys(String[] xryEntity) { + private Integer getCountOfKeyValuePairs(String[] xryEntity) { int count = 0; for (int i = 1; i < xryEntity.length; i++) { - if(XRYKeyValuePair.isPair(xryEntity[i])) { + if (XRYKeyValuePair.isPair(xryEntity[i])) { count++; } } @@ -367,23 +364,23 @@ final class XRYMessagesFileParser implements XRYFileParser { } /** - * Builds up segmented message entities so that the text is unified in the - * artifact. - * - * @param referenceNumber Reference number that messages are group by - * @param segmentNumber Segment number of the starting segment. - * @param reader + * Builds up segmented message entities so that the text is unified for a + * single artifact. + * + * @param reader File reader that is producing XRY entities. + * @param referenceNumbersSeen All known references numbers up until this point. + * @param xryEntity The source XRY entity. * @return * @throws IOException */ - private String getSegmentedText(String[] xryEntity, XRYFileReader reader, + private String getSegmentedText(String[] xryEntity, XRYFileReader reader, Set referenceNumbersSeen) throws IOException { Optional referenceNumber = getMetaKeyValue(xryEntity, XryMetaKey.REFERENCE_NUMBER); //Check if there is any segmented text. - if(!referenceNumber.isPresent()) { + if (!referenceNumber.isPresent()) { return ""; } - + logger.log(Level.INFO, String.format("[XRY DSP] Message entity " + "appears to be segmented with reference number [ %d ]", referenceNumber.get())); @@ -398,13 +395,13 @@ final class XRYMessagesFileParser implements XRYFileParser { referenceNumbersSeen.add(referenceNumber.get()); Optional segmentNumber = getMetaKeyValue(xryEntity, XryMetaKey.SEGMENT_NUMBER); - if(!segmentNumber.isPresent()) { + if (!segmentNumber.isPresent()) { logger.log(Level.SEVERE, String.format("No segment " + "number was found on the message entity" + "with reference number [%d]", referenceNumber.get())); return ""; } - + StringBuilder segmentedText = new StringBuilder(); int currentSegmentNumber = segmentNumber.get(); @@ -414,8 +411,8 @@ final class XRYMessagesFileParser implements XRYFileParser { String[] nextEntityLines = nextEntity.split("\n"); Optional nextReferenceNumber = getMetaKeyValue(nextEntityLines, XryMetaKey.REFERENCE_NUMBER); - if (!nextReferenceNumber.isPresent() || - !Objects.equals(nextReferenceNumber, referenceNumber)) { + if (!nextReferenceNumber.isPresent() + || !Objects.equals(nextReferenceNumber, referenceNumber)) { //Don't consume the next entity. It is not related //to the current message thread. break; @@ -429,7 +426,7 @@ final class XRYMessagesFileParser implements XRYFileParser { logger.log(Level.INFO, String.format("[XRY DSP] Processing [ %s ] " + "segment with reference number [ %d ]", nextEntityLines[0], referenceNumber.get())); - if(!nextSegmentNumber.isPresent()) { + if (!nextSegmentNumber.isPresent()) { logger.log(Level.SEVERE, String.format("[XRY DSP] Segment with reference" + " number [ %d ] did not have a segment number associated with it." + " It cannot be determined if the reconstructed text will be in order.", referenceNumber.get())); @@ -440,16 +437,16 @@ final class XRYMessagesFileParser implements XRYFileParser { + "text will be out of order.", nextSegmentNumber.get(), currentSegmentNumber)); } - int keyCount = countKeys(nextEntityLines); + int keyCount = getCountOfKeyValuePairs(nextEntityLines); for (int i = 1; i <= keyCount; i++) { XRYKeyValuePair pair = getKeyValuePairByIndex(nextEntityLines, i).get(); - if(pair.hasKey(XryKey.TEXT.getDisplayName()) || - pair.hasKey(XryKey.MESSAGE.getDisplayName())) { + if (pair.hasKey(XryKey.TEXT.getDisplayName()) + || pair.hasKey(XryKey.MESSAGE.getDisplayName())) { segmentedText.append(pair.getValue()).append(' '); } } - if(nextSegmentNumber.isPresent()) { + if (nextSegmentNumber.isPresent()) { currentSegmentNumber = nextSegmentNumber.get(); } } @@ -458,24 +455,25 @@ final class XRYMessagesFileParser implements XRYFileParser { if (segmentedText.length() > 0) { segmentedText.setLength(segmentedText.length() - 1); } - + return segmentedText.toString(); } - + /** + * Extracts the value of the XRY meta key, if any. * - * @param xryLines - * @param metaKey - * @return + * @param xryLines XRY entity to extract from. + * @param metaKey The key type to extract. + * @return */ private Optional getMetaKeyValue(String[] xryLines, XryMetaKey metaKey) { for (String xryLine : xryLines) { if (!XRYKeyValuePair.isPair(xryLine)) { continue; } - + XRYKeyValuePair pair = XRYKeyValuePair.from(xryLine); - if(pair.hasKey(metaKey.getDisplayName())) { + if (pair.hasKey(metaKey.getDisplayName())) { try { return Optional.of(Integer.parseInt(pair.getValue())); } catch (NumberFormatException ex) { @@ -488,42 +486,45 @@ final class XRYMessagesFileParser implements XRYFileParser { } /** + * Extracts the ith XRY Key Value pair in the XRY Entity. * - * @param xryLines - * @param index - * @return + * The total number of pairs can be determined via getCountOfKeyValuePairs(). + * + * @param xryLines XRY entity. + * @param index The requested Key Value pair. + * @return */ private Optional getKeyValuePairByIndex(String[] xryLines, int index) { int pairsParsed = 0; String namespace = ""; for (int i = 1; i < xryLines.length; i++) { String xryLine = xryLines[i]; - if(XryNamespace.contains(xryLine)) { + if (XryNamespace.contains(xryLine)) { namespace = xryLine.trim(); continue; } - - if(!XRYKeyValuePair.isPair(xryLine)) { + + if (!XRYKeyValuePair.isPair(xryLine)) { logger.log(Level.SEVERE, String.format("[XRY DSP] Expected a key value " - + "pair on this line (in brackets) [ %s ], but one was not detected." - + " Discarding...", xryLine)); + + "pair on this line (in brackets) [ %s ], but one was not detected." + + " Discarding...", xryLine)); continue; } - + XRYKeyValuePair pair = XRYKeyValuePair.from(xryLine); String value = pair.getValue(); //Build up multiple lines. - for (; (i+1) < xryLines.length - && !XRYKeyValuePair.isPair(xryLines[i+1]) - && !XryNamespace.contains(xryLines[i+1]); i++) { - String continuedValue = xryLines[i+1].trim(); + for (; (i + 1) < xryLines.length + && !XRYKeyValuePair.isPair(xryLines[i + 1]) + && !XryNamespace.contains(xryLines[i + 1]); i++) { + String continuedValue = xryLines[i + 1].trim(); //Assume multi lined values are split by word. value = value + " " + continuedValue; } pair = new XRYKeyValuePair(pair.getKey(), value, namespace); pairsParsed++; - if(pairsParsed == index) { + if (pairsParsed == index) { return Optional.of(pair); } } @@ -533,21 +534,21 @@ final class XRYMessagesFileParser implements XRYFileParser { /** * Creates an attribute from the extracted key value pair. - * - * @param nameSpace The namespace of this key value pair. - * It will have been verified beforehand, otherwise it will be NONE. + * + * @param nameSpace The namespace of this key value pair. It will have been + * verified beforehand, otherwise it will be NONE. * @param key The recognized XRY key. * @param value The value associated with that key. * @return Corresponding blackboard attribute, if any. */ private Optional getBlackboardAttribute(XRYKeyValuePair pair) { XryNamespace namespace = XryNamespace.NONE; - if(XryNamespace.contains(pair.getNamespace())) { + if (XryNamespace.contains(pair.getNamespace())) { namespace = XryNamespace.fromDisplayName(pair.getNamespace()); } XryKey key = XryKey.fromDisplayName(pair.getKey()); String normalizedValue = pair.getValue().toLowerCase().trim(); - + switch (key) { case TEL: case NUMBER: @@ -565,13 +566,13 @@ final class XRYMessagesFileParser implements XRYFileParser { return Optional.of(new BlackboardAttribute( BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER, PARSER_NAME, pair.getValue())); - } + } case TIME: try { //Tranform value to seconds since epoch long dateTimeSinceInEpoch = calculateSecondsSinceEpoch(pair.getValue()); return Optional.of(new BlackboardAttribute( - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START, PARSER_NAME, dateTimeSinceInEpoch)); } catch (DateTimeParseException ex) { logger.log(Level.WARNING, String.format("[XRY DSP] Assumption" @@ -584,8 +585,8 @@ final class XRYMessagesFileParser implements XRYFileParser { case "incoming": case "outgoing": return Optional.of(new BlackboardAttribute( - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, - PARSER_NAME, pair.getValue())); + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DIRECTION, + PARSER_NAME, pair.getValue())); case "deliver": case "submit": case "status report": @@ -600,11 +601,11 @@ final class XRYMessagesFileParser implements XRYFileParser { switch (normalizedValue) { case "read": return Optional.of(new BlackboardAttribute( - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS, PARSER_NAME, READ)); case "unread": return Optional.of(new BlackboardAttribute( - BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS, + BlackboardAttribute.ATTRIBUTE_TYPE.TSK_READ_STATUS, PARSER_NAME, UNREAD)); case "sending failed": case "deleted": @@ -620,15 +621,15 @@ final class XRYMessagesFileParser implements XRYFileParser { default: //Otherwise, the XryKey enum contains the correct BlackboardAttribute //type. - if(key.getType() != null) { - return Optional.of(new BlackboardAttribute(key.getType(), - PARSER_NAME, pair.getValue())); + if (key.getType() != null) { + return Optional.of(new BlackboardAttribute(key.getType(), + PARSER_NAME, pair.getValue())); } - + logger.log(Level.WARNING, String.format("[XRY DSP] Key value pair " - + "(in brackets) [ %s ] was recognized but " - + "more data or time is needed to finish implementation. Discarding... ", pair)); - + + "(in brackets) [ %s ] was recognized but " + + "more data or time is needed to finish implementation. Discarding... ", pair)); + return Optional.empty(); } } @@ -648,7 +649,7 @@ final class XRYMessagesFileParser implements XRYFileParser { result = result.substring(0, deviceIndex); } int networkIndex = result.toLowerCase().indexOf(NETWORK_LOCALE); - if(networkIndex != -1) { + if (networkIndex != -1) { result = result.substring(0, networkIndex); } return result; @@ -664,28 +665,27 @@ final class XRYMessagesFileParser implements XRYFileParser { String dateTimeWithoutLocale = removeDateTimeLocale(dateTime).trim(); /** * The format of time in XRY Messages reports is of the form: - * - * 1/3/1990 1:23:54 AM UTC+4 - * - * In our current version of Java (openjdk-1.8.0.222), there is - * a bug with having the timezone offset (UTC+4 or GMT-7) at the - * end of the date time input. This is fixed in later versions - * of the JDK (9 and beyond). - * https://bugs.openjdk.java.net/browse/JDK-8154050 - * Rather than update the JDK to accommodate this, the components of - * the date time string are reversed: - * - * UTC+4 AM 1:23:54 1/3/1990 - * + * + * 1/3/1990 1:23:54 AM UTC+4 + * + * In our current version of Java (openjdk-1.8.0.222), there is a bug + * with having the timezone offset (UTC+4 or GMT-7) at the end of the + * date time input. This is fixed in later versions of the JDK (9 and + * beyond). https://bugs.openjdk.java.net/browse/JDK-8154050 Rather than + * update the JDK to accommodate this, the components of the date time + * string are reversed: + * + * UTC+4 AM 1:23:54 1/3/1990 + * * The java time package will correctly parse this date time format. */ String reversedDateTime = reverseOrderOfDateTimeComponents(dateTimeWithoutLocale); /** - * Furthermore, the DateTimeFormatter's timezone offset letter ('O') does - * not recognize UTC but recognizes GMT. According to - * https://en.wikipedia.org/wiki/Coordinated_Universal_Time, - * GMT only differs from UTC by at most 1 second and so substitution - * will only introduce a trivial amount of error. + * Furthermore, the DateTimeFormatter's timezone offset letter ('O') + * does not recognize UTC but recognizes GMT. According to + * https://en.wikipedia.org/wiki/Coordinated_Universal_Time, GMT only + * differs from UTC by at most 1 second and so substitution will only + * introduce a trivial amount of error. */ String reversedDateTimeWithGMT = reversedDateTime.replace("UTC", "GMT"); TemporalAccessor result = DATE_TIME_PARSER.parseBest(reversedDateTimeWithGMT, @@ -693,23 +693,20 @@ final class XRYMessagesFileParser implements XRYFileParser { LocalDateTime::from, OffsetDateTime::from); //Query for the ZoneID - if(result.query(TemporalQueries.zoneId()) == null) { + if (result.query(TemporalQueries.zoneId()) == null) { //If none, assumed GMT+0. - return ZonedDateTime.of(LocalDateTime.from(result), + return ZonedDateTime.of(LocalDateTime.from(result), ZoneId.of("GMT")).toEpochSecond(); } else { return Instant.from(result).getEpochSecond(); } } - + /** * Reverses the order of the date time components. - * - * Example: - * 1/3/1990 1:23:54 AM UTC+4 - * becomes - * UTC+4 AM 1:23:54 1/3/1990 - * + * + * Example: 1/3/1990 1:23:54 AM UTC+4 becomes UTC+4 AM 1:23:54 1/3/1990 + * * @param dateTime * @return */