# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Mobius Forensic Toolkit
# Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024 Eduardo Aguiar
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 2, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
import traceback

import mobius
import pymobius
import pymobius.datasource.ufdr.parser

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Constants
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
ANT_ID = 'evidence.ufdr'
ANT_NAME = 'Evidence Finder Agent - UFDR'
ANT_VERSION = '1.0'
SAVE_THRESHOLD = 131072


# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Get party data
# @param Party object
# @return Party data object
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
def get_party_data(party):
    data = pymobius.Data()
    data.party_id = None
    data.party_name = None
    data.role = None
    data.is_phone_owner = None
    data.label = None

    if party:
        metadata = dict(party.metadata)

        data.party_id = metadata.get('Identifier')
        data.party_name = metadata.get('Name')
        data.role = metadata.get('Role')
        data.is_phone_owner = bool(metadata.get('IsPhoneOwner'))

        if data.party_id and data.party_name:
            data.label = f"{data.party_name} (ID={data.party_id})"

        else:
            data.label = data.party_name or data.party_id or ''

        if data.is_phone_owner:
            if data.label:
                data.label += '*'
            else:
                data.label = '<this-phone>'

    return data


# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Ant: UFDR Evidence
# @author Eduardo Aguiar
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class Ant(object):

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Initialize object
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def __init__(self, item):
        self.id = ANT_ID
        self.name = ANT_NAME
        self.version = ANT_VERSION

        self.__item = item
        self.__evidences = []
        self.__evidence_handlers = {}
        self.__unhandled_types = set()

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Run ant
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def run(self):
        mobius.core.logf(f"INF ant {self.id} started")

        try:
            # get UFDR path
            datasource = self.__item.get_datasource()
            uri = mobius.io.uri(datasource.get_url())
            path = uri.get_path('utf-8')

            # run UFDR parser
            parser = pymobius.datasource.ufdr.parser.UFDRParser(path, self)
            parser.run()

            # save remaining evidences
            self.__save_evidences()

            # log evidence_types found
            for e in sorted(self.__unhandled_types):
                mobius.core.logf(f"DEV unhandled evidence type: {e}")

            # log unknown_datatypes found
            for dt in sorted(parser.get_unknown_datatypes()):
                mobius.core.logf(f"DEV unknown datatype: {dt}")

        except Exception as e:
            mobius.core.logf(f'WRN {str(e)}\n{traceback.format_exc()}')

        mobius.core.logf(f"INF ant {self.id} ended")

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle on_element_end event
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_element_end(self, tag):
        # stop scanning at <project></decodedData>
        return tag == 'project.decodedData'

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle on_evidence event
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence(self, e):
        print()
        print(f'   ID: {e.id}')
        print(f'   Type: {e.type}')
        print(f'   Source idx: {e.source_index}')
        print(f'   Extraction ID: {e.extraction_id}')
        print(f'   Deleted state: {e.deleted_state}')
        print(f'   Decoding confidence: {e.decoding_confidence}')
        print(f'   Is carved: {e.is_carved}')
        print(f'   Is related: {e.is_related}')

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # get evidence handler
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        if e.type not in self.__evidence_handlers:
            self.__evidence_handlers[e.type] = getattr(
                self,
                f"on_evidence_{pymobius.camel_case_id_to_id(e.type, '_')}",
                None
            )

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # call evidence handler, if available
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        f = self.__evidence_handlers.get(e.type)
        if f:
            f(e)
        else:
            self.__unhandled_types.add(e.type)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle autofill evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_autofill(self, e):
        metadata = dict(e.metadata)

        evidence = pymobius.Data()
        evidence.type = 'autofill'

        evidence.attrs = {
            'fieldname': metadata.get('Key'),
            'value': metadata.get('Value'),
            'app': metadata.get('Source'),
            'username': None,
            'evidence_source': f"UFDR evidence #{e.id}"
        }

        evidence.metadata = mobius.pod.map()
        evidence.metadata.set('evidence-id', e.id)
        evidence.metadata.set('source-idx', e.source_index)
        evidence.metadata.set('extraction-id', e.extraction_id)
        evidence.metadata.set('deleted-state', e.deleted_state)
        evidence.metadata.set('decoding-confidence', e.decoding_confidence)
        evidence.metadata.set('account', metadata.get('Account'))
        evidence.metadata.set('timestamp', metadata.get('Timestamp'))
        evidence.metadata.set('last-used-date', metadata.get('LastUsedDate'))
        evidence.metadata.set('service-identifier', metadata.get('ServiceIdentifier'))
        evidence.metadata.set('user-mapping', metadata.get('UserMapping'))

        self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle Call evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_call(self, e):
        metadata = dict(e.metadata)

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # sources and destinations
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        sources = []
        destinations = []

        for party in metadata.get('Parties', []):
            party_data = get_party_data(party)

            if party_data.role == 'From':
                sources.append(party_data.label)

            else:
                destinations.append(party_data.label)

        direction = metadata.get('Direction')

        if not destinations and direction == 'Incoming':
            destinations.append('<this-phone>')

        if not sources and direction == 'Outgoing':
            sources.append('<this-phone>')

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # create evidence
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        evidence = pymobius.Data()
        evidence.type = 'call'

        evidence.attrs = {
            'timestamp': metadata.get('TimeStamp'),
            'duration': metadata.get('Duration'),
            'app': metadata.get('Source'),
            'source': ','.join(sources),
            'destination': destinations,
            'username': metadata.get('Account'),
            'evidence_source': f"UFDR evidence #{e.id}"
        }

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # metadata
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        evidence.metadata = mobius.pod.map()
        evidence.metadata.set('evidence-id', e.id)
        evidence.metadata.set('source-idx', e.source_index)
        evidence.metadata.set('extraction-id', e.extraction_id)
        evidence.metadata.set('deleted-state', e.deleted_state)
        evidence.metadata.set('decoding-confidence', e.decoding_confidence)
        evidence.metadata.set('type', metadata.get('Type'))
        evidence.metadata.set('direction', metadata.get('Direction'))
        evidence.metadata.set('status', metadata.get('Status'))
        evidence.metadata.set('is-video-call', metadata.get('VideoCall'))
        evidence.metadata.set('country-code', metadata.get('CountryCode'))
        evidence.metadata.set('network-code', metadata.get('NetworkCode'))
        evidence.metadata.set('network-name', metadata.get('NetworkName'))
        evidence.metadata.set('service-identifier', metadata.get('ServiceIdentifier'))
        evidence.metadata.set('user-mapping', metadata.get('UserMapping'))

        self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle Chat evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_chat(self, e):
        metadata = dict(e.metadata)

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # participants
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        participants = {}

        for party in metadata.get('Participants', []):
            party_data = get_party_data(party)
            participants[party_data.party_id] = party_data

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # chat messages
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        for idx, message in enumerate(metadata.get('Messages', []), 1):
            message_metadata = dict(message.metadata)

            # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
            # message extra data
            # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
            message_extra_data = message_metadata.get('MessageExtraData', [])
            text_type = 'text'

            for me_data in message_extra_data:
                d = dict(me_data.metadata)
                label = d.get('Label')

                if label == 'Forwarded':
                    party = d.get('OriginalSender')
                    message_metadata['OriginalSender'] = get_party_data(party).label
                    message_metadata['IsForwarded'] = True

                elif label == 'System':
                    text_type = 'system'

                else:
                    print(f"Unhandled message extra data: {d}")

            # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
            # sender and recipients
            # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
            from_is_phone_owner = False
            to_is_phone_owner = False
            from_party = message_metadata.get('From')
            sender = None
            recipients = []

            if from_party:
                from_party_data = get_party_data(from_party)
                sender_id = from_party_data.party_id
                sender = from_party_data.label
                from_is_phone_owner = from_party_data.is_phone_owner

                recipients = [party_data.label
                              for party_data in participants.values()
                              if party_data.party_id != sender_id]

                to_is_phone_owner = any(party_data.is_phone_owner
                                        for party_data in participants.values()
                                        if party_data.party_id != sender_id)

            # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
            # Evidence: Chat Message
            # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
            body = message_metadata.get('Body')
            if body:
                evidence = pymobius.Data()
                evidence.type = 'chat-message'

                evidence.attrs = {
                    'timestamp': message_metadata.get('TimeStamp'),
                    'sender': sender,
                    'recipients': recipients,
                    'text': [{'type': text_type, 'text': body}],
                    'app': message_metadata.get('SourceApplication') or message_metadata.get('Source'),
                    'username': metadata.get('Account'),
                }

                evidence.metadata = mobius.pod.map()
                evidence.metadata.set('evidence-id', message.id)
                evidence.metadata.set('source-idx', message.source_index)
                evidence.metadata.set('extraction-id', message.extraction_id)
                evidence.metadata.set('deleted-state', message.deleted_state)
                evidence.metadata.set('decoding-confidence', message.decoding_confidence)
                evidence.metadata.set('message-idx', idx)
                evidence.metadata.set('message-identifier', message_metadata.get('Identifier'))
                evidence.metadata.set('status', message_metadata.get('Status'))
                evidence.metadata.set('is-forwarded', bool(message_metadata.get('IsForwarded')))
                evidence.metadata.set('original-sender', message_metadata.get('OriginalSender'))

                self.__add_evidence(evidence)

            # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
            # message attachments
            # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
            for attachment in message_metadata.get('Attachments', []):
                attachment_metadata = dict(attachment.metadata)

                # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
                # Evidence: Sent file
                # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
                if from_is_phone_owner:
                    evidence = pymobius.Data()
                    evidence.type = 'sent-file'

                    evidence.attrs = {
                        'username': metadata.get('Account'),
                        'timestamp': message_metadata.get('TimeStamp'),
                        'filename': attachment_metadata.get('Filename'),
                        'app_name': message_metadata.get('SourceApplication') or metadata.get('Source'),
                        'evidence_source': f"UFDR evidence #{message.id}",
                    }

                    evidence.metadata = mobius.pod.map()
                    evidence.metadata.set('evidence-id', message.id)
                    evidence.metadata.set('source-idx', message.source_index)
                    evidence.metadata.set('extraction-id', message.extraction_id)
                    evidence.metadata.set('deleted-state', message.deleted_state)
                    evidence.metadata.set('decoding-confidence', message.decoding_confidence)
                    evidence.metadata.set('message-identifier', message_metadata.get('Identifier'))
                    evidence.metadata.set('message-idx', idx)
                    evidence.metadata.set('content-type', attachment_metadata.get('ContentType'))
                    evidence.metadata.set('attachment-extracted-path',
                                          attachment_metadata.get('attachment_extracted_path'))
                    evidence.metadata.set('sender', sender)
                    evidence.metadata.set('recipients', '\n'.join(sorted(recipients)))
                    evidence.metadata.set('is-forwarded', bool(message_metadata.get('IsForwarded')))
                    evidence.metadata.set('original-sender', message_metadata.get('OriginalSender'))

                    self.__add_evidence(evidence)

                # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
                # Evidence: Received file
                # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
                if to_is_phone_owner:
                    evidence = pymobius.Data()
                    evidence.type = 'received-file'

                    evidence.attrs = {
                        'username': metadata.get('Account'),
                        'timestamp': message_metadata.get('TimeStamp'),
                        'filename': attachment_metadata.get('Filename'),
                        'app_name': message_metadata.get('SourceApplication') or metadata.get('Source'),
                        'evidence_source': f"UFDR evidence #{message.id}",
                    }

                    evidence.metadata = mobius.pod.map()
                    evidence.metadata.set('evidence-id', message.id)
                    evidence.metadata.set('source-idx', message.source_index)
                    evidence.metadata.set('extraction-id', message.extraction_id)
                    evidence.metadata.set('deleted-state', message.deleted_state)
                    evidence.metadata.set('decoding-confidence', message.decoding_confidence)
                    evidence.metadata.set('message-identifier', message_metadata.get('Identifier'))
                    evidence.metadata.set('message-idx', idx)
                    evidence.metadata.set('content-type', attachment_metadata.get('ContentType'))
                    evidence.metadata.set('attachment-extracted-path',
                                          attachment_metadata.get('attachment_extracted_path'))
                    evidence.metadata.set('sender', sender)
                    evidence.metadata.set('recipients', '\n'.join(sorted(recipients)))
                    evidence.metadata.set('is-forwarded', bool(message_metadata.get('IsForwarded')))
                    evidence.metadata.set('original-sender', message_metadata.get('OriginalSender'))

                    self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle Cookie evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_cookie(self, e):
        metadata = dict(e.metadata)

        last_update_time = None
        if metadata.get('CreationTime') == metadata.get('LastAccessTime'):
            last_update_time = metadata.get('CreationTime')

        evidence = pymobius.Data()
        evidence.type = 'cookie'

        evidence.attrs = {
            'name': metadata.get('Name'),
            'value': metadata.get('Value'),
            'is_encrypted': False,
            'is_deleted': bool(e.deleted_state == "Deleted"),
            'domain': metadata.get('Domain'),
            'creation_time': metadata.get('CreationTime'),
            'last_access_time': metadata.get('LastAccessTime'),
            'expiration_time': metadata.get('Expiry'),
            'last_update_time': last_update_time,
            'username': None,
            'app_name': metadata.get('Source'),
            'evidence_source': f"UFDR evidence #{e.id}",
        }

        evidence.metadata = mobius.pod.map()
        evidence.metadata.set('evidence-id', e.id)
        evidence.metadata.set('source-idx', e.source_index)
        evidence.metadata.set('extraction-id', e.extraction_id)
        evidence.metadata.set('deleted-state', e.deleted_state)
        evidence.metadata.set('decoding-confidence', e.decoding_confidence)
        evidence.metadata.set('related-application', metadata.get('RelatedApplication'))
        evidence.metadata.set('path', metadata.get('Path'))
        evidence.metadata.set('service-identifier', metadata.get('ServiceIdentifier'))
        evidence.metadata.set('user-mapping', metadata.get('UserMapping'))

        self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle FileDownload evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_file_download(self, e):
        metadata = dict(e.metadata)

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # file info
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        filename = None
        file_id = None

        file_info = metadata.get('File')
        if file_info:
            file_id = file_info.id
            filename = file_info.name

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # create evidence
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        evidence = pymobius.Data()
        evidence.type = 'received-file'

        evidence.attrs = {
            'path': metadata.get('TargetPath'),
            'timestamp': metadata.get('StartTime'),
            'app_name': metadata.get('Source'),
            'filename': filename,
            'evidence_source': f"UFDR evidence #{e.id}",
        }

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # set metadata
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        evidence.metadata = mobius.pod.map()
        evidence.metadata.set('evidence-id', e.id)
        evidence.metadata.set('source-idx', e.source_index)
        evidence.metadata.set('extraction-id', e.extraction_id)
        evidence.metadata.set('deleted-state', e.deleted_state)
        evidence.metadata.set('decoding-confidence', e.decoding_confidence)
        evidence.metadata.set('url', metadata.get('Url'))
        evidence.metadata.set('file-size', metadata.get('FileSize'))
        evidence.metadata.set('file-id', file_id)

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # additional info
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        for info in metadata.get('AdditionalInfo', []):
            info_metadata = dict(info.metadata)
            key = info_metadata.get('Key')
            value = info_metadata.get('Value')
            evidence.metadata.set(key, value)

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # remaining metadata
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        evidence.metadata.set('start-time', metadata.get('StartTime'))
        evidence.metadata.set('end-time', metadata.get('EndTime'))
        evidence.metadata.set('bytes-received', metadata.get('BytesReceived'))
        evidence.metadata.set('download-state', metadata.get('DownloadState'))
        evidence.metadata.set('last-accessed', metadata.get('LastAccessed'))
        evidence.metadata.set('service-identifier', metadata.get('ServiceIdentifier'))
        evidence.metadata.set('user-mapping', metadata.get('UserMapping'))

        self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle InstalledApplication evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_installed_application(self, e):
        metadata = dict(e.metadata)

        evidence = pymobius.Data()
        evidence.type = 'installed-program'

        evidence.attrs = {
            'name': metadata.get('Name') or metadata.get('Identifier'),
            'version': metadata.get('Version'),
            'description': metadata.get('Description'),
            'evidence_source': f"UFDR evidence #{e.id}",
        }

        evidence.metadata = mobius.pod.map()
        evidence.metadata.set('evidence-id', e.id)
        evidence.metadata.set('source-idx', e.source_index)
        evidence.metadata.set('extraction-id', e.extraction_id)
        evidence.metadata.set('deleted-state', e.deleted_state)
        evidence.metadata.set('decoding-confidence', e.decoding_confidence)
        evidence.metadata.set('decoding-status', metadata.get('DecodingStatus'))
        evidence.metadata.set('app-guid', metadata.get('AppGUID'))
        evidence.metadata.set('categories', ','.join(metadata.get('Categories', [])))
        evidence.metadata.set('copyright', metadata.get('Copyright'))
        evidence.metadata.set('identifier', metadata.get('Identifier'))
        evidence.metadata.set('is-emulatable', metadata.get('IsEmulatable'))
        evidence.metadata.set('last-launched', metadata.get('LastLaunched'))
        evidence.metadata.set('operation-mode', metadata.get('OperationMode'))
        evidence.metadata.set('permissions', ','.join(metadata.get('Permissions', [])))
        evidence.metadata.set('purchase-date', metadata.get('PurchaseDate'))

        self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle InstantMessage evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_instant_message(self, e):
        metadata = dict(e.metadata)

        if metadata.get('Identifier'):
            mobius.core.logf(f"DEV InstantMessage.Identifier attribute found: {metadata.get('Identifier')}")

        if metadata.get('Attachment'):
            mobius.core.logf(f"DEV InstantMessage.Attachment attribute found: {metadata.get('Attachment')}")

        if metadata.get('SharedContacts'):
            mobius.core.logf(f"DEV InstantMessage.SharedContacts attribute found: {metadata.get('SharedContacts')}")

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # sender and recipients
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        sender = None
        recipients = []

        from_party = metadata.get('From')

        if from_party:
            from_party_data = get_party_data(from_party)
            sender = from_party_data.label

        for to_party in metadata.get('To', []):
            to_party_data = get_party_data(to_party)
            recipients.append(to_party_data.label)

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # create evidence
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        evidence = pymobius.Data()
        evidence.type = 'instant-message'

        evidence.attrs = {
            'timestamp': metadata.get('TimeStamp'),
            'message_type': metadata.get('Type'),
            'text': metadata.get('Body'),
            'app': metadata.get('SourceApplication'),
            'sender': sender,
            'recipients': recipients,
            'evidence_source': f"UFDR evidence #{e.id}",
        }

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # metadata
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        evidence.metadata = mobius.pod.map()
        evidence.metadata.set('evidence-id', e.id)
        evidence.metadata.set('source-idx', e.source_index)
        evidence.metadata.set('extraction-id', e.extraction_id)
        evidence.metadata.set('deleted-state', e.deleted_state)
        evidence.metadata.set('decoding-confidence', e.decoding_confidence)
        evidence.metadata.set('smsc', metadata.get('SMSC'))
        evidence.metadata.set('status', metadata.get('Status'))
        evidence.metadata.set('source', metadata.get('Source'))
        evidence.metadata.set('folder', metadata.get('Folder'))
        evidence.metadata.set('service-identifier', metadata.get('ServiceIdentifier'))
        evidence.metadata.set('user-mapping', metadata.get('UserMapping'))

        self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle SearchedItem evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_searched_item(self, e):
        metadata = dict(e.metadata)

        evidence = pymobius.Data()
        evidence.type = 'searched-text'

        evidence.attrs = {
            'timestamp': metadata.get('TimeStamp'),
            'search_type': metadata.get('Source'),
            'text': metadata.get('Value'),
            'username': metadata.get('Account'),
            'evidence_source': f"UFDR evidence #{e.id}",
        }

        evidence.metadata = mobius.pod.map()
        evidence.metadata.set('evidence-id', e.id)
        evidence.metadata.set('source-idx', e.source_index)
        evidence.metadata.set('extraction-id', e.extraction_id)
        evidence.metadata.set('deleted-state', e.deleted_state)
        evidence.metadata.set('decoding-confidence', e.decoding_confidence)
        evidence.metadata.set('origin', metadata.get('Origin'))
        evidence.metadata.set('service-identifier', metadata.get('ServiceIdentifier'))
        evidence.metadata.set('user-mapping', metadata.get('UserMapping'))

        self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle UserAccount evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_user_account(self, e):
        metadata = dict(e.metadata)

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # create evidence
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        evidence = pymobius.Data()
        evidence.type = 'user-account'

        evidence.attrs = {
            'account_type': metadata.get('ServiceType') or f"app/{metadata.get('Source')}",
            'id': metadata.get('Username'),
            'name': metadata.get('Name'),
            'evidence_source': f"UFDR evidence #{e.id}",
        }

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # password
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        password = metadata.get('Password')

        if password is not None:
            evidence.attrs['password'] = password
            evidence.attrs['password_found'] = 'yes'
        else:
            evidence.attrs['password_found'] = 'no'

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # metadata
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        evidence.metadata = mobius.pod.map()
        evidence.metadata.set('evidence-id', e.id)
        evidence.metadata.set('source-idx', e.source_index)
        evidence.metadata.set('extraction-id', e.extraction_id)
        evidence.metadata.set('deleted-state', e.deleted_state)
        evidence.metadata.set('decoding-confidence', e.decoding_confidence)
        evidence.metadata.set('creation-time', metadata.get('TimeCreated'))
        evidence.metadata.set('source', metadata.get('Source'))
        evidence.metadata.set('service-identifier', metadata.get('ServiceIdentifier'))
        evidence.metadata.set('user-mapping', metadata.get('UserMapping'))

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # additional info
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        for info in metadata.get('AdditionalInfo', []):
            info_metadata = dict(info.metadata)
            key = info_metadata.get('Key')
            value = info_metadata.get('Value')
            evidence.metadata.set(key, value)

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # entries
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        entries = metadata.get('Entries', [])

        for entry in entries:
            entry_metadata = dict(entry.metadata)
            key = f"{entry_metadata.get('Domain')}/{entry_metadata.get('Category')}"
            value = entry_metadata.get('Value')
            evidence.metadata.set(key, value)

        # @todo UserAccount: Addresses,Notes,Organizations,Photos,ServerAddress

        self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle VisitedPage evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_visited_page(self, e):
        metadata = dict(e.metadata)

        evidence = pymobius.Data()
        evidence.type = 'visited-url'

        evidence.attrs = {
            'timestamp': metadata.get('LastVisited'),
            'url': metadata.get('Url'),
            'title': metadata.get('Title'),
            'username': metadata.get('Account'),
            'evidence_source': f"UFDR evidence #{e.id}",
        }

        evidence.metadata = mobius.pod.map()
        evidence.metadata.set('evidence-id', e.id)
        evidence.metadata.set('source-idx', e.source_index)
        evidence.metadata.set('extraction-id', e.extraction_id)
        evidence.metadata.set('deleted-state', e.deleted_state)
        evidence.metadata.set('decoding-confidence', e.decoding_confidence)
        evidence.metadata.set('artifact-family', metadata.get('ArtifactFamily'))
        evidence.metadata.set('last-visited', metadata.get('LastVisited'))
        evidence.metadata.set('visit-count', metadata.get('VisitCount'))
        evidence.metadata.set('source', metadata.get('Source'))
        evidence.metadata.set('usage-pattern', metadata.get('UsagePattern'))
        evidence.metadata.set('url-cache-file', metadata.get('UrlCacheFile'))
        evidence.metadata.set('can-rebuild-cache-file', metadata.get('CanRebuildCacheFile'))
        evidence.metadata.set('service-identifier', metadata.get('ServiceIdentifier'))
        evidence.metadata.set('user-mapping', metadata.get('UserMapping'))

        self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle WebBookmark evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_web_bookmark(self, e):
        metadata = dict(e.metadata)

        evidence = pymobius.Data()
        evidence.type = 'bookmarked-url'

        evidence.attrs = {
            'url': metadata.get('Url'),
            'app_name': metadata.get('Source'),
            'name': metadata.get('Title'),
            'folder': metadata.get('Path'),
            'evidence_source': f"UFDR evidence #{e.id}",
        }

        evidence.metadata = mobius.pod.map()
        evidence.metadata.set('evidence-id', e.id)
        evidence.metadata.set('source-idx', e.source_index)
        evidence.metadata.set('extraction-id', e.extraction_id)
        evidence.metadata.set('deleted-state', e.deleted_state)
        evidence.metadata.set('decoding-confidence', e.decoding_confidence)
        evidence.metadata.set('timestamp', metadata.get('Timestamp'))
        evidence.metadata.set('last-visited', metadata.get('LastVisited'))
        evidence.metadata.set('visit-count', metadata.get('VisitCount'))
        evidence.metadata.set('service-identifier', metadata.get('ServiceIdentifier'))
        evidence.metadata.set('user-mapping', metadata.get('UserMapping'))

        self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle WirelessNetwork evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def on_evidence_wireless_network(self, e):
        metadata = dict(e.metadata)

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # Wireless connection
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        timestamps = {metadata.get('TimeStamp'),
                      metadata.get('LastAutoConnection'),
                      metadata.get('LastConnection')
                      }
        timestamps.discard(None)

        for timestamp in timestamps:
            evidence = pymobius.Data()
            evidence.type = 'wireless-connection'

            evidence.attrs = {
                'timestamp': timestamp,
                'bssid': metadata.get('BSSId'),
                'ssid': metadata.get('SSId'),
                'evidence_source': f"UFDR evidence #{e.id}",
            }

            evidence.metadata = mobius.pod.map()
            evidence.metadata.set('evidence-id', e.id)
            evidence.metadata.set('source-idx', e.source_index)
            evidence.metadata.set('extraction-id', e.extraction_id)
            evidence.metadata.set('deleted-state', e.deleted_state)
            evidence.metadata.set('decoding-confidence', e.decoding_confidence)
            evidence.metadata.set('current-password', metadata.get('Password'))
            evidence.metadata.set('current-security-mode', metadata.get('SecurityMode'))

            self.__add_evidence(evidence)

        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        # Wireless network
        # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        evidence = pymobius.Data()
        evidence.type = 'wireless-network'

        evidence.attrs = {
            'bssid': metadata.get('BSSId'),
            'ssid': metadata.get('SSId'),
            'password': metadata.get('Password'),
            'evidence_source': f"UFDR evidence #{e.id}",
        }

        evidence.metadata = mobius.pod.map()
        evidence.metadata.set('evidence-id', e.id)
        evidence.metadata.set('source-idx', e.source_index)
        evidence.metadata.set('extraction-id', e.extraction_id)
        evidence.metadata.set('deleted-state', e.deleted_state)
        evidence.metadata.set('decoding-confidence', e.decoding_confidence)
        evidence.metadata.set('timestamp', metadata.get('TimeStamp'))
        evidence.metadata.set('last-connection', metadata.get('LastConnection'))
        evidence.metadata.set('last-auto-connection', metadata.get('LastAutoConnection'))
        evidence.metadata.set('end-time', metadata.get('EndTime'))
        evidence.metadata.set('security-mode', metadata.get('SecurityMode'))

        self.__add_evidence(evidence)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Add evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def __add_evidence(self, e):
        self.__evidences.append(e)

        if len(self.__evidences) > SAVE_THRESHOLD:
            self.__save_evidences()

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Save evidences into case DB
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def __save_evidences(self):
        print('SAVING!!!')
        transaction = self.__item.new_transaction()

        for e in self.__evidences:
            evidence = self.__item.new_evidence(e.type)

            for key, value in e.attrs.items():
                setattr(evidence, key, value)

            evidence.metadata = e.metadata

        transaction.commit()

        self.__evidences = []

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Handle Password evidence
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # def on_evidence_Password(self, e):
    #     metadata = dict(e.metadata)
    #
    #     print()
    #     for key, value in metadata.items():
    #         print(f"{key}: {value}")
    #
    #     try:
    #         value = base64.b64decode(metadata.get('Data'))
    #     except Exception:
    #         pass
    #
    #     print(f"value: {value}")
    #
    #     #print(metadata, ))
    #
    #     # Password                  AccessGroup,Data,GenericAttribute,,Server,Service,,Source,
    #
    #     evidence_metadata = mobius.pod.map()
    #     evidence_metadata.set('account', metadata.get('Account'))
    #     evidence_metadata.set('service-identifier', metadata.get('ServiceIdentifier'))
    #     evidence_metadata.set('user-mapping', metadata.get('UserMapping'))
    #
    #     # evidence = self.__item.new_evidence('password')
    #     # evidence.password_type = metadata.get('Type')
    #     # evidence.description = metadata.get('Label')
    #     # evidence.value = metadata.get('Data')
    #     # evidence.evidence_source = f"UFDR evidence #{e.id}"
    #     # evidence.metadata = evidence_metadata
    #
    #     #passwords = [(p.type, p.description, p.value, p) for p in self.__passwords]
    #
    #     #for p_type, p_description, p_value, p_obj in sorted(passwords, key=lambda x: x[:3]):
    #     #    metadata = mobius.pod.map()
