# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# Mobius Forensic Toolkit
# Copyright (C) 2008,2009,2010,2011 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 libxml2
from mobius.extension.model import *
XML_ENCODING='utf-8'

# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Persistence layer for extension
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class Pickle (object):

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Get node property with correct encoding
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __get_prop (self, node, name):
    value = node.prop (name)
    if value:
      value = value.decode (XML_ENCODING)
    return value

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Set node property with correct encoding
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def __set_prop (self, node, name, value):
    if value != None:
      node.setProp (name, value.encode (XML_ENCODING))

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Load from string
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def load_from_string (self, data):
    doc = libxml2.parseDoc (data)
    node = doc.getRootElement ()
    extension = self.load_extension (node)
    doc.freeDoc ()

    return extension

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Load extension
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def load (self, path):
    doc = libxml2.parseFile (path)
    node = doc.getRootElement ()
    extension = self.load_extension (node)
    doc.freeDoc ()

    return extension

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Load <extension>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def load_extension (self, node):
    extension = Extension ()
    extension.id = self.__get_prop (node, 'id')
    extension.name = self.__get_prop (node, 'name')
    extension.author = self.__get_prop (node, 'author')
    extension.version = self.__get_prop (node, 'version')
    extension.description = self.__get_prop (node, 'description')

    # load children
    node = node.children

    while node:
      if node.type == 'element' and node.name == 'service':
        extension.services.append (self.load_service (node))

      elif node.type == 'element' and node.name == 'callback':
        extension.callbacks.append (self.load_callback (node))

      elif node.type == 'element' and node.name == 'icon':
        extension.icon_data = self.load_icon (node)

      elif node.type == 'element' and node.name == 'code':
        extension.code = self.load_code (node)

      node = node.next

    return extension

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Load <service>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def load_service (self, node):
    service = Service ()
    service.id = self.__get_prop (node, 'id')
    service.description = self.__get_prop (node, 'description')
    service.is_deprecated = self.__get_prop (node, 'is_deprecated') == 'true'
    service.is_autostarted = self.__get_prop (node, 'is_autostarted') == 'true'
    service.use_varargs = self.__get_prop (node, 'use_varargs') == 'true'
    service.use_kwds = self.__get_prop (node, 'use_kwds') == 'true'

    # load children
    node = node.children

    while node:
      if node.type == 'element' and node.name == 'arg':
        service.args.append (self.load_arg (node))

      elif node.type == 'element' and node.name == 'code':
        service.code = self.load_code (node)

      node = node.next

    return service

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Load <callback>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def load_callback (self, node):
    callback = Callback ()
    callback.id = self.__get_prop (node, 'id')
    callback.is_autostarted = self.__get_prop (node, 'is_autostarted') == 'true'
    callback.is_chain_end = self.__get_prop (node, 'is_chain_end') == 'true'
    callback.use_varargs = self.__get_prop (node, 'use_varargs') == 'true'
    callback.use_kwds = self.__get_prop (node, 'use_kwds') == 'true'

    # load children
    node = node.children

    while node:
      if node.type == 'element' and node.name == 'arg':
        callback.args.append (self.load_arg (node))

      elif node.type == 'element' and node.name == 'code':
        callback.code = self.load_code (node)

      node = node.next

    return callback

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Load <arg>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def load_arg (self, node):
    arg = Arg ()
    arg.id = self.__get_prop (node, 'id')
    arg.defaultvalue = self.__get_prop (node, 'defaultvalue')
    arg.description = self.__get_prop (node, 'description')

    return arg

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Load <icon>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def load_icon (self, node):
    icon_data = node.getContent ()
    return icon_data

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Load <code>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def load_code (self, node):
    code = node.getContent ().strip ()
    return code

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Save extension
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def save (self, extension, path):
    doc = libxml2.newDoc ('1.0')
    node = self.save_extension (extension)
    doc.addChild (node)
    doc.saveFormatFileEnc (path, XML_ENCODING, 1)
    doc.freeDoc ()

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Save <extension>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def save_extension (self, extension):
    node = libxml2.newNode ('extension')
    self.__set_prop (node, 'id', extension.id)
    self.__set_prop (node, 'name', extension.name)
    self.__set_prop (node, 'author', extension.author)
    self.__set_prop (node, 'version', extension.version)
    self.__set_prop (node, 'description', extension.description)

    # services
    for service in extension.services:
      child = self.save_service (service)
      node.addChild (child)

    # callbacks
    for callback in extension.callbacks:
      child = self.save_callback (callback)
      node.addChild (child)

    # icon
    if extension.icon_data:
      child = self.save_icon (extension.icon_data)
      node.addChild (child)

    # code
    if extension.code:
      child = self.save_code (extension.code)
      node.addChild (child)

    return node

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Save <service>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def save_service (self, service):
    node = libxml2.newNode ('service')
    self.__set_prop (node, 'id', service.id)
    self.__set_prop (node, 'description', service.description)
    self.__set_prop (node, 'is_deprecated', 'true' if service.is_deprecated else 'false')
    self.__set_prop (node, 'is_autostarted', 'true' if service.is_autostarted else 'false')

    # args
    for arg in service.args:
      child = self.save_arg (arg)
      node.addChild (child)

    self.__set_prop (node, 'use_varargs', 'true' if service.use_varargs else 'false')
    self.__set_prop (node, 'use_kwds', 'true' if service.use_kwds else 'false')

    # code
    if service.code:
      child = self.save_code (service.code)
      node.addChild (child)

    return node

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Save <callback>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def save_callback (self, callback):
    node = libxml2.newNode ('callback')
    self.__set_prop (node, 'id', callback.id)
    self.__set_prop (node, 'is_autostarted', 'true' if callback.is_autostarted else 'false')
    self.__set_prop (node, 'is_chain_end', 'true' if callback.is_chain_end else 'false')

    # args
    for arg in callback.args:
      child = self.save_arg (arg)
      node.addChild (child)

    self.__set_prop (node, 'use_varargs', 'true' if callback.use_varargs else 'false')
    self.__set_prop (node, 'use_kwds', 'true' if callback.use_kwds else 'false')

    # code
    if callback.code:
      child = self.save_code (callback.code)
      node.addChild (child)

    return node

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Save <arg>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def save_arg (self, arg):
    node = libxml2.newNode ('arg')
    self.__set_prop (node, 'id', arg.id)
    self.__set_prop (node, 'description', arg.description)
    self.__set_prop (node, 'defaultvalue', arg.defaultvalue)
    return node

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Save <icon>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def save_icon (self, icon_data):
    node = libxml2.newNode ('icon')
    node.addContent (icon_data)

    return node

  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  # @brief Save <code>
  # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  def save_code (self, code):
    node = libxml2.newNode ('code')
    node.addContent (code.strip ())

    return node
