# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Mobius Forensic Toolkit
# Copyright (C) 2008, 2009 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 mobius.model.extension
import mobius
import mobius.config
import glob
import imp
import os
import shutil

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Application class
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class Application (object):

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Initialize application
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __init__ (self):
    self.__resources = {}
    self.__config = {}
    self.ui_manager = None

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Add a resource (category, filesystem, etc) to application
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def add_resource (self, type, id, value):
    self.__resources.setdefault (type, {})[id] = value

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get a resource
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_resource (self, type, id):
    return self.__resources.setdefault (type, {}).get (id)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Pop resource from application
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def pop_resource (self, type, id):
    return self.__resources.setdefault (type, {}).pop (id, None)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get a list of resources
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_resource_list (self, type):
    return self.__resources.setdefault (type, {}).values ()

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Return path to application file/directory
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_path (self, sub_path=None):
    path = os.path.expandvars ("$HOME/.mobiusft")

    if sub_path:
      path = os.path.join (path, sub_path)

    return path
      
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Return application directory/subdirectory
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def get_directory (self, dirname=None):
    dir = os.path.expandvars ("$HOME/.mobiusft")

    if dirname:
      dir = os.path.join (dir, dirname)

    return dir

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Create application subdirectory
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def make_directory (self, dirname=None):
    dir = self.get_path (dirname)

    if not os.path.isdir (dir):
      os.makedirs (dir)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Import extension from path
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def import_extension (self, path):
    module = imp.new_module ('extension')
    pickle = mobius.model.extension.Pickle ()
    extension = pickle.load (path)
    code = compile (extension.code + '\n', 'extension: %s' % extension.id, 'exec')
    exec code in module.__dict__

    extension_class = module.Extension ()
    extension_class.id = extension.id
    extension_class.name = extension.name
    extension_class.version = extension.version
    extension_class.description = extension.description
    extension_class.icon_data = extension.icon_data
    extension_class.mediator = mobius.mediator.copy ()
    extension_class.path = path
    extension_class.module = module
    return extension_class

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Start extension
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def start_extension (self, extension):
    extension.start ()
    self.add_resource ('extension', extension.id, extension)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Install an extension
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def install_extension (self, extension):

    # run extension install method, if it exists
    install_method = getattr (extension, 'install', None)
    if install_method:
      install_method ()

    # copy file to extensions directory
    extension_dir = self.get_path ('extensions')
    shutil.copy (extension.path, extension_dir)
    extension.path = os.path.join (extension_dir, os.path.basename (extension.path))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Uninstall an extension
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def uninstall_extension (self, id):

    # stop extension
    extension = self.pop_resource ('extension', id)
    extension.stop ()

    # run extension uninstall method, if it exists
    uninstall_method = getattr (extension, 'uninstall', None)
    if uninstall_method:
      uninstall_method ()

    # remove file from extensions directory
    os.remove (extension.path)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Run application
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def run (self):

    # advertise services
    mobius.mediator.advertise ('app.get-path', self.svc_get_path)
    mobius.mediator.advertise ('app.get-data-path', self.svc_get_data_path)
    mobius.mediator.advertise ('app.get-name', self.svc_get_name)
    mobius.mediator.advertise ('app.get-version', self.svc_get_version)
    mobius.mediator.advertise ('app.get-config', self.svc_get_config)
    mobius.mediator.advertise ('app.set-config', self.svc_set_config)

    mobius.mediator.advertise ('extension.new', self.svc_extension_new)
    mobius.mediator.advertise ('extension.open', self.svc_extension_open)
    mobius.mediator.advertise ('extension.save', self.svc_extension_save)

    # configure user account
    self.__configure_user ()

    # start extensions API
    extension_dir = self.get_directory ('extensions')

    for path in glob.iglob ('%s/*.xml' % extension_dir):
      try:
        extension = self.import_extension (path)
        start_api = getattr (extension, 'start_api', None)

        if start_api:
          start_api ()

        self.add_resource ('extension', extension.id, extension)

      except Exception, e:
        print 'Extension error (%s): %s' % (os.path.basename (path), e)

    # start extensions
    for extension in self.get_resource_list ('extension'):
      start = getattr (extension, 'start', None)
      if start:
        start ()

    # load configuration
    config_path = os.path.join (self.get_directory (), 'mobius.xml')

    if os.path.exists (config_path):
      self.__config = mobius.mediator.call ('xml.unpickle', config_path) or {}

    # start application
    mobius.mediator.call ('app.start')

    # save configuration
    mobius.mediator.call ('xml.pickle', config_path, self.__config)

    # stop extensions
    for extension in self.get_resource_list ('extension'):
      stop = getattr (extension, 'stop', None)
      if stop:
        extension.stop ()

    # stop extensions API
    for extension in self.get_resource_list ('extension'):
      extension.mediator.clear ()

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Configure application for current user
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __configure_user (self):
    self.make_directory ()

    # install extensions, if necessary
    if not os.path.isdir (self.get_path ('extensions')):
      self.make_directory ('extensions')
      extension_dir = mobius.config.get_data_path ('extensions')

      for path in glob.iglob ('%s/*.xml' % extension_dir):
        extension = self.import_extension (path)
        self.install_extension (extension)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Return path to application file/directory
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def svc_get_path (self, *args):
    path = os.path.expandvars ("$HOME/.mobiusft")

    if args:
      path = os.path.join (path, *args)

    return path

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Return path to data file/directory
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def svc_get_data_path (self, *args):
    path = mobius.config.get_data_path (*args)
    return path

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Return application name
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def svc_get_name (self):
    return mobius.config.APP_NAME

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Return application version
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def svc_get_version (self):
    return mobius.config.APP_VERSION

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get configuration data
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def svc_get_config (self, key):
    return self.__config.get (key)

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Set configuration data
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def svc_set_config (self, key, value):
    self.__config[key] = value

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Create extension
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def svc_extension_new (self):
    extension = mobius.model.extension.Extension ()
    return extension

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Open extension from file
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def svc_extension_open (self, path):
    pickle = mobius.model.extension.Pickle ()
    extension = pickle.load (path)
    return extension

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Save extension to file
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def svc_extension_save (self, extension, path):
    pickle = mobius.model.extension.Pickle ()
    pickle.save (extension, path)
