From 48a04cd3a9898e04f60714b3fc14ae8401c789c1 Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Fri, 4 Oct 2019 11:24:53 -0400 Subject: [PATCH 01/11] 5560: add support for FB messenger call logs. --- InternalPythonModules/android/fbmessenger.py | 343 +++++++++++++------ 1 file changed, 234 insertions(+), 109 deletions(-) diff --git a/InternalPythonModules/android/fbmessenger.py b/InternalPythonModules/android/fbmessenger.py index 3a678f16d0..e3c27d1765 100644 --- a/InternalPythonModules/android/fbmessenger.py +++ b/InternalPythonModules/android/fbmessenger.py @@ -45,6 +45,7 @@ from org.sleuthkit.datamodel import Account from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection +from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CallMediaType import json import traceback @@ -181,15 +182,240 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): contactsDb.close() - - ## Adds a recipient to given list + ## Extracts recipeint id from 'user_key' column and adds recipient to given list. def addRecipientToList(self, user_key, recipientList): if user_key is not None: recipientId = user_key.replace('FACEBOOK:', '') recipientList.append(recipientId) + + + ## Extracts sender id from the json in 'sender' column. + def getSenderIdFromJson(self, senderJsonStr): + senderId = None; + if senderJsonStr is not None: + sender_dict = json.loads(senderJsonStr) + senderId = sender_dict['user_key'] + senderId = senderId.replace('FACEBOOK:', '') - ## Analyze messages - def analyzeMessages(self, dataSource, fileManager, context): + return senderId + + ## determines communication direction by comparing senderId with selfAccountId + def deduceDirectionFromSenderId(self, senderId): + direction = CommunicationDirection.UNKNOWN + if senderId is not None: + if senderId == self.selfAccountId: + direction = CommunicationDirection.OUTGOING + else: + direction = CommunicationDirection.INCOMING + return direction + + ## Analyzes messages + def analyzeMessages(self, threadsDb, threadsDBHelper): + try: + + ## Messages are found in the messages table. + ## This query filters messages by msg_type to only get actual user created conversation messages (msg_type 0). + ## The participant ids can be found in the thread_participants table. + ## Participant names are found in thread_users table. + ## Joining these tables produces multiple rows per message, one row for each recipient. + ## The result set is processed to collect the multiple recipients for a given message. + sqlString = """ + SELECT msg_id, text, sender, timestamp_ms, msg_type, messages.thread_key as thread_key, + snippet, thread_participants.user_key as user_key, thread_users.name as name + FROM messages + JOIN thread_participants ON messages.thread_key = thread_participants.thread_key + JOIN thread_users ON thread_participants.user_key = thread_users.user_key + WHERE msg_type = 0 + ORDER BY msg_id + """ + + messagesResultSet = threadsDb.runQuery(sqlString) + if messagesResultSet is not None: + oldMsgId = None + + direction = CommunicationDirection.UNKNOWN + fromId = None + recipientIdsList = None + timeStamp = -1 + msgText = "" + threadId = "" + + while messagesResultSet.next(): + msgId = messagesResultSet.getString("msg_id") + + # new msg begins when msgId changes + if msgId != oldMsgId: + # Create message artifact with collected attributes + if oldMsgId is not None: + messageArtifact = threadsDBHelper.addMessage( + self._MESSAGE_TYPE, + direction, + fromId, + recipientIdsList, + timeStamp, + MessageReadStatus.UNKNOWN, + "", # subject + msgText, + threadId) + + oldMsgId = msgId + + # New message - collect all attributes + recipientIdsList = [] + + ## get sender id by parsing JSON in sender column + fromId = self.getSenderIdFromJson(messagesResultSet.getString("sender")) + direction = self.deduceDirectionFromSenderId(fromId) + + # Get recipient and add to list + self.addRecipientToList(messagesResultSet.getString("user_key"), + recipientIdsList) + + timeStamp = messagesResultSet.getLong("timestamp_ms") / 1000 + + # Get msg text + # Sometimes there may not be an explict msg text, + # but an app generated snippet instead + msgText = messagesResultSet.getString("text") + if not msgText: + msgText = messagesResultSet.getString("snippet") + + # TBD: get attachment + + threadId = messagesResultSet.getString("thread_key") + + else: # same msgId as last, just collect recipient from current row + self.addRecipientToList(messagesResultSet.getString("user_key"), + recipientIdsList) + + + # at the end of the loop, add last message + messageArtifact = threadsDBHelper.addMessage( + self._MESSAGE_TYPE, + direction, + fromId, + recipientIdsList, + timeStamp, + MessageReadStatus.UNKNOWN, + "", # subject + msgText, + threadId) + + except SQLException as ex: + self._logger.log(Level.WARNING, "Error processing query result for FB Messenger messages.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Failed to add FB Messenger message artifacts.", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except BlackboardException as ex: + self._logger.log(Level.WARNING, "Failed to post artifacts.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + + ## Analyzes call logs + def analyzeCallLogs(self, threadsDb, threadsDBHelper): + try: + + ## Call logs are found in the messages table. + ## msg_type indicates type of call: + ## 9: one to one calls + ## 203: group call + ## 1-to-1 calls only have a call_ended record. + ## group calls have a call_started_record as well as call_ended recorded, with *different* message ids. + ## all the data we need can be found in the call_ended record. + + sqlString = """ + SELECT msg_id, text, sender, timestamp_ms, msg_type, admin_text_thread_rtc_event, + generic_admin_message_extensible_data, + messages.thread_key as thread_key, + thread_participants.user_key as user_key, + thread_users.name as name + FROM messages + JOIN thread_participants ON messages.thread_key = thread_participants.thread_key + JOIN thread_users ON thread_participants.user_key = thread_users.user_key + WHERE msg_type = 9 OR (msg_type = 203 AND admin_text_thread_rtc_event = 'group_call_ended') + ORDER BY msg_id + """ + + messagesResultSet = threadsDb.runQuery(sqlString) + if messagesResultSet is not None: + oldMsgId = None + + direction = CommunicationDirection.UNKNOWN + callerId = None + calleeIdsList = None + startTimeStamp = -1 + endTimeStamp = -1 + duration = 0 + mediaType = CallMediaType.AUDIO + + while messagesResultSet.next(): + msgId = messagesResultSet.getString("msg_id") + + # new call begins when msgId changes + if msgId != oldMsgId: + # Create call log artifact with collected attributes + if oldMsgId is not None: + messageArtifact = threadsDBHelper.addCalllog( + direction, + callerId, + calleeIdsList, + startTimeStamp, + endTimeStamp, + mediaType ) + + oldMsgId = msgId + + # New message - collect all attributes + calleeIdsList = [] + + ## get caller id by parsing JSON in sender column + callerId = self.getSenderIdFromJson(messagesResultSet.getString("sender")) + direction = self.deduceDirectionFromSenderId(callerId) + + # Get recipient and add to list + self.addRecipientToList(messagesResultSet.getString("user_key"), + calleeIdsList) + + # the timestamp from call ended msg is used as end timestamp + endTimeStamp = messagesResultSet.getLong("timestamp_ms") / 1000 + + # parse the generic_admin_message_extensible_data JSON to extract the duration and video fields + adminDataJsonStr = messagesResultSet.getString("generic_admin_message_extensible_data") + if adminDataJsonStr is not None: + adminData_dict = json.loads(adminDataJsonStr) + duration = adminData_dict['call_duration'] # call duration in seconds + isVideo = adminData_dict['video'] + if isVideo: + mediaType = CallMediaType.VIDEO + + startTimeStamp = endTimeStamp - duration + + else: # same msgId as last, just collect callee from current row + self.addRecipientToList(messagesResultSet.getString("user_key"), + calleeIdsList) + + # at the end of the loop, add last message + messageArtifact = threadsDBHelper.addCalllog( + direction, + callerId, + calleeIdsList, + startTimeStamp, + endTimeStamp, + mediaType ) + + except SQLException as ex: + self._logger.log(Level.WARNING, "Error processing query result for FB Messenger call logs.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + except TskCoreException as ex: + self._logger.log(Level.SEVERE, "Failed to add FB Messenger call log artifacts.", ex) + self._logger.log(Level.SEVERE, traceback.format_exc()) + except BlackboardException as ex: + self._logger.log(Level.WARNING, "Failed to post FB Messenger call log artifacts.", ex) + self._logger.log(Level.WARNING, traceback.format_exc()) + + + ## Analyze messages and call log threads + def analyzeMessagesAndCallLogs(self, dataSource, fileManager, context): threadsDbs = AppSQLiteDB.findAppDatabases(dataSource, "threads_db2", True, self._FB_MESSENGER_PACKAGE_NAME) for threadsDb in threadsDbs: try: @@ -202,113 +428,12 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): self._MODULE_NAME, threadsDb.getDBFile(), Account.Type.FACEBOOK) - ## Messages are found in the messages table. - ## This query filters messages by msg_type to only get actual user created conversation messages (msg_type 0). - ## The participant ids can be found in the thread_participants table. - ## Participant names are found in thread_users table. - ## Joining these tables produces multiple rows per message, one row for each recipient. - ## The result set is processed to collect the multiple recipients for a given message. - sqlString = """ - SELECT msg_id, text, sender, timestamp_ms, msg_type, messages.thread_key as thread_key, - snippet, thread_participants.user_key as user_key, thread_users.name as name - FROM messages - JOIN thread_participants ON messages.thread_key = thread_participants.thread_key - JOIN thread_users ON thread_participants.user_key = thread_users.user_key - WHERE msg_type = 0 - ORDER BY msg_id - """ - - messagesResultSet = threadsDb.runQuery(sqlString) - if messagesResultSet is not None: - oldMsgId = None - - direction = CommunicationDirection.UNKNOWN - fromId = None - recipientIdsList = None - timeStamp = -1 - msgText = "" - threadId = "" - - while messagesResultSet.next(): - msgId = messagesResultSet.getString("msg_id") - - # new msg begins when msgId changes - if msgId != oldMsgId: - # Create message artifact with collected attributes - if oldMsgId is not None: - messageArtifact = threadsDBHelper.addMessage( - self._MESSAGE_TYPE, - direction, - fromId, - recipientIdsList, - timeStamp, - MessageReadStatus.UNKNOWN, - "", # subject - msgText, - threadId) - - oldMsgId = msgId - - # New message - collect all attributes - recipientIdsList = [] - - ## get sender id by parsing JSON in sender column - senderJsonStr = messagesResultSet.getString("sender") - if senderJsonStr is not None: - sender_dict = json.loads(senderJsonStr) - senderId = sender_dict['user_key'] - senderId = senderId.replace('FACEBOOK:', '') - senderName = sender_dict['name'] - fromId = senderId - if senderId == self.selfAccountId: - direction = CommunicationDirection.OUTGOING - else: - direction = CommunicationDirection.INCOMING - - - # Get recipient and add to list - self.addRecipientToList(messagesResultSet.getString("user_key"), - recipientIdsList) - - timeStamp = messagesResultSet.getLong("timestamp_ms") / 1000 - - # Get msg text - # Sometimes there may not be an explict msg text, - # but an app generated snippet instead - msgText = messagesResultSet.getString("text") - if not msgText: - msgText = messagesResultSet.getString("snippet") - - # TBD: get attachment - - threadId = messagesResultSet.getString("thread_key") - - else: # same msgId as last, just collect recipient from current row - self.addRecipientToList(messagesResultSet.getString("user_key"), - recipientIdsList) - - - # at the end of the loop, add last message - messageArtifact = threadsDBHelper.addMessage( - self._MESSAGE_TYPE, - direction, - fromId, - recipientIdsList, - timeStamp, - MessageReadStatus.UNKNOWN, - "", # subject - msgText, - threadId) + self.analyzeMessages(threadsDb, threadsDBHelper) + self.analyzeCallLogs(threadsDb, threadsDBHelper) - except SQLException as ex: - self._logger.log(Level.WARNING, "Error processing query result for FB Messenger messages.", ex) - self._logger.log(Level.WARNING, traceback.format_exc()) except TskCoreException as ex: - self._logger.log(Level.SEVERE, "Failed to add FB Messenger message artifacts.", ex) + self._logger.log(Level.SEVERE, "Failed to to create CommunicationArtifactsHelper for FB Messenger.", ex) self._logger.log(Level.SEVERE, traceback.format_exc()) - except BlackboardException as ex: - self._logger.log(Level.WARNING, "Failed to post artifacts.", ex) - self._logger.log(Level.WARNING, traceback.format_exc()) finally: threadsDb.close() @@ -321,6 +446,6 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): return self.analyzeContacts(dataSource, fileManager, context) - self.analyzeMessages(dataSource, fileManager, context) + self.analyzeMessagesAndCallLogs(dataSource, fileManager, context) From af56179eba6e2e8ab4f960cd47ee3c9ba3b73195 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 30 Oct 2019 13:23:05 -0400 Subject: [PATCH 02/11] Added support for Mac OS and addressed issue with Travis Mac OS builds failing silently when 'set -e' command is used in the .travis.yml file by moving the script into travis_build.sh. --- .travis.yml | 27 +++++++++++++++++++++------ travis_build.sh | 12 ++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 travis_build.sh diff --git a/.travis.yml b/.travis.yml index 56c8b7bbbd..5e7c79a9c0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,11 @@ language: java sudo: required -dist: bionic -os: - - linux + +jobs: + include: + - os: linux + dist bionic + - os: osx env: global: @@ -12,6 +15,7 @@ addons: apt: update: true packages: + - testdisk - libafflib-dev - libewf-dev - libpq-dev @@ -29,11 +33,13 @@ addons: update: true packages: - ant - - ant-optional + - wget + - libpq - libewf - gettext - cppunit - afflib + - testdisk python: - "2.7" @@ -43,7 +49,6 @@ before_install: - python setupSleuthkitBranch.py install: - - sudo apt-get install testdisk - cd sleuthkit/sleuthkit - ./travis_install_libs.sh @@ -54,12 +59,22 @@ before_script: export PATH=/usr/bin:$PATH; unset JAVA_HOME; fi + - if [ $TRAVIS_OS_NAME = osx ]; then + brew uninstall java --force; + brew cask uninstall java --force; + brew tap adoptopenjdk/openjdk; + brew cask install adoptopenjdk8; + wget https://cdn.azul.com/zulu/bin/zulu8.40.0.25-ca-fx-jdk8.0.222-macosx_x64.tar.gz; + sudo tar xf zulu8.40.0.25-ca-fx-jdk8.0.222-macosx_x64.tar.gz --strip-components=1 -C /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home; + export JAVA_HOME=/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home; + fi + - java -version script: - set -e - echo "Building TSK..." - ./bootstrap && ./configure --prefix=/usr && make - - pushd bindings/java/ && ant -q dist-PostgreSQL && popd + - pushd bindings/java && ant dist-PostgreSQL && popd - echo "Building Autopsy..." && echo -en 'travis_fold:start:script.build\\r' - cd $TRAVIS_BUILD_DIR/ - ant build diff --git a/travis_build.sh b/travis_build.sh new file mode 100644 index 0000000000..d25d52d6ae --- /dev/null +++ b/travis_build.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e +echo "Building TSK..." +cd sleuthkit/sleuthkit +./bootstrap && configure --prefix=/usr && make +pushd bindings/java && ant -q dist-PostgreSQL && popd + +echo "Building Autopsy..." && echo -en 'travis_fold:start:script.build\\r' +cd $TRAVIS_BUILD_DIR/ +ant build +echo -en 'travis_fold:end:script.build\\r' From af3f20db9504b6d6ef5867e8925e874016fc62c7 Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 30 Oct 2019 13:25:21 -0400 Subject: [PATCH 03/11] Forgot to add updated .travis.yml file on previous commit. --- .travis.yml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5e7c79a9c0..be8df28a5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,8 +49,7 @@ before_install: - python setupSleuthkitBranch.py install: - - cd sleuthkit/sleuthkit - - ./travis_install_libs.sh + - pushd sleuthkit/sleuthkit && ./travis_install_libs.sh && popd before_script: - if [ $TRAVIS_OS_NAME = linux ]; then @@ -70,12 +69,4 @@ before_script: fi - java -version -script: - - set -e - - echo "Building TSK..." - - ./bootstrap && ./configure --prefix=/usr && make - - pushd bindings/java && ant dist-PostgreSQL && popd - - echo "Building Autopsy..." && echo -en 'travis_fold:start:script.build\\r' - - cd $TRAVIS_BUILD_DIR/ - - ant build - - echo -en 'travis_fold:end:script.build\\r' +script: ./travis_build.sh From 8ec2f2b28160148616ccd9b31e32c28e2b2c17cd Mon Sep 17 00:00:00 2001 From: esaunders Date: Wed, 30 Oct 2019 14:39:03 -0400 Subject: [PATCH 04/11] Missed a : --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index be8df28a5d..b907afbd81 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ sudo: required jobs: include: - os: linux - dist bionic + dist: bionic - os: osx env: From cccbde6452a041dc5b0bc669e88edfa42b259efc Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Thu, 31 Oct 2019 13:50:16 -0400 Subject: [PATCH 05/11] 5706: Message attachments. --- .../DataContentViewerArtifact.java | 14 +++++++ .../datamodel/ArtifactStringContent.java | 1 + .../PortableCaseReportModule.java | 1 + .../autopsy/test/CustomArtifactType.java | 4 ++ .../autoingest/FileExporterSettingsPanel.java | 2 + InternalPythonModules/android/imo.py | 38 ++++++++++++++++--- 6 files changed, 55 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java index 39d3203b13..8c8e722bce 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java @@ -56,6 +56,10 @@ import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; import org.netbeans.swing.etable.ETable; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; /** * Instances of this class display the BlackboardArtifacts associated with the @@ -552,6 +556,16 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat value = dateFormatter.format(new java.util.Date(epoch * 1000)); } break; + case JSON: + // @TODO: 5726 - return a multilevel bulleted list instead of prettyprint JSON + String jsonVal = attr.getValueString(); + + JsonParser parser = new JsonParser(); + JsonObject json = parser.parse(jsonVal).getAsJsonObject(); + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + value = gson.toJson(json); + break; } /* * Attribute sources column. diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java index 774d1f2f9e..e2950e5b12 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java @@ -122,6 +122,7 @@ public class ArtifactStringContent implements StringContent { case LONG: case DOUBLE: case BYTE: + case JSON: default: value = attr.getDisplayString(); break; diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java b/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java index de3350ad88..38e53b5eb6 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/portablecase/PortableCaseReportModule.java @@ -768,6 +768,7 @@ public class PortableCaseReportModule implements ReportModule { oldAttr.getValueLong())); break; case STRING: + case JSON: newAttrs.add(new BlackboardAttribute(newAttributeType, String.join(",", oldAttr.getSources()), oldAttr.getValueString())); break; diff --git a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java index b27fa29dd6..49784b46a7 100644 --- a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java +++ b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java @@ -50,6 +50,8 @@ final class CustomArtifactType { private static final String BYTES_ATTR_DISPLAY_NAME = "Custom Bytes"; private static final String STRING_ATTR_TYPE_NAME = "CUSTOM_STRING_ATTRIBUTE"; private static final String STRING_ATTR_DISPLAY_NAME = "Custom String"; + private static final String JSON_ATTR_TYPE_NAME = "CUSTOM_JSON_ATTRIBUTE"; + private static final String JSON_ATTR_DISPLAY_NAME = "Custom Json"; private static BlackboardArtifact.Type artifactType; private static BlackboardAttribute.Type intAttrType; private static BlackboardAttribute.Type doubleAttrType; @@ -57,6 +59,7 @@ final class CustomArtifactType { private static BlackboardAttribute.Type dateTimeAttrType; private static BlackboardAttribute.Type bytesAttrType; private static BlackboardAttribute.Type stringAttrType; + private static BlackboardAttribute.Type jsonAttrType; /** * Adds the custom artifact type, with its associated custom attribute @@ -73,6 +76,7 @@ final class CustomArtifactType { dateTimeAttrType = blackboard.getOrAddAttributeType(DATETIME_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME, DATETIME_ATTR_DISPLAY_NAME); bytesAttrType = blackboard.getOrAddAttributeType(BYTES_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE, BYTES_ATTR_DISPLAY_NAME); stringAttrType = blackboard.getOrAddAttributeType(STRING_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING, STRING_ATTR_DISPLAY_NAME); + jsonAttrType = blackboard.getOrAddAttributeType(JSON_ATTR_TYPE_NAME, BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON, JSON_ATTR_DISPLAY_NAME); } /** diff --git a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java index 517c1e46a6..001ad7271d 100644 --- a/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java +++ b/Experimental/src/org/sleuthkit/autopsy/experimental/autoingest/FileExporterSettingsPanel.java @@ -244,6 +244,8 @@ public final class FileExporterSettingsPanel extends JPanel { comboBoxValueType.addItem(BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.STRING.getLabel()); comboBoxValueType.addItem(BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME.getLabel()); comboBoxValueType.addItem(BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.BYTE.getLabel()); + comboBoxValueType.addItem(BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.JSON.getLabel()); + comboBoxValueType.addItem(UNSET); load(); diff --git a/InternalPythonModules/android/imo.py b/InternalPythonModules/android/imo.py index 25dd750c1e..5a1bd89778 100644 --- a/InternalPythonModules/android/imo.py +++ b/InternalPythonModules/android/imo.py @@ -43,10 +43,14 @@ from org.sleuthkit.datamodel import Content from org.sleuthkit.datamodel import TskCoreException from org.sleuthkit.datamodel.Blackboard import BlackboardException from org.sleuthkit.datamodel import Account +from org.sleuthkit.datamodel.blackboardutils import FileAttachment +from org.sleuthkit.datamodel.blackboardutils import URLAttachment +from org.sleuthkit.datamodel.blackboardutils import MessageAttachments from org.sleuthkit.datamodel.blackboardutils import CommunicationArtifactsHelper from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import MessageReadStatus from org.sleuthkit.datamodel.blackboardutils.CommunicationArtifactsHelper import CommunicationDirection +import json import traceback import general @@ -66,6 +70,8 @@ class IMOAnalyzer(general.AndroidComponentAnalyzer): -- A messages table which stores the message details --- sender/receiver buid, timestamp, message_type (1: incoming, 0: outgoing), message_read... --- 'imdata' column stores a json structure with all the message details, including attachments + ---- attachment file path may be specified in local_path or original_path. Original path, if available is a better candidate. + ---- For sent files, files seem to get uploaded to IMO Servers. There is no URL available in the imdata though. """ @@ -156,7 +162,7 @@ class IMOAnalyzer(general.AndroidComponentAnalyzer): msgReadStatus = MessageReadStatus.UNKNOWN timeStamp = messagesResultSet.getLong("timestamp") / 1000000000 - + msgBody = messagesResultSet.getString("last_message") messageArtifact = friendsDBHelper.addMessage( self._MESSAGE_TYPE, @@ -166,12 +172,34 @@ class IMOAnalyzer(general.AndroidComponentAnalyzer): timeStamp, msgReadStatus, "", # subject - messagesResultSet.getString("last_message"), + msgBody, "") # thread id - - # TBD: parse the imdata JSON structure to figure out if there is an attachment. - # If one exists, add the attachment as a derived file and a child of the message artifact. + + # Parse the imdata JSON structure to check if there is an attachment. + # If one exists, create an attachment and add to the message. + fileAttachments = ArrayList() + urlAttachments = ArrayList() + + imdataJsonStr = messagesResultSet.getString("imdata") + if imdataJsonStr is not None: + imdata_dict = json.loads(imdataJsonStr) + + # set to none if the key doesn't exist in the dict + attachmentOriginalPath = imdata_dict.get('original_path', None) + attachmentLocalPath = imdata_dict.get('local_path', None) + if attachmentOriginalPath: + attachmentPath = attachmentOriginalPath + else: + attachmentPath = attachmentLocalPath + + if attachmentPath: + # Create a file attachment with given path + fileAttachment = FileAttachment(current_case.getSleuthkitCase(), friendsDb.getDBFile().getDataSource(), attachmentPath) + fileAttachments.add(fileAttachment) + + msgAttachments = MessageAttachments(fileAttachments, []) + attachmentArtifact = friendsDBHelper.addAttachments(messageArtifact, msgAttachments) except SQLException as ex: self._logger.log(Level.WARNING, "Error processing query result for IMO friends", ex) From 1a07c9d3709a903844cca8ad4c43cdd106c93a8a Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Thu, 31 Oct 2019 14:04:07 -0400 Subject: [PATCH 06/11] Addressed Codacy comment. --- Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java index 49784b46a7..e584c6bdfc 100644 --- a/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java +++ b/Core/src/org/sleuthkit/autopsy/test/CustomArtifactType.java @@ -97,6 +97,7 @@ final class CustomArtifactType { attributes.add(new BlackboardAttribute(dateTimeAttrType, MODULE_NAME, 60L)); attributes.add(new BlackboardAttribute(bytesAttrType, MODULE_NAME, DatatypeConverter.parseHexBinary("ABCD"))); attributes.add(new BlackboardAttribute(stringAttrType, MODULE_NAME, "Zero")); + attributes.add(new BlackboardAttribute(jsonAttrType, MODULE_NAME, "{\"fruit\": \"Apple\",\"size\": \"Large\",\"color\": \"Red\"}")); artifact.addAttributes(attributes); /* From d80dafc478408674c9c6abc8b904fccdeb6cb3e3 Mon Sep 17 00:00:00 2001 From: Raman Arora Date: Thu, 31 Oct 2019 15:06:18 -0400 Subject: [PATCH 07/11] Don't add caller/sender to the callee/recipient list. --- InternalPythonModules/android/fbmessenger.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/InternalPythonModules/android/fbmessenger.py b/InternalPythonModules/android/fbmessenger.py index e3c27d1765..2347144f09 100644 --- a/InternalPythonModules/android/fbmessenger.py +++ b/InternalPythonModules/android/fbmessenger.py @@ -182,11 +182,13 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): contactsDb.close() - ## Extracts recipeint id from 'user_key' column and adds recipient to given list. - def addRecipientToList(self, user_key, recipientList): + ## Extracts recipeint id from 'user_key' column and adds recipient to given list, + ## if the recipeint id is not the same as sender id + def addRecipientToList(self, user_key, senderId, recipientList): if user_key is not None: - recipientId = user_key.replace('FACEBOOK:', '') - recipientList.append(recipientId) + recipientId = user_key.replace('FACEBOOK:', '') + if recipientId != senderId: + recipientList.append(recipientId) ## Extracts sender id from the json in 'sender' column. @@ -268,7 +270,7 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): direction = self.deduceDirectionFromSenderId(fromId) # Get recipient and add to list - self.addRecipientToList(messagesResultSet.getString("user_key"), + self.addRecipientToList(messagesResultSet.getString("user_key"), fromId, recipientIdsList) timeStamp = messagesResultSet.getLong("timestamp_ms") / 1000 @@ -285,7 +287,7 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): threadId = messagesResultSet.getString("thread_key") else: # same msgId as last, just collect recipient from current row - self.addRecipientToList(messagesResultSet.getString("user_key"), + self.addRecipientToList(messagesResultSet.getString("user_key"), fromId, recipientIdsList) @@ -373,7 +375,7 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): direction = self.deduceDirectionFromSenderId(callerId) # Get recipient and add to list - self.addRecipientToList(messagesResultSet.getString("user_key"), + self.addRecipientToList(messagesResultSet.getString("user_key"), callerId, calleeIdsList) # the timestamp from call ended msg is used as end timestamp @@ -391,7 +393,7 @@ class FBMessengerAnalyzer(general.AndroidComponentAnalyzer): startTimeStamp = endTimeStamp - duration else: # same msgId as last, just collect callee from current row - self.addRecipientToList(messagesResultSet.getString("user_key"), + self.addRecipientToList(messagesResultSet.getString("user_key"), callerId, calleeIdsList) # at the end of the loop, add last message From c007b27f602d05d86d03d897417c2a3c4c828d15 Mon Sep 17 00:00:00 2001 From: esaunders Date: Mon, 4 Nov 2019 15:03:24 -0500 Subject: [PATCH 08/11] Switch to Amazon Corretto JDK which bundles Java FX. --- .travis.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index b907afbd81..575fa3f9dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,11 +61,9 @@ before_script: - if [ $TRAVIS_OS_NAME = osx ]; then brew uninstall java --force; brew cask uninstall java --force; - brew tap adoptopenjdk/openjdk; - brew cask install adoptopenjdk8; - wget https://cdn.azul.com/zulu/bin/zulu8.40.0.25-ca-fx-jdk8.0.222-macosx_x64.tar.gz; - sudo tar xf zulu8.40.0.25-ca-fx-jdk8.0.222-macosx_x64.tar.gz --strip-components=1 -C /Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home; - export JAVA_HOME=/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home; + brew tap homebrew/cask-versions; + brew cask install corretto8; + export JAVA_HOME=/Library/Java/JavaVirtualMachines/amazon-corretto-8.jdk/Contents/Home; fi - java -version From 3a54c9a03bad3452f984b52d0c822d88200829ab Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 5 Nov 2019 13:46:26 -0500 Subject: [PATCH 09/11] Simplfied the model --- .../datamodel/ArtifactWaypoint.java | 197 ------------ .../geolocation/datamodel/EXIFWaypoint.java | 4 +- .../datamodel/LastKnownWaypoint.java | 4 +- .../autopsy/geolocation/datamodel/Route.java | 81 ++--- .../geolocation/datamodel/RoutePoint.java | 85 ----- .../geolocation/datamodel/SearchWaypoint.java | 6 +- .../datamodel/TrackpointWaypoint.java | 4 +- .../geolocation/datamodel/Waypoint.java | 303 +++++++++++++----- 8 files changed, 279 insertions(+), 405 deletions(-) delete mode 100755 Core/src/org/sleuthkit/autopsy/geolocation/datamodel/ArtifactWaypoint.java delete mode 100755 Core/src/org/sleuthkit/autopsy/geolocation/datamodel/RoutePoint.java diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/ArtifactWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/ArtifactWaypoint.java deleted file mode 100755 index a2a0f3315f..0000000000 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/ArtifactWaypoint.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2019 Basis Technology Corp. - * contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.geolocation.datamodel; - -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.BlackboardArtifact; -import org.sleuthkit.datamodel.BlackboardAttribute; -import org.sleuthkit.datamodel.TskCoreException; - -/** - * Representation of a Waypoint created from a BlackboardArtifact. - * - */ -class ArtifactWaypoint implements Waypoint { - - final private Long timestamp; - final private Double longitude; - final private Double latitude; - final private Double altitude; - final private String label; - final private AbstractFile image; - final private BlackboardArtifact artifact; - - // This list is not expected to change after construction so the - // constructor will take care of creating an unmodifiable List - final private List immutablePropertiesList; - - /** - * Construct a waypoint with the given artifact. - * - * @param artifact BlackboardArtifact for this waypoint - * - * @throws GeoLocationDataException Exception will be thrown if artifact did - * not have a valid longitude and latitude. - */ - ArtifactWaypoint(BlackboardArtifact artifact) throws GeoLocationDataException { - this(artifact, - getAttributesFromArtifactAsMap(artifact)); - } - - /** - * Constructor that sets all of the member variables. - * - * @param artifact BlackboardArtifact for this waypoint - * @param label String waypoint label - * @param timestamp Long timestamp, unix/java epoch seconds - * @param latitude Double waypoint latitude - * @param longitude Double waypoint longitude - * @param altitude Double waypoint altitude - * @param image AbstractFile image for waypoint, this maybe null - * @param attributeMap A Map of attributes for the given artifact - * - * @throws GeoLocationDataException Exception will be thrown if artifact did - * not have a valid longitude and latitude. - */ - ArtifactWaypoint(BlackboardArtifact artifact, String label, Long timestamp, Double latitude, Double longitude, Double altitude, AbstractFile image, Map attributeMap) throws GeoLocationDataException { - if (longitude == null || latitude == null) { - throw new GeoLocationDataException("Invalid waypoint, null value passed for longitude or latitude"); - } - - this.artifact = artifact; - this.label = label; - this.image = image; - this.timestamp = timestamp; - this.longitude = longitude; - this.latitude = latitude; - this.altitude = altitude; - - immutablePropertiesList = Collections.unmodifiableList(Waypoint.createGeolocationProperties(attributeMap)); - } - - /** - * Constructs a new ArtifactWaypoint. - * - * @param artifact BlackboardArtifact for this waypoint - * @param attributeMap A Map of the BlackboardAttributes for the given - * artifact. - * - * @throws GeoLocationDataException - */ - private ArtifactWaypoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { - this(artifact, - getLabelFromArtifact(attributeMap), - attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME).getValueLong() : null, - attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, - attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, - attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, - null, attributeMap); - } - - /** - * Get the BlackboardArtifact that this waypoint represents. - * - * @return BlackboardArtifact for this waypoint. - */ - BlackboardArtifact getArtifact() { - return artifact; - } - - @Override - public Long getTimestamp() { - return timestamp; - } - - @Override - public String getLabel() { - return label; - } - - @Override - public Double getLatitude() { - return latitude; - } - - @Override - public Double getLongitude() { - return longitude; - } - - @Override - public Double getAltitude() { - return altitude; - } - - @Override - public AbstractFile getImage() { - return image; - } - - @Override - public List getOtherProperties() { - return immutablePropertiesList; - } - - /** - * Gets the label for this waypoint. - * - * @param artifact BlackboardArtifact for waypoint - * - * @return Returns a label for the waypoint, or empty string if no label was - * found. - */ - private static String getLabelFromArtifact(Map attributeMap) { - BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); - if (attribute != null) { - return attribute.getDisplayString(); - } - - return ""; - } - - /** - * Gets the list of attributes from the artifact and puts them into a map - * with the ATRIBUTE_TYPE as the key. - * - * @param artifact BlackboardArtifact current artifact - * - * @return A Map of BlackboardAttributes for the given artifact with - * ATTRIBUTE_TYPE as the key. - * - * @throws GeoLocationDataException - */ - static Map getAttributesFromArtifactAsMap(BlackboardArtifact artifact) throws GeoLocationDataException { - Map attributeMap = new HashMap<>(); - try { - List attributeList = artifact.getAttributes(); - for (BlackboardAttribute attribute : attributeList) { - BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID()); - attributeMap.put(type, attribute); - } - } catch (TskCoreException ex) { - throw new GeoLocationDataException("Unable to get attributes from artifact", ex); - } - - return attributeMap; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/EXIFWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/EXIFWaypoint.java index 927b91b1d4..3a1ec3e66e 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/EXIFWaypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/EXIFWaypoint.java @@ -27,7 +27,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Waypoint wrapper class for TSK_METADATA_EXIF artifacts. */ -final class EXIFWaypoint extends ArtifactWaypoint { +final class EXIFWaypoint extends Waypoint { /** * Construct a way point with the given artifact. @@ -56,7 +56,7 @@ final class EXIFWaypoint extends ArtifactWaypoint { attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, - image, attributeMap); + image, attributeMap, null); } /** diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/LastKnownWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/LastKnownWaypoint.java index 3c27e81f5d..7bf85874ff 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/LastKnownWaypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/LastKnownWaypoint.java @@ -26,7 +26,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute; /** * A Last Known Location Waypoint object. */ -final class LastKnownWaypoint extends ArtifactWaypoint { +final class LastKnownWaypoint extends Waypoint { /** * Constructs a new waypoint. @@ -55,7 +55,7 @@ final class LastKnownWaypoint extends ArtifactWaypoint { attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, - null, attributeMap); + null, attributeMap, null); } /** diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java index 8b6d3a968b..29cfd5afe4 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Route.java @@ -31,14 +31,13 @@ import org.sleuthkit.datamodel.TskCoreException; /** * A Route represents a TSK_GPS_ROUTE artifact which has a start and end point - * however the class was written with the assumption that some routes may have + * however the class was written with the assumption that routes may have * more that two points. * */ public final class Route { private final List points; private final Long timestamp; - private final Double altitude; // This list is not expected to change after construction so the // constructor will take care of creating an unmodifiable List @@ -78,14 +77,11 @@ public final class Route { Route(BlackboardArtifact artifact) throws GeoLocationDataException { points = new ArrayList<>(); - Map attributeMap = ArtifactWaypoint.getAttributesFromArtifactAsMap(artifact); - points.add(getRouteStartPoint(attributeMap)); - points.add(getRouteEndPoint(attributeMap)); - - BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); - altitude = attribute != null ? attribute.getValueDouble() : null; - - attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); + Map attributeMap = Waypoint.getAttributesFromArtifactAsMap(artifact); + points.add(getRouteStartPoint(artifact, attributeMap)); + points.add(getRouteEndPoint(artifact, attributeMap)); + + BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); timestamp = attribute != null ? attribute.getValueLong() : null; immutablePropertiesList = Collections.unmodifiableList(Waypoint.createGeolocationProperties(attributeMap)); @@ -100,24 +96,6 @@ public final class Route { return Collections.unmodifiableList(points); } - /** - * Get the timestamp for this Route - * - * @return The timestamp (java/unix epoch seconds) or null if none was set. - */ - public Long getTimestamp() { - return timestamp; - } - - /** - * Get the altitude for this route. - * - * @return The Double altitude value or null if none was set. - */ - public Double getAltitude() { - return altitude; - } - /** * Get the "Other attributes" for this route. The map will contain display * name, formatted value pairs. This list is unmodifiable. @@ -139,25 +117,39 @@ public final class Route { public String getLabel() { return Bundle.Route_Label(); } - + + public Long getTimestamp() { + return timestamp; + } + /** * Get the route start point. * - * @param attributeMap Map of artifact attributes for this waypoint + * @param attributeMap Map of artifact attributes for this waypoint. + * + * An exception will be thrown if longitude or latitude is null. * - * @return Start RoutePoint + * @return Start waypoint * - * @throws GeoLocationDataException when longitude or latitude is null + * @throws GeoLocationDataException. */ @Messages({ "Route_Start_Label=Start" }) - private Waypoint getRouteStartPoint(Map attributeMap) throws GeoLocationDataException { + private Waypoint getRouteStartPoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { BlackboardAttribute latitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START); BlackboardAttribute longitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START); + BlackboardAttribute altitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); + BlackboardAttribute pointTimestamp = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); if (latitude != null && longitude != null) { - return new RoutePoint(this, latitude.getValueDouble(), longitude.getValueDouble(), Bundle.Route_Start_Label()); + return new Waypoint(artifact, + Bundle.Route_Start_Label(), + pointTimestamp != null ? pointTimestamp.getValueLong() : null, + latitude.getValueDouble(), + longitude.getValueDouble(), + altitude != null ? altitude.getValueDouble() : null, + null, attributeMap, this); } else { throw new GeoLocationDataException("Unable to create route start point, invalid longitude and/or latitude"); } @@ -165,24 +157,33 @@ public final class Route { /** * Get the route End point. + * + * An exception will be thrown if longitude or latitude is null. * * @param attributeMap Map of artifact attributes for this waypoint * - * @return End RoutePoint or null if valid longitude and latitude are not - * found + * @return The end waypoint * - * @throws GeoLocationDataException when longitude or latitude is null + * @throws GeoLocationDataException */ @Messages({ "Route_End_Label=End" }) - private Waypoint getRouteEndPoint(Map attributeMap) throws GeoLocationDataException { + private Waypoint getRouteEndPoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { BlackboardAttribute latitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END); BlackboardAttribute longitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END); + BlackboardAttribute altitude = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE); + BlackboardAttribute pointTimestamp = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); if (latitude != null && longitude != null) { - return new RoutePoint(this, latitude.getValueDouble(), longitude.getValueDouble(), Bundle.Route_End_Label()); - }else { + return new Waypoint(artifact, + Bundle.Route_End_Label(), + pointTimestamp != null ? pointTimestamp.getValueLong() : null, + latitude.getValueDouble(), + longitude.getValueDouble(), + altitude != null ? altitude.getValueDouble() : null, + null, attributeMap, this); + } else { throw new GeoLocationDataException("Unable to create route end point, invalid longitude and/or latitude"); } } diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/RoutePoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/RoutePoint.java deleted file mode 100755 index 50087de873..0000000000 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/RoutePoint.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Autopsy Forensic Browser - * - * Copyright 2019 Basis Technology Corp. - * contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.geolocation.datamodel; - -import java.util.List; -import org.sleuthkit.datamodel.AbstractFile; - -/** - * A point in a Route. For future use this point will have a pointer to its - * parent route. - */ -final class RoutePoint implements Waypoint { - - private final Route parent; - private final Double longitude; - private final Double latitude; - private final String label; - - /** - * Construct a route for a route. - * - * @param parent The parent route object. - * @param latitude Latitude for point - * @param longitude Longitude for point - * @param label Way point label. - */ - RoutePoint(Route parent, double latitude, double longitude, String label) { - this.longitude = longitude; - this.latitude = latitude; - this.label = label; - this.parent = parent; - } - - @Override - public Long getTimestamp() { - return parent.getTimestamp(); - } - - @Override - public String getLabel() { - return label; - } - - @Override - public Double getLatitude() { - return latitude; - } - - @Override - public Double getLongitude() { - return longitude; - } - - @Override - public Double getAltitude() { - return parent.getAltitude(); - } - - @Override - public List getOtherProperties() { - return parent.getOtherProperties(); - } - - @Override - public AbstractFile getImage() { - return null; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/SearchWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/SearchWaypoint.java index bc70cfe570..7f0746d6ca 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/SearchWaypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/SearchWaypoint.java @@ -24,9 +24,9 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; /** - * A SearchWaypoint is a subclass of ArtifactWaypoint. + * A SearchWaypoint is a subclass of Waypoint. */ -final class SearchWaypoint extends ArtifactWaypoint { +final class SearchWaypoint extends Waypoint { /** * Construct a waypoint for TSK_GPS_SEARCH artifact. @@ -44,7 +44,7 @@ final class SearchWaypoint extends ArtifactWaypoint { attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, - null, attributeMap); + null, attributeMap, null); } /** diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/TrackpointWaypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/TrackpointWaypoint.java index ea724f2821..7009aa63a1 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/TrackpointWaypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/TrackpointWaypoint.java @@ -26,7 +26,7 @@ import org.sleuthkit.datamodel.BlackboardAttribute; /** * A wrapper class for TSK_GPS_TRACKPOINT artifacts. */ -final class TrackpointWaypoint extends ArtifactWaypoint { +final class TrackpointWaypoint extends Waypoint { /** * Construct a waypoint for trackpoints. * @@ -43,7 +43,7 @@ final class TrackpointWaypoint extends ArtifactWaypoint { attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, - null, attributeMap); + null, attributeMap, null); } /** diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java index 45c8816574..3c84c92c26 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java @@ -19,6 +19,9 @@ package org.sleuthkit.autopsy.geolocation.datamodel; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -31,17 +34,29 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * The basic details of a waypoint. + * Representation of a Waypoint created from a BlackboardArtifact. * */ -public interface Waypoint { - static final Logger logger = Logger.getLogger(Waypoint.class.getName()); - +public class Waypoint { + + final private Long timestamp; + final private Double longitude; + final private Double latitude; + final private Double altitude; + final private String label; + final private AbstractFile image; + final private BlackboardArtifact artifact; + final private Route route; + + // This list is not expected to change after construction. The + // constructor will take care of making an unmodifiable List + final private List immutablePropertiesList; + /** * This is a list of attributes that are already being handled by the - * waypoint classes and will have get functions. + * by getter functions. */ - BlackboardAttribute.ATTRIBUTE_TYPE[] ALREADY_HANDLED_ATTRIBUTES = { + static BlackboardAttribute.ATTRIBUTE_TYPE[] ALREADY_HANDLED_ATTRIBUTES = { BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, @@ -53,61 +68,202 @@ public interface Waypoint { BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END,}; + private static final Logger logger = Logger.getLogger(Waypoint.class.getName()); + /** - * Interface to describe a waypoint. A waypoint is made up of - * a longitude, latitude, label, timestamp, type, image and altitude. - * + * Construct a waypoint with the given artifact. + * + * @param artifact BlackboardArtifact for this waypoint + * + * @throws GeoLocationDataException Exception will be thrown if artifact did + * not have a valid longitude and latitude. + */ + Waypoint(BlackboardArtifact artifact) throws GeoLocationDataException { + this(artifact, + getAttributesFromArtifactAsMap(artifact)); + } + + /** + * Constructor that initializes all of the member variables. + * + * @param artifact BlackboardArtifact for this waypoint + * @param label String waypoint label + * @param timestamp Long timestamp, unix/java epoch seconds + * @param latitude Double waypoint latitude + * @param longitude Double waypoint longitude + * @param altitude Double waypoint altitude + * @param image AbstractFile image for waypoint, this maybe null + * @param attributeMap A Map of attributes for the given artifact + * + * @throws GeoLocationDataException Exception will be thrown if artifact did + * not have a valid longitude and latitude. + */ + Waypoint(BlackboardArtifact artifact, String label, Long timestamp, Double latitude, Double longitude, Double altitude, AbstractFile image, Map attributeMap, Route route) throws GeoLocationDataException { + if (longitude == null || latitude == null) { + throw new GeoLocationDataException("Invalid waypoint, null value passed for longitude or latitude"); + } + + this.artifact = artifact; + this.label = label; + this.image = image; + this.timestamp = timestamp; + this.longitude = longitude; + this.latitude = latitude; + this.altitude = altitude; + this.route = null; + + immutablePropertiesList = Collections.unmodifiableList(createGeolocationProperties(attributeMap)); + } + + /** + * Constructs a new ArtifactWaypoint. + * + * @param artifact BlackboardArtifact for this waypoint + * @param attributeMap A Map of the BlackboardAttributes for the given + * artifact. + * + * @throws GeoLocationDataException + */ + private Waypoint(BlackboardArtifact artifact, Map attributeMap) throws GeoLocationDataException { + this(artifact, + getLabelFromArtifact(attributeMap), + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME).getValueLong() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE).getValueDouble() : null, + attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE) != null ? attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE).getValueDouble() : null, + null, attributeMap, null); + } + + /** + * Get the BlackboardArtifact that this waypoint represents. + * + * @return BlackboardArtifact for this waypoint. + */ + public BlackboardArtifact getArtifact() { + return artifact; + } + + /** + * Interface to describe a waypoint. A waypoint is made up of a longitude, + * latitude, label, timestamp, type, image and altitude. + * * A good way point should have at minimum a longitude and latutude. * * @return Timestamp in java/unix epoch seconds or null if none was set. */ - Long getTimestamp(); + public Long getTimestamp() { + return timestamp; + } /** * Get the label for this point object. * * @return String label for the point or null if none was set */ - String getLabel(); + public String getLabel() { + return label; + } /** * Get the latitude for this point. * - * @return Returns the latitude for the point or null if none was set + * @return Returns the latitude for the point */ - Double getLatitude(); + public Double getLatitude() { + return latitude; + } /** * Get the longitude for this point. * - * @return Returns the longitude for the point or null if none was set + * @return Returns the longitude for the point */ - Double getLongitude(); + public Double getLongitude() { + return longitude; + } /** * Get the altitude for this point. * * @return Returns the altitude for the point or null if none was set */ - Double getAltitude(); - - /** - * Gets an unmodifiable List of other properties that may be interesting to this way point. - * The List will not include properties for which getter functions - * exist. - * - * @return A List of waypoint properties - */ - List getOtherProperties(); + public Double getAltitude() { + return altitude; + } /** * Get the image for this waypoint. * * @return AbstractFile image or null if one was not set */ - AbstractFile getImage(); + public AbstractFile getImage() { + return image; + } /** + * Gets an unmodifiable List of other properties that may be interesting to + * this way point. The List will not include properties for which getter + * functions exist. + * + * @return A List of waypoint properties + */ + public List getOtherProperties() { + return immutablePropertiesList; + } + + /** + * Returns the route that this waypoint is apart of . + * + * @return The waypoint route or null if the waypoint is not apart of a route. + */ + public Route getRoute() { + return route; + } + + /** + * Gets the label for this waypoint. + * + * @param artifact BlackboardArtifact for waypoint + * + * @return Returns a label for the waypoint, or empty string if no label was + * found. + */ + private static String getLabelFromArtifact(Map attributeMap) { + BlackboardAttribute attribute = attributeMap.get(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME); + if (attribute != null) { + return attribute.getDisplayString(); + } + + return ""; + } + + /** + * Gets the list of attributes from the artifact and puts them into a map + * with the ATRIBUTE_TYPE as the key. + * + * @param artifact BlackboardArtifact current artifact + * + * @return A Map of BlackboardAttributes for the given artifact with + * ATTRIBUTE_TYPE as the key. + * + * @throws GeoLocationDataException + */ + static Map getAttributesFromArtifactAsMap(BlackboardArtifact artifact) throws GeoLocationDataException { + Map attributeMap = new HashMap<>(); + try { + List attributeList = artifact.getAttributes(); + for (BlackboardAttribute attribute : attributeList) { + BlackboardAttribute.ATTRIBUTE_TYPE type = BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attribute.getAttributeType().getTypeID()); + attributeMap.put(type, attribute); + } + } catch (TskCoreException ex) { + throw new GeoLocationDataException("Unable to get attributes from artifact", ex); + } + + return attributeMap; + } + + /** * Returns a list of Waypoints for the artifacts with geolocation * information. * @@ -120,7 +276,7 @@ public interface Waypoint { * * @throws GeoLocationDataException */ - static List getAllWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + public static List getAllWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { List points = new ArrayList<>(); points.addAll(getTrackpointWaypoints(skCase)); @@ -141,22 +297,22 @@ public interface Waypoint { * * @throws GeoLocationDataException */ - static List getTrackpointWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + public static List getTrackpointWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { List artifacts = null; - try{ + try { artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT); - } catch(TskCoreException ex) { + } catch (TskCoreException ex) { throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_TRACKPOINT", ex); } - + List points = new ArrayList<>(); for (BlackboardArtifact artifact : artifacts) { - try{ - ArtifactWaypoint point = new TrackpointWaypoint(artifact); + try { + Waypoint point = new TrackpointWaypoint(artifact); points.add(point); - } catch(GeoLocationDataException ex) { + } catch (GeoLocationDataException ex) { logger.log(Level.WARNING, String.format("No longitude or latitude available for TSK_GPS_TRACKPOINT artifactID: %d", artifact.getArtifactID())); - } + } } return points; } @@ -170,25 +326,25 @@ public interface Waypoint { * * @throws GeoLocationDataException */ - static List getEXIFWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + static public List getEXIFWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { List artifacts = null; - try{ + try { artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF); - } catch(TskCoreException ex) { + } catch (TskCoreException ex) { throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_LAST_KNOWN_LOCATION", ex); } - + List points = new ArrayList<>(); if (artifacts != null) { for (BlackboardArtifact artifact : artifacts) { - try{ - ArtifactWaypoint point = new EXIFWaypoint(artifact); + try { + Waypoint point = new EXIFWaypoint(artifact); points.add(point); - } catch(GeoLocationDataException ex) { + } catch (GeoLocationDataException ex) { // I am a little relucant to log this error because I suspect // this will happen more often than not. It is valid for // METADAT_EXIF to not have longitude and latitude - } + } } } return points; @@ -203,23 +359,23 @@ public interface Waypoint { * * @throws GeoLocationDataException */ - static List getSearchWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + public static List getSearchWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { List artifacts = null; - try{ + try { artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH); - } catch(TskCoreException ex) { + } catch (TskCoreException ex) { throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_SEARCH", ex); } - + List points = new ArrayList<>(); if (artifacts != null) { for (BlackboardArtifact artifact : artifacts) { - try{ - ArtifactWaypoint point = new SearchWaypoint(artifact); + try { + Waypoint point = new SearchWaypoint(artifact); points.add(point); - } catch(GeoLocationDataException ex) { + } catch (GeoLocationDataException ex) { logger.log(Level.WARNING, String.format("No longitude or latitude available for TSK_GPS_SEARCH artifactID: %d", artifact.getArtifactID())); - } + } } } return points; @@ -234,23 +390,23 @@ public interface Waypoint { * * @throws GeoLocationDataException */ - static List getLastKnownWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + public static List getLastKnownWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { List artifacts = null; - try{ + try { artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION); - } catch(TskCoreException ex) { + } catch (TskCoreException ex) { throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_LAST_KNOWN_LOCATION", ex); } - + List points = new ArrayList<>(); if (artifacts != null) { - for (BlackboardArtifact artifact : artifacts) { - try{ - ArtifactWaypoint point = new LastKnownWaypoint(artifact); + for (BlackboardArtifact artifact : artifacts) { + try { + Waypoint point = new LastKnownWaypoint(artifact); points.add(point); - } catch(GeoLocationDataException ex) { + } catch (GeoLocationDataException ex) { logger.log(Level.WARNING, String.format("No longitude or latitude available for TSK_GPS_LAST_KNOWN_LOCATION artifactID: %d", artifact.getArtifactID())); - } + } } } return points; @@ -265,29 +421,28 @@ public interface Waypoint { * * @throws GeoLocationDataException */ - static List getBookmarkWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { + public static List getBookmarkWaypoints(SleuthkitCase skCase) throws GeoLocationDataException { List artifacts = null; - try{ + try { artifacts = skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK); - } catch(TskCoreException ex) { + } catch (TskCoreException ex) { throw new GeoLocationDataException("Unable to get artifacts for type: TSK_GPS_BOOKMARK", ex); } - + List points = new ArrayList<>(); if (artifacts != null) { for (BlackboardArtifact artifact : artifacts) { - try{ - ArtifactWaypoint point = new ArtifactWaypoint(artifact); + try { + Waypoint point = new Waypoint(artifact); points.add(point); - } catch(GeoLocationDataException ex) { + } catch (GeoLocationDataException ex) { logger.log(Level.WARNING, String.format("No longitude or latitude available for TSK_GPS_BOOKMARK artifactID: %d", artifact.getArtifactID())); - } + } } } return points; } - - + /** * Get a list of Waypoint.Property objects for the given artifact. This list * will not include attributes that the Waypoint interfact has get functions @@ -299,10 +454,10 @@ public interface Waypoint { * * @throws GeoLocationDataException */ - static List createGeolocationProperties(Map attributeMap) throws GeoLocationDataException { + static public List createGeolocationProperties(Map attributeMap) throws GeoLocationDataException { List list = new ArrayList<>(); - Set keys = attributeMap.keySet(); + Set keys = new HashSet<>(attributeMap.keySet()); for (BlackboardAttribute.ATTRIBUTE_TYPE type : ALREADY_HANDLED_ATTRIBUTES) { keys.remove(type); @@ -321,7 +476,7 @@ public interface Waypoint { * Simple property class for waypoint properties that a purely * informational. */ - class Property { + public static final class Property { private final String displayName; private final String value; @@ -333,7 +488,7 @@ public interface Waypoint { * or empty string. * @param value String value for property. Can be null. */ - Property(String displayName, String value) { + private Property(String displayName, String value) { this.displayName = displayName; this.value = value; } From 20469bf71979461836391bb4229729b34c265358 Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 5 Nov 2019 14:55:51 -0500 Subject: [PATCH 10/11] changed field to private --- .../org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java index 3c84c92c26..bd5b39b6b7 100755 --- a/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java +++ b/Core/src/org/sleuthkit/autopsy/geolocation/datamodel/Waypoint.java @@ -56,7 +56,7 @@ public class Waypoint { * This is a list of attributes that are already being handled by the * by getter functions. */ - static BlackboardAttribute.ATTRIBUTE_TYPE[] ALREADY_HANDLED_ATTRIBUTES = { + static private BlackboardAttribute.ATTRIBUTE_TYPE[] ALREADY_HANDLED_ATTRIBUTES = { BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE, From 4b5447cc4ab8a3bfb44652163be1dfcfe70dc0cd Mon Sep 17 00:00:00 2001 From: Kelly Kelly Date: Tue, 5 Nov 2019 14:58:13 -0500 Subject: [PATCH 11/11] Fixed bug in KMLReport --- .../autopsy/report/modules/kml/KMLReport.java | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java b/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java index 002215bf04..79fb65ec0c 100644 --- a/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java +++ b/Core/src/org/sleuthkit/autopsy/report/modules/kml/KMLReport.java @@ -730,9 +730,9 @@ class KMLReport implements GeneralReportModule { * * @param route * - * @return + * @return A HTML formatted list of the Route attributes */ - private String getFormattedDetails(Route route) { + private String getFormattedDetails(Route route) { List points = route.getRoute(); StringBuilder result = new StringBuilder(); //NON-NLS @@ -749,13 +749,20 @@ class KMLReport implements GeneralReportModule { Waypoint end = points.get(1); result.append(formatAttribute("Start Latitude", start.getLatitude().toString())) - .append(formatAttribute("Start Longitude", start.getLongitude().toString())) - .append(formatAttribute("End Latitude", end.getLatitude().toString())) + .append(formatAttribute("Start Longitude", start.getLongitude().toString())); + + Double altitude = start.getAltitude(); + if(altitude != null) { + result.append(formatAttribute("Start Altitude", altitude.toString())); + } + + result.append(formatAttribute("End Latitude", end.getLatitude().toString())) .append(formatAttribute("End Longitude", end.getLongitude().toString())); - } - - if (route.getAltitude() != null) { - result.append(formatAttribute("Altitude", route.getAltitude().toString())); + + altitude = end.getAltitude(); + if(altitude != null) { + result.append(formatAttribute("End Altitude", altitude.toString())); + } } List list = route.getOtherProperties();