# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Mobius Forensic Toolkit
# Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023 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 os.path
import traceback
import pymobius.app.chromium
import pymobius.app.skype
import pymobius.app.utorrent
import pymobius.ant.cookies
import pymobius.ant.turing
import mobius

ANT_ID = 'accounts'
ANT_NAME = 'Accounts'
ANT_VERSION = '1.0'

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Decode login.live.com MSSPre cookie
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
def func_login_live_com (c, data):
  c_data = c.value.decode ('utf-8').split ('|')
  data.id = c_data[0]
  data.metadata.set ('mspcid', c_data[1])

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Decode generic cookie
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
def func_value (c, data):
  data.id = c.value.decode ('utf-8')
   
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Decode slideplayer.com.br login cookie
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
def func_slideplayer_com_br (c, data):
  c_data = c.value.decode ('utf-8').split ('+')
  data.id = c_data[0]
  data.name = c_data[1]
  data.password = c_data[2]

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Decode hotmart.com __kdtc cookie
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
def func_hotmart_com (c, data):
  #s = 'cid%3Dswdsil%40yahoo.com.br%3Bt%3D1569186164243'
  u = mobius.io.uri (c.value.decode ('ascii'))
  text = u.get_path ('utf-8')

  if text.startswith ('cid='):
    pos = text.find (';')

    if pos != -1:
      data.id = text[4:pos]

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Cookies by domain and cookie name
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
COOKIES = {
   ('hotmart.com', '__kdtc') : 'hotmart_com',
   ('imvu.com', '_imvu_avnm') : 'value',
   ('login.live.com', 'MSPPre') : 'login_live_com',
   ('pof.com', 'usernameb') : 'value',
   ('pof.com.br', 'usernameb') : 'value',
   ('slideplayer.com.br', 'login') : 'slideplayer_com_br',
}

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Cookie decoders
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
COOKIE_DECODERS = {
  'hotmart_com' : func_hotmart_com,
  'login_live_com' : func_login_live_com,
  'slideplayer_com_br' : func_slideplayer_com_br,
  'value' : func_value
}

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Ant: Accounts
# @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

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Run ant
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def run (self):
    if not self.__item.has_vfs ():
      return

    self.__entries = []
    self.__retrieve_chromium ()
    self.__retrieve_cookies ()
    self.__retrieve_passwords ()
    self.__retrieve_skype ()
    self.__retrieve_utorrent ()

    self.entries = self.__entries
    self.__save_data ()

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Chromium based browsers
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_chromium (self):
    try:
      model = pymobius.app.chromium.model (self.__item)

      for profile in model.get_profiles ():
        self.__retrieve_chromium_profile (profile)
    except Exception as e:
      mobius.core.logf ('WRN %s %s' % (str (e), traceback.format_exc ()))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Chromium profile
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_chromium_profile (self, profile):
    try:
      for account in profile.get_accounts ():
         entry = pymobius.Data ()
         entry.type = 'app.%s' % profile.app_id
         entry.id = account.id
         entry.name = account.name
         entry.password = None
         entry.metadata = mobius.pod.map ()
         entry.source = account.source
         entry.metadata.set ('fullname', account.fullname)
         entry.metadata.set ('locale', account.locale)
         entry.metadata.set ('email', account.email)
         entry.metadata.set ('profile_path', profile.path)
         entry.metadata.set ('username', profile.username)
         entry.metadata.set ('app_id', profile.app_id)
         entry.metadata.set ('app_name', profile.app_name)
         self.__entries.append (entry)
    except Exception as e:
      mobius.core.logf ('WRN %s %s' % (str (e), traceback.format_exc ()))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from cookies
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_cookies (self):

    # run ant.cookies, if necessary
    if not self.__item.has_ant ('cookies'):
      ant = pymobius.ant.cookies.Ant (self.__item)
      ant.run ()

    # retrieve data
    for c in self.__item.get_cookies ():
      if not c.is_encrypted:
        try:
          self.__retrieve_cookie (c)
        except Exception as e:
          mobius.core.logf ('WRN %s %s' % (str (e), traceback.format_exc ()))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from cookie
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_cookie (self, c):

    # get cookie decoder
    decoder_name = COOKIES.get ((c.domain, c.name))
    if not decoder_name:
      return

    # get decoder function
    decoder = COOKIE_DECODERS.get (decoder_name)
    if not decoder:
      mobius.core.logf ('WRN Decoder "%s" not found' % decoder_name)
      return

    # run decoder function
    acc = pymobius.Data ()
    acc.id = None
    acc.name = None
    acc.password = None
    acc.metadata = mobius.pod.map ()

    decoder (c, acc)
    if not acc.id:
      return

    # create account
    acc.type = 'web.' + c.domain
    acc.source = 'Cookie "%s". Created in %s' % (c.name, c.creation_time)
    acc.metadata.set ('cookie_creation_time', c.creation_time)
    acc.metadata.set ('cookie_domain', c.domain)
    acc.metadata.set ('cookie_name', c.name)
    acc.metadata.set ('evidence_path', c.evidence_path)
          
    if c.profile:
      acc.metadata.set ('profile_path', c.profile.path)
      acc.metadata.set ('profile_id', c.profile.id)
      acc.metadata.set ('username', c.profile.username)
      acc.metadata.set ('app_id', c.profile.application.id)
      acc.metadata.set ('app_name', c.profile.application.name)

    self.__entries.append (acc)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from passwords
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_passwords (self):

    # run ant.turing, if necessary
    if not self.__item.has_ant ('turing'):
      ant = pymobius.ant.turing.Ant (self.__item)
      ant.run ()

    # retrieve data
    for p in self.__item.get_passwords ():
      try:
        self.__retrieve_password (p)
      except Exception as e:
        mobius.core.logf ('WRN %s %s' % (str (e), traceback.format_exc ()))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from password
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_password (self, p):
    metadata = p.metadata.to_python ()
    
    acc = pymobius.Data ()
    acc.type = None
    acc.id = None
    acc.name = None
    acc.password = None
    acc.metadata = mobius.pod.map ()

    if p.type == 'net.http':
      url = metadata.get ('URL')
      uri = mobius.io.uri (url)

      acc.type = 'web.' + uri.get_host ()
      acc.id = metadata.get ('User ID')
      acc.password = p.value
      acc.source = metadata.get ('Application')
      acc.metadata.set ('domain', uri.get_host ())
      acc.metadata.set ('login_url', url)
      acc.metadata.set ('profile_path', metadata.get ('Profile path'))
 
    elif p.type == 'net.account':
      metadata = p.metadata.to_python ()
      domain = p.metadata.get ('Domain', '')

      if domain.startswith ('WindowsLive:name='):
        acc.type = 'web.live.com'
        acc.id = metadata.get ('Account')
        acc.password = p.value
        acc.source = 'Win Credential file'
        acc.metadata.set ('credential_file_path', metadata.get ('Credential file path'))

    elif p.type == 'os.user':
      metadata = p.metadata.to_python ()

      acc.type = 'os.user'
      acc.id = metadata.get ('username')
      acc.password = p.value
      acc.name = metadata.get ('fullname') or metadata.get ('admin_comment')
      acc.source = 'Win Registry'

    # add account, if it is valid
    if acc.type and acc.id:
      self.__entries.append (acc)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Skype
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_skype (self):

    try:
      model = pymobius.app.skype.model (self.__item)

      for profile in model.get_profiles ():
        self.__retrieve_skype_profile (profile)

    except Exception as e:
      mobius.core.logf ('WRN %s %s' % (str (e), traceback.format_exc ()))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Skype profile
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_skype_profile (self, profile):
    try:
      for account in profile.get_accounts ():
        entry = pymobius.Data ()
        entry.type = 'app.skype'
        entry.id = account.id
        entry.name = account.fullname
        entry.source = profile.path
        entry.password = None
        entry.metadata = mobius.pod.map ()
        entry.metadata.set ('username', profile.username)
        entry.metadata.set ('app_id', 'skype')
        entry.metadata.set ('app_name', 'Skype')
        entry.metadata.set ('profile_path', profile.path)
        entry.metadata.set ('fullname', account.fullname)
        entry.metadata.set ('mood_text', account.mood_text)
        entry.metadata.set ('city', account.city)
        entry.metadata.set ('province', account.province)
        entry.metadata.set ('country', account.country)
        entry.metadata.set ('emails', ', '.join (account.emails))
        
        if account.birthday:
          entry.metadata.set ('birthday', account.birthday)
          
        if account.gender == 1:
          entry.metadata.set ('gender', 'male')

        elif account.gender == 2:
          entry.metadata.set ('gender', 'female')

        if account.type == 1:		# main.db based
          self.__retrieve_skype_profile_v1 (entry, account)
          
        elif account.type == 2:		# s4l.db based
          self.__retrieve_skype_profile_v2 (entry, account)

        self.__entries.append (entry)

    except Exception as e:
      mobius.core.logf ('WRN %s %s' % (str (e), traceback.format_exc ()))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Skype profile v1 (main.db based)
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_skype_profile_v1 (self, entry, account):
    entry.metadata.set ('homepage', account.homepage)
    entry.metadata.set ('about', account.about)
    entry.metadata.set ('languages', account.languages)
    entry.metadata.set ('phone_home', account.phone_home)
    entry.metadata.set ('phone_office', account.phone_office)
    entry.metadata.set ('phone_mobile', account.phone_mobile)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Skype profile v2 (s4l.db based)
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_skype_profile_v2 (self, entry, account):
    entry.metadata.set ('phones', ' ,'.join (account.phones))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from µTorrent
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_utorrent (self):

    try:
      model = pymobius.app.utorrent.model (self.__item)

      for profile in model.get_profiles ():
        self.__retrieve_utorrent_profile (profile)

    except Exception as e:
      mobius.core.logf ('WRN %s %s' % (str (e), traceback.format_exc ()))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from µTorrent profile
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_utorrent_profile (self, profile):
    try:
      for account in profile.get_accounts ():
        entry = pymobius.Data ()
        entry.type = 'p2p.bittorrent'
        entry.id = account.guid
        entry.name = None
        entry.source = profile.path
        entry.password = None
        entry.metadata = mobius.pod.map ()
        entry.metadata.set ('username', account.username)
        entry.metadata.set ('app_id', account.app_id)
        entry.metadata.set ('app_name', account.app_name)
        entry.metadata.set ('network', 'BitTorrent')
        entry.metadata.set ('profile_path', profile.path)
        self.__entries.append (entry)

    except Exception as e:
      mobius.core.logf ('WRN %s %s' % (str (e), traceback.format_exc ()))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Save data into model
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __save_data (self):

    case = self.__item.case
    transaction = case.new_transaction ()

    # remove old data
    self.__item.remove_accounts ()

    # save text searches
    for e in self.__entries:
      account = self.__item.new_account (e.type, e.id)
      
      if e.name:
        account.name = e.name
        
      if e.password != None:
        account.password = e.password

      account.source = e.source
      #account.avatars = acc.avatars
      account.metadata = e.metadata

    # set ant run
    self.__item.set_ant (ANT_ID, ANT_NAME, ANT_VERSION)
    transaction.commit ()
