# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# 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 pymobius.app.chromium
import pymobius.app.gecko
import pymobius.app.internet_explorer
import pymobius
import mobius
import traceback

ANT_ID = 'visited-urls'
ANT_NAME = 'Visited URLs'
ANT_VERSION = '1.0'

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Ant: visited-urls
# @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_gecko ()
    self.__retrieve_internet_explorer ()

    self.__save_data ()

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

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

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Chromium based browsers
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_chromium_profile (self, profile):
    try:
      for entry in profile.get_history ():
        if not entry.url.startswith ('file://'):
           v = pymobius.Data ()
           v.timestamp = entry.timestamp
           v.url = entry.url
           v.title = entry.title
           v.profile = profile

           v.metadata = mobius.pod.map ()
           v.metadata.set ('id', entry.id)

           self.__entries.append (v)
    except Exception as e:
      mobius.core.logf ('WRN ' + str (e) + ' ' + traceback.format_exc ())

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Gecko based browsers
  # @see http://doxygen.db48x.net/mozilla/html/interfacensIDownloadManager.html
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_gecko (self):
    model = pymobius.app.gecko.model (self.__item)

    for profile in model.get_profiles ():
      for entry in profile.get_history ():
        if not entry.url.startswith ('file://'):
           v = pymobius.Data ()
           v.timestamp = entry.timestamp
           v.url = entry.url
           v.title = entry.title
           v.profile = profile

           v.metadata = mobius.pod.map ()
           v.metadata.set ('id', entry.id)
           v.metadata.set ('typed', entry.typed)
           v.metadata.set ('visit-type', entry.visit_type)

           self.__entries.append (v)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve data from Internet Explorer
  # @todo Move Unicode convertion to profile class
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_internet_explorer (self):
    model = pymobius.app.internet_explorer.model (self.__item)

    for profile in model.get_profiles ():
      for entry in profile.get_history ():
        if not entry.url.startswith ('file://') and entry.timestamp_utc:
           v = pymobius.Data ()
           v.timestamp = entry.timestamp
           v.url = entry.url
           v.title = entry.title or ''
           v.profile = profile

           v.metadata = mobius.pod.map ()
           v.metadata.set ('file-path', entry.index_dat_path)
           v.metadata.set ('file-type', entry.file_type)
           v.metadata.set ('file-creation-time', entry.index_dat_creation_time)
           v.metadata.set ('file-last-modification-time', entry.index_dat_last_modification_time)
           v.metadata.set ('record-offset', '0x%08x' % entry.offset)
           v.metadata.set ('record-type', entry.type)
           v.metadata.set ('record-size', entry.size)
           v.metadata.set ('record-primary-time', entry.primary_time)
           v.metadata.set ('record-secondary-time', entry.secondary_time)

           local_time = entry.tags.get (0x18)
           if local_time:
             v.metadata.set ('local-time-tag-0x18', local_time)

           if entry.type == 'URL':
             v.metadata.set ('expiration-time', entry.expiration_time)
             v.metadata.set ('last-sync-time', entry.last_sync_time)
             v.metadata.set ('hits', entry.hits)
             v.metadata.set ('cached-file-path', entry.filename)

           self.__entries.append (v)

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

    # remove old data
    self.__item.remove_visited_urls ()

    # save entries
    profiles = {}

    for e in self.__entries:
      url = self.__item.new_visited_url (e.timestamp, e.url)
      url.title = e.title or ''
      url.metadata = e.metadata

      # create profile, if necessary
      p = profiles.get (id (e.profile))
      if not p:
        app = case.new_application (e.profile.app_id, e.profile.app_name)

        p = self.__item.new_profile (e.profile.app_id, e.profile.path)
        p.id = e.profile.name
        p.username = e.profile.username

        if e.profile.creation_time:
          p.creation_time = e.profile.creation_time
        profiles[id (e.profile)] = p

      url.profile = p

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