# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# 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.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)

    # Shareaza folders
    #for username, key in pymobius.registry.iter_hkey_users (registry):
    #  shareaza_key = key.get_key_by_path ('Software\\Shareaza\\Shareaza')

    #  if shareaza_key:
    #    user_path = get_data_as_string (shareaza_key.get_data_by_name ('UserPath'))
    #    collection_path = get_data_as_string (shareaza_key.get_data_by_path ('Downloads\\CollectionPath'))
    #    complete_path = get_data_as_string (shareaza_key.get_data_by_path ('Downloads\\CompletePath'))
    #    incomplete_path = get_data_as_string (shareaza_key.get_data_by_path ('Downloads\\IncompletePath'))
    #    torrent_path = get_data_as_string (shareaza_key.get_data_by_path ('Downloads\\TorrentPath'))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @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
