# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Mobius Forensic Toolkit
# Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025 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.registry.installed_programs
from pymobius.registry import *
import pymobius.p2p.application
from . import decoder_sd
from . import decoder_library_dat
from . import decoder_profile_xml
from . import decoder_searches_dat


# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# All Date/Times are stored in Coordinated Universal Time (UTC).
# @see https://msdn.microsoft.com/pt-br/library/windows/desktop/ms724397(v=vs.85).aspx
#
# References:
#   . Shareaza 2.7.10.2 source code
#
# Shareaza main files:
#   . Searches.dat - Stores search history and search results
#   . Library1.dat and Library2.dat - Stores configuration for local folders and files
#   . *.sd files - Download control files (one per downloading file)
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Generic dataholder
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class dataholder(object):
    pass


# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Iterate through Shareaza AppData folders
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
def iter_shareaza_folders(item):
    for opsys in pymobius.operating_system.scan(item):
        for user_profile in opsys.get_profiles():
            try:
                folder = user_profile.get_entry_by_path('%appdata%/Shareaza')
                if folder:
                    yield user_profile, folder
            except Exception as e:
                mobius.core.logf('WRN %s %s' % (str(e), traceback.format_exc()))


# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Retrieves Shareaza P2P activity data
# @author Eduardo Aguiar
# This function is planned to run in an independent thread. The idea here
# is to gather all activity data and only then write data to the model
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
def retrieve(model):
    try:
        ant = Ant(model)
        ant.run()
    except Exception as e:
        mobius.core.logf('WRN %s %s' % (str(e), traceback.format_exc()))


# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief forensics: P2P Shareaza
# @author Eduardo Aguiar
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class Ant(object):

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief initialize object
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def __init__(self, model):
        self.__model = model  # P2P model
        self.__item = model.item

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief run
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def run(self):

        # create local data model
        self.__data = dataholder()
        self.__data.application = pymobius.p2p.application.application()
        self.__data.application.id = 'shareaza'
        self.__data.application.name = 'Shareaza'

        self.__data.accounts = []
        self.__data.searches = []
        self.__data.local_files = []
        self.__data.remote_files = []

        # retrieve data
        self.__retrieve_registry_data()
        self.__retrieve_app_data()
        self.__normalize_data()

        # update P2P model
        self.__model.applications.append(self.__data.application)
        self.__model.accounts += self.__data.accounts
        self.__model.searches += self.__data.searches
        self.__model.local_files += self.__data.local_files
        self.__model.remote_files += self.__data.remote_files

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief retrieve registry data
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def __retrieve_registry_data(self):
        ant = pymobius.registry.main.Ant(self.__item)

        # Program version
        for registry in ant.get_data():
            for program in pymobius.registry.installed_programs.get(registry):
                if program.display_name.startswith('Shareaza '):
                    self.__data.application.versions.add(program.version)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Retrieve data from disk files
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def __retrieve_app_data(self):

        # scan shareaza profiles
        for user_profile, folder in iter_shareaza_folders(self.__item):
            try:
                data_folder = folder.get_child_by_name('Data', False)
                self.__retrieve_data_folder(data_folder, user_profile.username)

                incomplete_folder = user_profile.get_entry_by_path('%localappdata%/Shareaza/Incomplete')

                self.__retrieve_incomplete_folder(incomplete_folder, user_profile.username)
            except Exception as e:
                mobius.core.logf('WRN %s %s' % (str(e), traceback.format_exc()))

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Retrieve Data folder evidence files
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def __retrieve_data_folder(self, folder, username):
        if not folder:
            return

        f = folder.get_child_by_name('Searches.dat', False)
        decoder_searches_dat.retrieve(self.__item, self.__data, f, username)

        f = folder.get_child_by_name('Library1.dat', False)
        if not f:
            f = folder.get_child_by_name('Library2.dat', False)
        decoder_library_dat.retrieve(self.__item, self.__data, f, username)

        f = folder.get_child_by_name('Profile.xml', False)
        decoder_profile_xml.retrieve(self.__item, self.__data, f, username)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief retrieve incomplete control files
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def __retrieve_incomplete_folder(self, folder, username):

        if not folder or not folder.is_folder():
            return

        for f in folder.get_children():
            if f.name.endswith('.sd'):
                decoder_sd.retrieve(self.__item, self.__data, f, username)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief normalize retrieved data
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def __normalize_data(self):

        # sort and remove duplicated remote files
        remote_files = [(f.timestamp, f) for f in self.__data.remote_files]
        timestamp, peer_ip, size, name = None, None, None, None
        self.__data.remote_files = []

        for (timestamp, f) in sorted(remote_files, key=lambda x: x[0]):
            if (timestamp, peer_ip, size, name) != (f.timestamp, f.peer.ip, f.size, f.name):
                self.__data.remote_files.append(f)
                timestamp, peer_ip, size, name = f.timestamp, f.peer.ip, f.size, f.name
