# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Mobius Forensic Toolkit
# Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 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 tempfile
import os
import os.path
import decoder_cookies
import decoder_downloads
import decoder_formhistory
import decoder_places
#import beta.util

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Known files list
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
KFF_FILES = [
   'cookies.sqlite',
   'cookies.sqlite-shm',
   'cookies.sqlite-wal',
   'downloads.sqlite',
   'favicons.sqlite',
   'favicons.sqlite-wal',
   'formhistory.sqlite',
   'places.sqlite',
   'places.sqlite-shm',
   'places.sqlite-wal',
   'signons.sqlite'
   ]

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Firefox Profile class
# @author Eduardo Aguiar
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class Profile (object):

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Initialize object
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __init__ (self, folder):
    self.__cookies_loaded = False
    self.__downloads_loaded = False
    self.__form_history_loaded = False
    self.__history_loaded = False

    self.__cookies = []
    self.__downloads = []
    self.__form_history = []
    self.__history = []

    # beta.util.dirtree (folder)	# development only

    self.__roaming_folder = folder
    self.path = folder.path.replace ('/', '\\')
    
    if folder.name.lower ().endswith ('.default'):
      self.name = folder.name[:-8]

    else:
      self.name = folder.name

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get cookies from cookies.sqlite
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_cookies (self):

    if not self.__cookies_loaded:
      self.__cookies_loaded = True

      path = self.__retrieve_roaming_file ('cookies.sqlite')
      if path:
        data = decoder_cookies.decode (path)
        self.__cookies = data.cookies
        os.remove (path)

    return self.__cookies

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get download history from downloads.sqlite
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_downloads (self):

    if not self.__downloads_loaded:
      self.__downloads_loaded = True

      path = self.__retrieve_roaming_file ('downloads.sqlite')
      if path:
        data = decoder_downloads.decode (path)
        self.__downloads = data.downloads
        os.remove (path)

    return self.__downloads

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get form history from formhistory.sqlite
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_form_history (self):

    if not self.__form_history_loaded:
      self.__form_history_loaded = True

      path = self.__retrieve_roaming_file ('formhistory.sqlite')
      if path:
        data = decoder_formhistory.decode (path)
        self.__form_history = data.form_history
        os.remove (path)

    return self.__form_history

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get history entries from places.sqlite
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_history (self):

    if not self.__history_loaded:
      self.__history_loaded = True

      path = self.__retrieve_roaming_file ('places.sqlite')
      if path:
        data = decoder_places.decode (path)
        self.__history = data.history
        os.remove (path)

    return self.__history

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get known files list
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_known_files (self):

    for kff_file in KFF_FILES:
      f = self.__roaming_folder.get_child_by_name (kff_file, False)

      if f and not f.is_reallocated:
        yield f

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Retrieve file into temp directory
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __retrieve_roaming_file (self, rpath):

    # retrieve file
    f = self.__roaming_folder.get_child_by_path (rpath, False)
    if not f:
      return

    reader = f.new_reader ()
    if not reader:
      return

    # create temporary .sqlite local file
    ext = os.path.splitext (f.name)[1]
    path = tempfile.mktemp (suffix=ext)

    fp = open (path, 'w')
    fp.write (reader.read ())
    fp.close ()
    
    return path

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get any file (development only)
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_file (self, rpath, name=None):

    # try to get file
    f = self.__roaming_folder.get_child_by_path (rpath, False)
    if not f:
      return

    reader = f.new_reader ()
    if not reader:
      return

    # create temporary .sqlite local file
    name = name or os.path.basename (rpath)
    fp = open (name, 'w')
    fp.write (reader.read ())
    fp.close ()
