4242from java .lang import ProcessBuilder
4343from java .util import ArrayList
4444from java .util .logging import Level
45- from org .sleuthkit .autopsy .casemodule import Case , NoCurrentCaseException
46- from org .sleuthkit .autopsy .coreutils import ExecUtil , Logger , PlatformUtil
45+ from org .sleuthkit .autopsy .casemodule import Case
46+ from org .sleuthkit .autopsy .casemodule import NoCurrentCaseException
47+ from org .sleuthkit .autopsy .coreutils import ExecUtil
48+ from org .sleuthkit .autopsy .coreutils import Logger
49+ from org .sleuthkit .autopsy .coreutils import PlatformUtil
4750from org .sleuthkit .autopsy .datamodel import ContentUtils
48- from org .sleuthkit .autopsy .ingest import (
49- DataSourceIngestModule ,
50- DataSourceIngestModuleProcessTerminator ,
51- IngestMessage ,
52- IngestModule ,
53- IngestModuleFactoryAdapter ,
54- IngestServices ,
55- )
51+ from org .sleuthkit .autopsy .ingest import DataSourceIngestModule
52+ from org .sleuthkit .autopsy .ingest import DataSourceIngestModuleProcessTerminator
53+ from org .sleuthkit .autopsy .ingest import IngestMessage
54+ from org .sleuthkit .autopsy .ingest import IngestModule
55+ from org .sleuthkit .autopsy .ingest import IngestModuleFactoryAdapter
56+ from org .sleuthkit .autopsy .ingest import IngestServices
5657from org .sleuthkit .autopsy .ingest .IngestModule import IngestModuleException
57- from org .sleuthkit .datamodel import (
58- BlackboardArtifact ,
59- BlackboardAttribute ,
60- CommunicationsManager ,
61- TskCoreException ,
62- TskData ,
63- )
58+ from org .sleuthkit .datamodel import BlackboardArtifact
59+ from org .sleuthkit .datamodel import BlackboardAttribute
60+ from org .sleuthkit .datamodel import CommunicationsManager
61+ from org .sleuthkit .datamodel import TskCoreException
62+ from org .sleuthkit .datamodel import TskData
6463from org .sleuthkit .datamodel .Blackboard import BlackboardException
6564from org .sleuthkit .datamodel .blackboardutils import CommunicationArtifactsHelper
66- from org .sleuthkit .datamodel .blackboardutils .attributes import MessageAttachments
67- from org .sleuthkit .datamodel .blackboardutils .attributes .MessageAttachments import (
68- URLAttachment ,
69- )
7065from org .sleuthkit .datamodel .blackboardutils .CommunicationArtifactsHelper import (
7166 CallMediaType ,
67+ )
68+ from org .sleuthkit .datamodel .blackboardutils .CommunicationArtifactsHelper import (
7269 CommunicationDirection ,
70+ )
71+ from org .sleuthkit .datamodel .blackboardutils .CommunicationArtifactsHelper import (
7372 MessageReadStatus ,
7473)
74+ from org .sleuthkit .datamodel .blackboardutils .attributes import MessageAttachments
75+ from org .sleuthkit .datamodel .blackboardutils .attributes .MessageAttachments import (
76+ URLAttachment ,
77+ )
7578
7679# Common Prefix Shared for all artefacts
7780ARTIFACT_PREFIX = "Microsoft Teams"
@@ -141,11 +144,11 @@ def startUp(self, context):
141144 )
142145 if not os .path .exists (self .path_to_executable ):
143146 raise IngestModuleException (
144- "Could not find main .exe within the module directory."
147+ "Could not find ms_teams_parser .exe within the module directory."
145148 )
146149 else :
147150 raise IngestModuleException (
148- "This Plugin currently only works on Windows based systems."
151+ "This plugin currently only works on Windows based systems."
149152 )
150153
151154 blackboard = Case .getCurrentCase ().getServices ().getBlackboard ()
@@ -210,11 +213,11 @@ def _parse_databases(self, content, progress_bar):
210213 os .makedirs (temp_path_to_content )
211214 self .log (
212215 Level .INFO ,
213- f "Created temporary directory: { temp_path_to_content } ." ,
216+ "Created temporary directory: {}." . format ( temp_path_to_content ) ,
214217 )
215218 except OSError :
216219 raise IngestModuleException (
217- f "Could not create directory: { temp_path_to_content } ."
220+ "Could not create directory: {}." . format ( temp_path_to_content )
218221 )
219222
220223 # At first extract the desired artefacts to our newly created temp directory
@@ -238,15 +241,15 @@ def _extract(self, content, path):
238241 # ignore relative paths
239242 if child_name == "." or child_name == ".." :
240243 continue
241- elif child .isFile (): # noqa: RET507
244+ elif child .isFile ():
242245 ContentUtils .writeToFile (child , File (child_path ))
243246 elif child .isDir ():
244247 os .mkdir (child_path )
245248 self ._extract (child , child_path )
246- self .log (Level .INFO , f "Successfully extracted to { path } " )
249+ self .log (Level .INFO , "Successfully extracted to {}" . format ( path ) )
247250 except OSError :
248251 raise IngestModuleException (
249- f "Could not extract files to directory: { path } ."
252+ "Could not extract files to directory: {}." . format ( path )
250253 )
251254
252255 def _analyze (self , content , path , progress_bar ):
@@ -286,20 +289,25 @@ def _process_imported_records(self, imported_records, content, progress_bar):
286289 database_sub_files = [
287290 i
288291 for n , i in enumerate (imported_records )
289- if i .get ("origin_file" )
292+ if "origin_file" in i and i .get ("origin_file" )
290293 not in [y .get ("origin_file" ) for y in imported_records [n + 1 :]]
291294 ]
292295 try :
293296 for file in database_sub_files :
297+ # Skip empty files as these are invalid records
298+ if file ["origin_file" ] is None :
299+ continue
300+
294301 user_account_instance = None
295302 teams_leveldb_file_path = self .get_level_db_file (
296303 content , file ["origin_file" ]
297304 )
305+
298306 # Get only the records per file
299307 records = [
300308 d
301309 for d in imported_records
302- if d ["origin_file" ] == file ["origin_file" ]
310+ if 'origin_file' in d and d ["origin_file" ] == file ["origin_file" ]
303311 ]
304312 try :
305313 user_account_instance = self .get_user_account (records )
@@ -480,7 +488,12 @@ def parse_calllogs(self, calls, helper):
480488 start_date = self .date_to_long (
481489 call ["properties" ]["call-log" ]["startTime" ]
482490 )
483- end_date = self .date_to_long (call ["properties" ]["call-log" ]["endTime" ])
491+ end_date = self .date_to_long (
492+ call ["properties" ]["call-log" ]["endTime" ]
493+ )
494+ # Skip empty callees
495+ if to_address is None :
496+ continue
484497
485498 helper .addCalllog (
486499 call_direction ,
@@ -508,18 +521,20 @@ def parse_messages(self, messages, helper, teams_leveldb_file_path):
508521 # each message artifact
509522 try :
510523 for message in messages :
524+
511525 message_type = ARTIFACT_PREFIX
512526 message_id = message ["clientmessageid" ]
513527 direction = self .deduce_message_direction (message ["isFromMe" ])
514528 phone_number_from = message ["creator" ]
515529 # TODO Fix To Number
516- phone_number_to = ""
530+ phone_number_to = []
517531 message_date_time = self .date_to_long (message ["composetime" ])
518532 message_read_status = MessageReadStatus .UNKNOWN
519533 subject = None
520534 message_text = message ["content" ]
521535 # Group by the conversationId, these can be direct messages, but also posts
522536 thread_id = message ["conversationId" ]
537+ # Additional Attributes
523538
524539 additional_attributes = ArrayList ()
525540 additional_attributes .add (
@@ -551,11 +566,15 @@ def parse_messages(self, messages, helper, teams_leveldb_file_path):
551566
552567 if "properties" in message :
553568 # process links
554- if "links" in message ["properties" ]:
569+ if not ( message ["properties" ]. get ( 'links' ) is None ) :
555570 for link in message ["properties" ]["links" ]:
556- url_attachments .add (URLAttachment (link ["url" ]))
571+ try :
572+ url_to_store = str (link .get ('url' ))
573+ url_attachments .add (URLAttachment (url_to_store ))
574+ except AttributeError :
575+ continue
557576 # process emotions
558- if "emotions" in message ["properties" ]:
577+ if not ( message ["properties" ]. get ( 'emotions' ) is None ) :
559578 for emotion in message ["properties" ]["emotions" ]:
560579 # emotions are grouped by their key like, heart..
561580 # Add one reaction entry by per
@@ -579,7 +598,7 @@ def parse_messages(self, messages, helper, teams_leveldb_file_path):
579598 file_attachments , url_attachments
580599 )
581600 helper .addAttachments (artifact , message_attachments )
582-
601+
583602 except TskCoreException as ex :
584603 # Severe error trying to add to case database.. case is not complete.
585604 # These exceptions are thrown by the CommunicationArtifactsHelper.
@@ -692,17 +711,26 @@ def get_level_db_file(self, content, filepath):
692711 dir_name = os .path .join (content .getParentPath (), content .getName ())
693712 results = file_manager .findFiles (data_source , filename , dir_name )
694713 if results .isEmpty ():
695- self .log (Level .INFO , f "Unable to locate { filename } " )
696- return None
697- return results .get (
714+ self .log (Level .INFO , "Unable to locate {}" . format ( filename ) )
715+ return
716+ db_file = results .get (
698717 0
699718 ) # Expect a single match so retrieve the first (and only) file
719+ return db_file
700720
701- def date_to_long (self , formatted_date ):
702- # Timestamp
703- dt = datetime .strptime (formatted_date [:19 ], "%Y-%m-%dT%H:%M:%S" )
704- time_struct = dt .timetuple ()
705- return int (calendar .timegm (time_struct ))
721+ def date_to_long (self , passed_date ):
722+ try :
723+ # Newer versions store the dates as unix timestamps
724+ composetime = float (passed_date )
725+ timestamp = int (composetime )
726+ # Cut off the miliseconds
727+ timestamp = int (timestamp / 1000 )
728+ except ValueError :
729+ # Timestamp
730+ dt = datetime .strptime (passed_date [:19 ], "%Y-%m-%dT%H:%M:%S" )
731+ time_struct = dt .timetuple ()
732+ timestamp = int (calendar .timegm (time_struct ))
733+ return timestamp
706734
707735 # Extract the direction of a phone call
708736 def deduce_call_direction (self , direction ):
@@ -715,10 +743,10 @@ def deduce_call_direction(self, direction):
715743 return call_direction
716744
717745 def deduce_message_direction (self , is_from_me ):
718- call_direction = CommunicationDirection .INCOMING
746+ message_direction = CommunicationDirection .INCOMING
719747 if is_from_me is True :
720- call_direction = CommunicationDirection .OUTGOING
721- return call_direction
748+ message_direction = CommunicationDirection .OUTGOING
749+ return message_direction
722750
723751 def create_artifact_type (self , artifact_name , artifact_description , blackboard ):
724752 try :
@@ -768,7 +796,9 @@ def process(self, data_source, progress_bar):
768796
769797 self .log (
770798 Level .INFO ,
771- f"Found { directories_to_process } { directory } directories to process." ,
799+ "Found {} {} directories to process." .format (
800+ directories_to_process , directory
801+ ),
772802 )
773803
774804 for i , content in enumerate (all_ms_teams_leveldbs ):
@@ -803,4 +833,4 @@ def process(self, data_source, progress_bar):
803833 "Finished analysing the LeveLDB from Microsoft Teams." ,
804834 )
805835 IngestServices .getInstance ().postMessage (message )
806- return IngestModule .ProcessResult .OK
836+ return IngestModule .ProcessResult .OK
0 commit comments