# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Mobius Forensic Toolkit
# Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019 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 pymobius.app.chrome
import pymobius.app.firefox
import pymobius.app.geckofx
import pymobius.app.internet_explorer
import pymobius.app.opera
import datetime

ANT_ID = 'cookies'
ANT_NAME = 'Cookies'
ANT_VERSION = '1.0'

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Generic cookie class
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class Cookie (object):

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Initialize object
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __init__ (self):
    self.is_encrypted = False
    self.is_deleted = False
    self.last_access_time = None
    self.expiration_time = None

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

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Initialize object
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __init__ (self, item):
    self.name = 'Web Cookies Agent'
    self.version = '1.0'
    self.__item = item

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

    self.__entries = []

    # @begin-deprecated since: 1.14    
    if self.__item.has_attribute ('data.cookies'):
      self.__retrieve_data_cookies ()

    else:
      # @end-deprecated
      self.__retrieve_chrome ()
      self.__retrieve_firefox ()
      self.__retrieve_geckofx ()
      self.__retrieve_internet_explorer ()
      self.__retrieve_opera ()

    self.__save_data ()

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Google Chrome
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_chrome (self):
    model = pymobius.app.chrome.model (self.__item)
    
    for profile in model.get_profiles ():
      for entry in profile.get_cookies ():
        cookie = Cookie ()
        cookie.name = entry.name
        cookie.value = entry.value
        cookie.domain = entry.domain
        cookie.creation_time = entry.creation_time
        cookie.last_access_time = entry.last_access_time
        cookie.evidence_path = entry.evidence_path
        cookie.is_deleted = entry.is_deleted
        cookie.is_encrypted = entry.is_encrypted
        cookie.profile = profile
        self.__entries.append (cookie)
        
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Mozilla Firefox
  # @see http://doxygen.db48x.net/mozilla/html/interfacensIDownloadManager.html
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_firefox (self):
    model = pymobius.app.firefox.model (self.__item)
    
    for profile in model.get_profiles ():
      for entry in profile.get_cookies ():
        cookie = Cookie ()
        cookie.name = entry.name
        cookie.value = entry.value
        cookie.domain = entry.domain
        cookie.creation_time = entry.creation_time
        cookie.last_access_time = entry.last_access_time
        cookie.evidence_path = entry.evidence_path
        cookie.is_deleted = entry.is_deleted
        cookie.profile = profile
        self.__entries.append (cookie)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from GeckoFX
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_geckofx (self):
    model = pymobius.app.geckofx.model (self.__item)
    
    for profile in model.get_profiles ():
      for entry in profile.get_cookies ():
        cookie = Cookie ()
        cookie.name = entry.name
        cookie.value = entry.value
        cookie.domain = entry.domain
        cookie.creation_time = entry.creation_time
        cookie.last_access_time = entry.last_access_time
        cookie.evidence_path = entry.evidence_path
        cookie.is_deleted = entry.is_deleted
        cookie.profile = profile
        self.__entries.append (cookie)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Internet Explorer
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_internet_explorer (self):
    model = pymobius.app.internet_explorer.model (self.__item)
    
    for profile in model.get_profiles ():
      for entry in profile.get_cookies ():
        cookie = Cookie ()
        cookie.name = entry.name
        cookie.value = entry.value
        cookie.domain = entry.domain
        cookie.creation_time = entry.creation_time
        cookie.expiration_time = entry.expiration_time
        cookie.evidence_path = entry.evidence_path
        cookie.is_deleted = entry.is_deleted
        cookie.profile = profile
        self.__entries.append (cookie)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Opera
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_opera (self):
    model = pymobius.app.opera.model (self.__item)
    
    for profile in model.get_profiles ():
      for entry in profile.get_cookies ():
        cookie = Cookie ()
        cookie.name = entry.name
        cookie.value = entry.value
        cookie.domain = entry.domain
        cookie.creation_time = entry.creation_time
        cookie.last_access_time = entry.last_access_time
        cookie.evidence_path = entry.evidence_path
        cookie.is_deleted = entry.is_deleted
        cookie.is_encrypted = entry.is_encrypted
        cookie.profile = profile
        self.__entries.append (cookie)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from 'data.cookies' attribute
  # @deprecated since: 1.14
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_data_cookies (self):
    def iso_to_datetime (s):
      return datetime.datetime.strptime (s, '%Y-%m-%d %H:%M:%S')

    class profile (object): pass
    profiles = {}

    for c in self.__item.get_attribute ('data.cookies'):
      metadata = dict (c.metadata)

      cookie = Cookie ()
      cookie.name = c.name.encode ('utf8')
      cookie.value = c.value.encode ('utf8')
      cookie.is_encrypted = cookie.value == '<ENCRYPTED>'
      cookie.domain = c.domain.encode ('utf8')
      cookie.creation_time = c.creation_time

      # last access time
      last_access_time = metadata.get ('Last access date/time')
      if last_access_time:
        cookie.last_access_time = iso_to_datetime (last_access_time)

      # expiration time
      expiration_time = metadata.get ('Expiration date/time')
      if expiration_time:
        cookie.expiration_time = iso_to_datetime (expiration_time)

      # evidence_path
      if c.app_id in ('chrome', 'opera'):
        cookie.evidence_path = c.profile_path.encode ('utf8') + '\\Cookies'

      elif c.app_id in ('firefox', 'geckofx'):
        cookie.evidence_path = c.profile_path.encode ('utf8') + '\\cookies.sqlite'

      else:
        cookie.evidence_path = c.profile_path.encode ('utf8')
      
      # profile
      p = profiles.get ((c.app_id, c.profile_path))
      if not p:
        p = profile ()
        p.name = c.profile_id.encode ('utf8')
        p.path = c.profile_path.encode ('utf8')
        p.creation_time = None
        p.app_id = c.app_id.encode ('utf8')
        p.app_name = c.app_name.encode ('utf8')
        p.username = c.username.encode ('utf8')
        p.creation_time = None
        profiles[(c.app_id, c.profile_path)] = p

      cookie.profile = p
      self.__entries.append (cookie)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Save data into model
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __save_data (self):
    case = self.__item.case
    transaction = case.new_transaction ()

    # remove old data
    self.__item.remove_cookies ()

    # @begin-deprecated since: 1.14    
    if self.__item.has_attribute ('data.cookies'):
      self.__item.remove_attribute ('data.cookies')
    # @end-deprecated

    # save cookies
    profiles = {}

    for c in self.__entries:
      cookie = self.__item.new_cookie (c.name, c.value, c.is_encrypted)
      cookie.domain = c.domain
      cookie.creation_time = c.creation_time
      cookie.last_access_time = c.last_access_time
      cookie.expiration_time = c.expiration_time
      cookie.evidence_path = c.evidence_path
      
      if c.is_deleted != None:
        cookie.is_deleted = c.is_deleted
      
      # create profile, if necessary
      p = profiles.get (id (cookie.profile))
      if not p:
        app = case.new_application (c.profile.app_id, c.profile.app_name)

        p = self.__item.new_profile (c.profile.app_id, c.profile.path)
        p.id = c.profile.name
        p.username = c.profile.username
        
        if c.profile.creation_time:
          p.creation_time = c.profile.creation_time
        profiles[id (c.profile)] = p

      cookie.profile = p

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