# Copyright 1999-2015. Parallels IP Holdings GmbH. All Rights Reserved.
import os
import time
import random
import shutil
import logging
import cStringIO
import sys
from xml.dom import minidom
from encoder import EncoderFile

import subproc
import dirutil

import pmm_migration_handler
import pmm_config
import plesk_config
import pmmcli_config
import pmm_dump_overview
import pmm_dump_formatter
import pmm_api_xml_protocols
import pmm_conflict_detector
import pmm_dump_access_service
import pmm_repository_access_service
from pmmcli_exceptions import PMMUtilityException

mswindows = (sys.platform == "win32")

_logger = logging.getLogger("pmmcli.session")

def to_unicode(str):
    return str if isinstance(str, unicode) else str.decode('utf-8')

def to_str(uni):
    return uni if isinstance(uni, str) else uni.encode('utf-8')

def fspath(path):
    assert(isinstance(path, basestring))
    return to_unicode(path) if os.path.supports_unicode_filenames else to_str(path)

def get_dir_separator():
    if mswindows:
        return "\\"
    else:
        return "/"

def _put_session_element_by_full_path(element_path, value):
    with open(element_path, 'wt') as f:
        f.write(to_str(value))

def _put_session_element(session_path, element, value):
    _put_session_element_by_full_path(os.path.join(fspath(session_path), fspath(element)), value)

def _update_dump_index_file(old_path, new_path):
    dump_index_file = os.path.join(old_path, "dump_index.xml")
    dump_index_xml = minidom.parse(dump_index_file)
    dump_index_xml_child = dump_index_xml.childNodes[0]
    for dump in dump_index_xml_child.getElementsByTagName('dump'):
        for path in dump.getElementsByTagName('path'):
           path.firstChild.nodeValue = os.path.join(new_path, path.firstChild.nodeValue[path.firstChild.nodeValue.rfind(get_dir_separator())+1 : len(path.firstChild.nodeValue)])
    with open(os.path.join(new_path, "dump_index.xml"), 'wt') as content:
        content.write(dump_index_xml.toxml("utf-8"))

def _create_dump_index_element(writer, type, name, path, content_base, is_root=False):
    attribute={}
    if is_root:
        attribute={"root":"true"}
    writer.startElement("dump", attribute)
    if type != '':
        writer.startElement("object-type", {})
        writer.addContent(type)
        writer.endElement("object-type", '')
    if name != '':
        writer.startElement("object-name", {})
        writer.addContent(name)
        writer.endElement("object-name", '')
    writer.startElement("path", {})
    writer.addContent(path)
    writer.endElement("path", '')
    writer.startElement("content-base", {})
    writer.addContent(content_base)
    writer.endElement("content-base", '')
    writer.endElement("dump", '')

def convertToXmlString(response, name):
    resp_str = cStringIO.StringIO()
    resp_encoded = EncoderFile(resp_str, "utf-8")
    resp_encoded.write('<?xml version="1.0" encoding="UTF-8"?>\n')
    response.export(resp_encoded, 0, name_ = name)
    packet = resp_str.getvalue()
    return packet


def prepareSessionId(session_id):
    if session_id == None:
        return session_id
    if isinstance(session_id, str) or isinstance(session_id, unicode):
        return session_id
    if isinstance(session_id, pmm_api_xml_protocols.SessionId):
        return session_id.getValueOf_()
    raise Exception("session_id has unexpected type: %s" % str(session_id))


class RSessionException(Exception):
    def __init__(self, session_id):
        Exception.__init__(self, str(session_id))
        self.__session_id = prepareSessionId(session_id)
        _logger.info("RSessionException generated: " + str(self.__session_id))

    def get_session_id(self):
        return self.__session_id


class RSessionParamException(Exception):
    def __init__(self, message):
        Exception.__init__(self, message)
        self.__message = message
        _logger.info("RSessionParamException generated: " + message)

    def get_message(self):
        return self.__message


class PmmcliSession:
    def __init__(self, session_id):
        self.__session_id = prepareSessionId(session_id)
        self.__session_path = os.path.join(pmm_config.restore_session_dir(), self.__session_id)
        # test if session directory exists and raise otherwise
        self.__info_xml_base = os.path.join(self.__session_path, 'base')
        self.__restore_specification = os.path.join(self.__session_path, 'restore.xml')
        self.__owner_guid = os.path.join(self.__session_path, 'owner_guid')
        self.__owner_type = os.path.join(self.__session_path, 'owner_type')
        self.__restore_specification_xml_transform = os.path.join(self.__session_path, 'xml_transform.xml')
        self.__conflicted_restore_specification = os.path.join(self.__session_path, 'restore.xml.conflicted')
        self.__dump_specification = os.path.join(self.__session_path, 'dump_specification')
        self.__dump_overview = os.path.join(self.__session_path, 'dump_overview')
        self.__task_id = os.path.join(self.__session_path,'task_id')
        self.__conflict_description = os.path.join(self.__session_path, 'conflict_description')
        self.__conflict_resolution_rules = os.path.join(self.__session_path, 'conflict_resolution_rules')
        self.__content_filter_type = os.path.join(self.__session_path, 'content_filter_type')
        self.__content_filter_data = os.path.join(self.__session_path, 'content_filter_data')
        self.__content_restore_type = os.path.join(self.__session_path, 'content_restore_type')
        self.__session_dump_path = os.path.join(self.__session_path, "dump")
        self.__dump_index = os.path.join(self.__session_dump_path, "dump_index.xml")
        self.__info_xml = os.path.join(self.__session_dump_path, 'info.xml')
        self.__repository_url = os.path.join(self.__session_path, 'repository_url')
        self.__repository_login = os.path.join(self.__session_path, 'repository_login')
        self.__repository_pwd = os.path.join(self.__session_path, 'repository_pwd')
        self.__repository_ftp_passive_mode = os.path.join(self.__session_path, 'repository_ftp_passive_mode')
        self.__repository_use_ftps = os.path.join(self.__session_path, 'repository_use_ftps')
        self.__storage_type = os.path.join(self.__session_path, 'storage_type')
        self.__extension_filter_file = os.path.join(self.__session_path, 'extension_filter')
        self.__capability_info = os.path.join(self.__session_path, 'capability_info')
        self.__logs_dir = os.path.join(self.__session_path, 'logs_dir')
        self.__restore_mode = os.path.join(self.__session_path, 'restore_mode')
        self.__delete_dump = os.path.join(self.__session_path, 'delete_dump')
        self.__ignore_sign = os.path.join(self.__session_path, 'ignore_sign')
        _logger.info("Initialized restore session " + str(self.__session_id))

    def get_restore_mode(self):
        return self._safe_get_session_parameter(self.__restore_mode)

    def get_dump_index_file(self):
        return self.__dump_index
        
    def get_session_id(self):
        return self.__session_id

    def get_task_id(self):
        try:
            with open(self.__task_id, 'rt') as f:
                task_id = f.read()
            return int(task_id)
        except:
            return None

    def set_task_id(self, task_id):
        _put_session_element_by_full_path(self.__task_id, str(task_id))

    def get_ignore_sign(self):
        try:
            with open(self.__ignore_sign, 'rt') as f:
                value = f.read()
            return int(value)
        except:
            return None

    def set_ignore_sign(self, value):
        _put_session_element_by_full_path(self.__ignore_sign, str(value))

    def get_owner_guid(self):
        try:
            with open(self.__owner_guid, 'rt') as f:
                return f.read()
        except:
            _logger.error("Could not get owner guid from '" + self.__owner_guid + "'.")
            raise RSessionException(self.__session_id)

    def get_owner_type(self):
        try:
            with open(self.__owner_type, 'rt') as f:
                return f.read()
        except:
            _logger.error("Could not get owner type from '" + self.__owner_type + "'.")
            raise RSessionException(self.__session_id)

    def get_repository_url(self):
        return self._safe_get_session_parameter(self.__repository_url)

    def get_repository_login(self):
        return self._safe_get_session_parameter(self.__repository_login)

    def get_repository_pwd(self):
        return self._safe_get_session_parameter(self.__repository_pwd)

    def get_repository_ftp_passive_mode(self):
        return self._safe_get_session_parameter(self.__repository_ftp_passive_mode)

    def get_repository_use_ftps(self):
        return self._safe_get_session_parameter(self.__repository_use_ftps)

    def get_storage_type(self):
        return self._safe_get_session_parameter(self.__storage_type)

    def _safe_get_session_parameter(self, parameter):
        parameter_value = None
        try:
            with open(parameter, 'rt') as f:
                parameter_value = f.read()
        except IOError as e:
            _logger.debug("Could not get value from '" + parameter + "'. Details: " + e.message)
            pass
        except:
            _logger.debug("Could not get value from '" + parameter + "'.")
            pass
        return parameter_value

    def get_restore_specification_xml_transform(self):
        return self.__restore_specification_xml_transform

    def get_restore_specification(self):
        return self.__restore_specification

    def get_conflicted_restore_specification(self):
        return self.__conflicted_restore_specification

    def get_dump_specification(self):
        # return file name
        return self.__dump_specification

    def get_dump_specification_value(self):
        try:
            with open(self.__dump_specification, 'rt') as f:
                return f.read()
        except:
            _logger.error("Could not get dump specification from '" + self.__dump_specification + "'.")
            raise RSessionException(self.__session_id)

    def get_dump_overview(self):
        return self.__dump_overview

    def get_session_path(self):
        return self.__session_path

    def get_session_dump_path(self):
        return self.__session_dump_path

    def get_info_xml(self):
        return self.__info_xml

    def get_info_xml_base(self):
        base = self.__session_path
        try:
            with open(self.__info_xml_base, 'rt') as f:
                base = f.read().decode('utf-8')
        except:
            pass
        return base

    def conflicts_resolved(self):
        return os.path.isfile(self.__conflict_resolution_rules)

    def apply_base(self):
        writer = pmm_dump_access_service.XMLSimpleWriter()
        setter = pmm_dump_access_service.XmlElementAttrSetter('migration-dump','base',to_unicode(self.get_info_xml_base()))
        xml_processor = pmm_dump_access_service.InfoXMLProcessor( self.get_info_xml(), pmm_dump_access_service.XmlHandler([setter,writer]) )
        xml_processor.process()
        with open(self.get_info_xml(), 'wt') as r_s:
            r_s.write(writer.get_content())

    def set_restore_mode(self, mode):
        _put_session_element_by_full_path(self.__restore_mode, mode)

    def set_content_filter_type(self, type):
        _put_session_element_by_full_path(self.__content_filter_type, type)

    def set_content_filter_data(self, data):
        _put_session_element_by_full_path(self.__content_filter_data, data)

    def set_content_restore_type(self, type):
        _put_session_element_by_full_path(self.__content_restore_type, type)

    def create_log(self, logname):
        filename = os.path.join( self.__session_path, logname)
        log = open(filename,'wt')
        return log

    @staticmethod
    def __createEmptySession():
        # create new unique session_id
        random.seed()
        session_id = time.strftime('%Y%m%d%H%M%S',time.localtime()) + str(random.randint(0,1000))
        if not os.path.isdir(pmm_config.restore_session_dir()):
            dirutil.mkdirs(pmm_config.restore_session_dir(), 0755 )

        session_path = os.path.join( pmm_config.restore_session_dir(), session_id)
        while os.path.isdir(session_path):
            session_id = time.strftime('%Y%m%d%H%M%S',time.localtime()) + str(random.randint(0,1000))
            session_path = os.path.join( pmm_config.restore_session_dir(), session_id)

        _logger.info("Create restore session " + session_id + " started")
        session_path = os.path.join( pmm_config.restore_session_dir(), session_id)

        session_dump_path = os.path.join(session_path, "dump")
        dirutil.mkdirs(session_dump_path, 0750, -1, plesk_config.psaadm_gid() )
        return session_id;

    @staticmethod
    def createEmptySession(owner_type = '', owner_guid = ''):
        session_id = PmmcliSession.__createEmptySession()
        session_path = os.path.join( pmm_config.restore_session_dir(), session_id)
        _put_session_element(session_path, 'owner_type', owner_type)
        _put_session_element(session_path, 'owner_guid', owner_guid)
        session = PmmcliSession(session_id)
        _logger.info("Create empty session " + str(session.get_session_id()) + " done")
        return session

    # create new dir and copy info_xml into it
    def createSession(owner_type, owner_guid, info_xml_base, dump_specification, copy_child_dumps_from_repo = False, initial_dump_specification = False):
        # Do not check owner_type and owner_guid. They are allowed to be specified with 'restore' command.
        if not os.path.isfile(fspath(info_xml_base)):
            raise RSessionParamException("Info xml file is not found: '%s'" % info_xml_base)
        if dump_specification is None:
            raise RSessionParamException("Dump specification is not specified")

        session_id = PmmcliSession.__createEmptySession()
        session_path = os.path.join( pmm_config.restore_session_dir(), session_id)

        session_dump_path = os.path.join(session_path, "dump")

        shutil.copy2(fspath(info_xml_base),os.path.join(session_dump_path,u'info.xml'))

        if copy_child_dumps_from_repo == True:
            dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(dump_specification)
            dump_file = dump_specification_formatter.get_name_of_xml_file() #todo change to info_xml_base?
            dump_storage = dump_specification_formatter.get_dumps_storage_credentials_formatter().getDumpStorage()

            child_dumps_list, errcode, errmessage = pmm_repository_access_service.LocalRepositoryAccessService(dump_specification_formatter.get_dumps_storage_credentials_formatter())._pmmras_get_child_dumps(dump_storage, dump_file)

            #Copy and rename all children dumps
            dump_index_number = 1
            writer = pmm_dump_access_service.XMLSimpleWriter()
            writer.startElement("dump-list", {})

            info_xml_base_path, info_xml_base_name = os.path.split(info_xml_base)
            _create_dump_index_element(writer, '', '', to_unicode(os.path.join(session_dump_path,'info.xml')), to_unicode(info_xml_base_path), True)

            for child_dump in child_dumps_list:
                renamed_dump_file = "dump_object" + str(dump_index_number) + ".xml"

                child_dump_path, child_dump_file = os.path.split(child_dump)

                type_candidate = child_dump_path[0: child_dump_path.rfind(get_dir_separator())]
                if type_candidate.rfind(get_dir_separator()):
                    type_candidate = type_candidate[type_candidate.rfind(get_dir_separator()) + 1 : len(type_candidate)]

                type = None
                if type_candidate == 'domains':
                    type = 'domain'
                if type_candidate == 'clients':
                    type = 'client'
                if type_candidate == 'resellers':
                    type = 'reseller'

                if type in ['reseller', 'client', 'domain']:
                    path = os.path.join(fspath(dump_storage), fspath(child_dump))
                    realname_getter = pmm_dump_access_service.XmlElementAttrGetter(type,'name')
                    xml_processor = pmm_dump_access_service.InfoXMLProcessor( path, pmm_dump_access_service.XmlHandler([realname_getter]))
                    xml_processor.process()
                    real_name = realname_getter.get_attribute_value()

                    if os.path.isdir(path) == False:
                        shutil.copy2(path, os.path.join(session_dump_path, renamed_dump_file))

                    _create_dump_index_element(
                        writer, type, real_name,
                        os.path.join(to_unicode(session_dump_path), to_unicode(renamed_dump_file)),
                        os.path.join(to_unicode(dump_storage), to_unicode(child_dump_path))
                    )
                    dump_index_number = dump_index_number + 1
            writer.endElement("dump-list", '')

            dump_index_file_name = os.path.join(fspath(session_dump_path), 'dump_index.xml')
            with open(dump_index_file_name, 'wt') as content:
                content.write(writer.get_content())

        _put_session_element(session_path, 'base', info_xml_base)
        _put_session_element(session_path, 'dump_specification', dump_specification)
        _put_session_element(session_path, 'owner_type', owner_type)
        _put_session_element(session_path, 'owner_guid', owner_guid)

        if initial_dump_specification:
            initial_dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(initial_dump_specification)
            _put_session_element(session_path, 'repository_url', initial_dump_specification_formatter.get_dumps_storage_credentials_formatter().getDumpStorage())
            _put_session_element(session_path, 'repository_login', initial_dump_specification_formatter.get_dumps_storage_credentials_formatter().get_login())
            _put_session_element(session_path, 'repository_pwd', initial_dump_specification_formatter.get_dumps_storage_credentials_formatter().get_password())
            _put_session_element(session_path, 'repository_ftp_passive_mode', initial_dump_specification_formatter.get_dumps_storage_credentials_formatter().get_use_passive_ftp_mode())
            _put_session_element(session_path, 'repository_use_ftps', initial_dump_specification_formatter.get_dumps_storage_credentials_formatter().get_use_ftps())
            _put_session_element(session_path, 'storage_type', initial_dump_specification_formatter.get_dumps_storage_credentials_formatter().get_storage_type())

        # copy info_xml into session_path/info.xml
        session = PmmcliSession(session_id)
        # Next will create well-known restore_specification and dump_overview for session
        pmm_dump_overview.DumpOverviewCreator(session.get_session_id()).create(session.get_info_xml(),session.get_dump_overview())
        # now session has info.xml, R.S and D.O.
        _logger.info("Create restore session " + str(session.get_session_id()) + " done")
        return session

    createSession = staticmethod(createSession)

    # get session directory and initialize session files
    def openSession(session_id):
        return PmmcliSession(session_id)

    openSession = staticmethod(openSession)

    # get name for new session and create directory for it
    def deriveSession(session_id):
        # open old session to get info.xml
        session = PmmcliSession.openSession(session_id)
        _logger.info("Derive restore session from session " + str(session.get_session_id()) + " started")
        derived_session = PmmcliSession.createSession(session.get_owner_type(), session.get_owner_guid(), session.get_info_xml_base(), session.get_dump_specification_value())
        # now session has info.xml, R.S and D.O.
        shutil.copy2(os.path.join(session.get_session_path(),'owner_type'),os.path.join(derived_session.get_session_path(),'owner_type'))
        shutil.copy2(os.path.join(session.get_session_path(),'owner_guid'),os.path.join(derived_session.get_session_path(),'owner_guid'))
        derived_session.set_parent(session_id)
        _update_dump_index_file(session.get_session_dump_path(), derived_session.get_session_dump_path())
        _logger.info("Derive restore session " + str(derived_session.get_session_id()) + " from session " + str(session.get_session_id()) + " done")
        return derived_session

    deriveSession = staticmethod(deriveSession)

    # used for clone session - copying info.xml, R.S, C.D. to new directory
    # this is for '--resolve-conflicts' call, in this case ConflictResolver changes R.S. and initiate RestoreSpecificationCreator().update()
    def cloneSession(session_id):
        session = PmmcliSession.openSession(session_id)
        _logger.info("Clone restore session from session " + str(session.get_session_id()) + " started")
        cloned_session = PmmcliSession.createSession(session.get_owner_type(), session.get_owner_guid(), session.get_info_xml_base(),session.get_dump_specification_value())
        _logger.info("Clone restore session " + str(cloned_session.get_session_id()) + " from session " + str(session.get_session_id()) + " done")
        
        # copy sesion files (R.S., C.D. and other)
        for session_file in os.listdir(session.get_session_path()):
            if not os.path.isdir(os.path.join(session.get_session_path(), session_file)):
                shutil.copy2(os.path.join(session.get_session_path(),session_file),os.path.join(cloned_session.get_session_path(),session_file))

        if os.path.isdir(session.get_session_dump_path()):
            for session_dump_file in os.listdir(session.get_session_dump_path()):
                shutil.copy2(os.path.join(session.get_session_dump_path(),session_dump_file),os.path.join(cloned_session.get_session_dump_path(),session_dump_file))

        cloned_session.set_parent(session_id)
        _update_dump_index_file(session.get_session_dump_path(), cloned_session.get_session_dump_path())
        return cloned_session

    cloneSession = staticmethod(cloneSession)

    def getDumpOverview(self):
        # read dump_overview file and form class DumpOverview instance
        d_o = pmm_api_xml_protocols.DumpOverview.factory()
        d_o.build(minidom.parse(self.__dump_overview).childNodes[0] )
        return d_o

    def getDumpSpecification(self):
        # read dump_overview file and form class DumpSpecification instance
        dump_specification_formatter = pmm_dump_formatter.DumpSpecificationFormatter(self.get_dump_specification_value())
        return dump_specification_formatter.buildXml()
    
    def getConflictDescription(self):
        # read conflict_description file and form class ConflictDescription instance
        c_d = pmm_api_xml_protocols.ConflictsDescription.factory()
        c_d.build(minidom.parse(self.__conflict_description).childNodes[0] )
        return c_d

    def set_parent(self, parent_session_id):
        parent_session_id = prepareSessionId(parent_session_id)
        _put_session_element(self.get_session_path(), 'parent', parent_session_id)

    def get_logs_dir(self):
        return self._safe_get_session_parameter(self.__logs_dir)

    def set_logs_dir(self, value):
        _put_session_element_by_full_path(self.__logs_dir, value)

    def get_delete_dump(self):
        value = self._safe_get_session_parameter(self.__delete_dump)
        if value is None:
            return False
        else:
            return value == 'True'

    def set_delete_dump(self, value):
        _put_session_element_by_full_path(self.__delete_dump, str(value))

    def __store_conflict_resolution_rules(self, conflict_resolution_rules_object):
        if conflict_resolution_rules_object is None:
            return None
        conflict_resolution_rules_str = convertToXmlString(conflict_resolution_rules_object, 'conflict-resolution-rules')
        stored_conflict_resolution_rules = self.__conflict_resolution_rules
        conflict_iteration = 0
        while os.path.isfile(stored_conflict_resolution_rules):
            conflict_iteration += 1
            stored_conflict_resolution_rules = self.__conflict_resolution_rules + "." + str(conflict_iteration)
        with open(stored_conflict_resolution_rules, 'wt') as f:
            f.write(conflict_resolution_rules_str)
        return stored_conflict_resolution_rules

    def __doDetectConflicts(self):
        owner_guid = None
        if self.get_owner_guid() != '':
            owner_guid = self.get_owner_guid()
        pmm_conflict_detector.ConflictDetector.run(owner_guid,self.__conflict_description,self.__session_path, self.__dump_index)
        conflicts_description = self.getConflictDescription()
        return len(conflicts_description.get_conflict())

    def detectConflicts(self):
        return self.__doDetectConflicts()

    def getCapabilityInfo(self, source_capability_info, migration_session_id = None):
        owner_guid = None
        if self.get_owner_guid() != '':
            owner_guid = self.get_owner_guid()

        if migration_session_id is not None:
            download_speed, error_code, error_message = pmm_migration_handler.MigrationHandler.migrationGetDownloadSpeed(migration_session_id)
            if error_code:
                return None, error_code, error_message

            download_speed_value = download_speed.get_migration_download_speed().getValueOf_()

            server_info = source_capability_info.get_migration_selected_objects().get_server()
            if server_info is None:
                server_info = pmm_api_xml_protocols.MigrationObjectServer()
                source_capability_info.get_migration_selected_objects().set_server(server_info)

            if server_info.get_capability_info() is None:
                server_info.set_capability_info(pmm_api_xml_protocols.AgentCapabilityInfoType(resource_usage=pmm_api_xml_protocols.AgentCapabilityResourceUsageType()))

            server_info.get_capability_info().get_resource_usage().add_resource(
                pmm_api_xml_protocols.AgentCapabilityNameValueType('download.speed', download_speed_value)
            )

        tmp_file = self.__capability_info + '_in'
        try:
            with open(tmp_file, 'wt') as f:
                f.write(convertToXmlString(source_capability_info.get_migration_selected_objects(), 'migration-selected-objects'))
            error_code = pmm_conflict_detector.CapabilityInfo.runForTransfer(owner_guid, self.__session_path, tmp_file, self.__capability_info)
            #error_code != 254 means that there is NO problems with download speed and we save the value to not calculate speed next time
            if error_code != 254 and migration_session_id is not None:
                pmm_migration_handler.MigrationHandler.migrationSetDownloadSpeed(migration_session_id, download_speed_value)
        finally:
            os.unlink(tmp_file)

        capability_info_object = pmm_api_xml_protocols.MigrationCapabilityInfo.factory()
        capability_info_object.build(minidom.parse(self.__capability_info).childNodes[0] )
        return capability_info_object

    def fixGuids(self):
        restore_specification = self.get_dump_index_file()        
        pmm_conflict_detector.GuidsFixer.run(restore_specification, self.__session_path)

    def fixSettings(self, owner_guid):
        restore_specification = self.get_dump_index_file()
        pmm_conflict_detector.SettingsFixer.run(restore_specification, self.__session_path, owner_guid)

    def fixNames(self):
        restore_specification = self.get_dump_index_file()        
        pmm_conflict_detector.NamesFixer.run(restore_specification, self.__session_path)

    def __doResolveConflictsOnce(self, conflict_resolution_rules_object):
        stored_conflict_resolution_rules = self.__store_conflict_resolution_rules(conflict_resolution_rules_object)
        owner_guid = None
        if self.get_owner_guid() != '':
            owner_guid = self.get_owner_guid()
        pmm_conflict_detector.ConflictResolver.run(owner_guid, self.__restore_specification, self.__conflict_description, stored_conflict_resolution_rules, self.__session_path, self.__dump_index, self.__session_dump_path, self.__extension_filter_file, self.get_restore_mode())

    def resolveConflictsOnce(self, conflict_resolution_rules_object):
        self.__doResolveConflictsOnce(conflict_resolution_rules_object)
        pmm_dump_overview.RestoreSpecificationCreator(self.get_session_id()).update(self.get_info_xml(), self.__dump_overview)

    def resolveConflicts(self, conflict_resolution_rules_object):
        iteration = 0
        total_conflicts_count = 0
        max_conflict_resolves = pmmcli_config.get().get_max_conflict_resolves()
        while iteration < max_conflict_resolves:
            _logger.debug("doResolveConflictsOnce called " + str(iteration+1) + " time")
            self.__doResolveConflictsOnce(conflict_resolution_rules_object)
            total_conflicts_count = self.__doDetectConflicts()
            _logger.debug("doResolveConflictsOnce: " + str(total_conflicts_count) + " conflicts total found")
            if total_conflicts_count == 0:
                break
            iteration += 1
            # Only conflict resolution policy could be applied more then once
            # So we should remove rules for consequences operations
            if conflict_resolution_rules_object is not None:
                conflict_resolution_rules_object.set_rule([])
        pmm_dump_overview.RestoreSpecificationCreator(self.get_session_id()).update(self.get_info_xml(), self.__dump_overview)
        return total_conflicts_count

    def decryptBackup(self, resetPasswordsIfError):
        _logger.debug("decryptBackup")

        backup_files_map = self._transformDumpIndexFileToMapForBackupEncryptUtility()        

        cmd = subproc.CmdLine(pmm_config.backup_encrypt())
        cmd.arg('--encrypt-by-plesk')
        
        cmd.arg('-backup-files-map')        
        cmd.arg(backup_files_map)

        if resetPasswordsIfError:
            cmd.arg('-allow-reset-passwords')

        cmd.arg('-use-server-key')

        errcode = 0
        try:
            cmd.spawn()
        except subproc.NonzeroExitException, x:
            errcode = x.exitcode
            if errcode != 200:                
                raise PMMUtilityException('backup_encrypt', x)

        return errcode != 0

    def _transformDumpIndexFileToMapForBackupEncryptUtility(self):
        writer = pmm_dump_access_service.XMLSimpleWriter()

        dump_index_file = self.get_dump_index_file()
        dump_index_xml = minidom.parse(dump_index_file)
        dump_index_xml_child = dump_index_xml.childNodes[0]
        source_dir = None
        for dump in dump_index_xml_child.getElementsByTagName('dump'):
            for path in dump.getElementsByTagName('path'):
                if source_dir is None:
                    source_dir = path.firstChild.nodeValue[0:path.firstChild.nodeValue.rfind(get_dir_separator())+1]
                    writer.startElement("dump-files", {"source-directory":source_dir})
                file_name = path.firstChild.nodeValue[path.firstChild.nodeValue.rfind(get_dir_separator())+1 : len(path.firstChild.nodeValue)]
                writer.startElement("file", {})
                writer.addContent(file_name)
                writer.endElement("file", '')
        writer.endElement("dump-files",'')

        backup_files_map = os.path.join(self.get_session_dump_path(), u'backup_files_map.xml')
        with open(backup_files_map,'wt') as content:
            content.write(writer.get_content())
        return backup_files_map

    def create_restore_specification(self, selected_objects):
        selected_objects_file = None
        self.__apply_secure_transform()
        if selected_objects:
            with open(self.get_restore_specification_xml_transform(), 'wt') as f:
                resp_str = cStringIO.StringIO()
                resp_encoded = EncoderFile(resp_str, "utf-8")
                resp_encoded.write('<?xml version="1.0" encoding="UTF-8"?>\n')
                selected_objects.export(resp_encoded, 0, name_='transformation')
                f.write(resp_str.getvalue())
            selected_objects_file = self.get_restore_specification_xml_transform()
        pmm_dump_overview.RestoreSpecificationCreator(self.get_session_id()).create(self.get_owner_type(), self.get_owner_guid(), self.get_info_xml(),self.get_info_xml(),self.__dump_overview,selected_objects_file)

    # cut only owner hive from restore.xml for security purporses
    def __apply_secure_transform(self):
        owner_type = self.get_owner_type()
        owner_guid = self.get_owner_guid()
        if owner_type != '' and owner_type != 'server' and owner_guid != '':
            # apply secure transform only if restore dump contains owner itself
            # else suppose it is backup of one of owner's child
            
            element_finder = pmm_dump_access_service.XmlElementAttrValueGetter(owner_type,{"guid":owner_guid})
            xml_processor = pmm_dump_access_service.InfoXMLProcessor( self.get_info_xml(), pmm_dump_access_service.XmlHandler([element_finder]) )
            xml_processor.process()
            
            if element_finder.get_element():
                _logger.debug("Prepare secure transform: element found")
                secure_transform_file = os.path.join( self.__session_path, 'secure_transform')
                with open(secure_transform_file, 'wt') as secure_transform:
                    xml_transform = '<transformation><node children-processing-type="copy" name="%s"><attributes><attribute name="guid" value="%s"/></attributes></node></transformation>' % (self.get_owner_type(),self.get_owner_guid())
                    secure_transform.write('<?xml version="1.0" encoding="UTF-8"?>\n')
                    secure_transform.write(xml_transform)
                writer = pmm_dump_access_service.XMLSimpleWriter()
                xml_processor = pmm_dump_overview.InfoXMLProcessor( self.get_info_xml(),
                                                                    pmm_dump_overview.InfoXmlHandler( pmm_dump_overview.RestoreSpecificationXMLTransformWorker(pmm_dump_overview.RestoreSpecificationXMLTransformSpecification(secure_transform_file),
                                                                                                                                                               writer),
                                                                                                      pmm_dump_overview.XMLTransformPathProcessor()) )
                xml_processor.process()
                result_restore_specification_file = self.get_info_xml()
                with open(result_restore_specification_file,'wt') as result_restore_specification:
                    result_restore_specification.write(writer.get_content())

            else:
                _logger.debug("Owner element for secure transform does not found")
                #hierarchical_nodes = ['admin','reseller','client','domain']
                hierarchical_cut_nodes = {'admin':[],'reseller':['admin','resellers'],'client':['admin','resellers','clients'],'domain':['admin','resellers','clients','client','domains']}
                hierarchical_copy_nodes = {'admin':['admin'],'reseller':['reseller','reseller-info','clients','client','client-info','domains','domain','domain-info'],'client':['client','client-info','domains','domain','domain-info'],'domain':['domain','domain-info']}
                
                xml_transform = '<transformation>'
                xml_transform_cut_node = '<node children-processing-type="skip" name="%s"/>'
                xml_transform_copy_node = '<node children-processing-type="copy" name="%s"/>'
                
                index = hierarchical_cut_nodes.keys().index(owner_type)
                if index <= 0:
                    raise Exception("Unknown owner type '%s' for restore" % owner_type)
                
                cutting_nodes_array = hierarchical_cut_nodes[owner_type]
                for cutting_node in cutting_nodes_array:
                    cutting_node_transform = xml_transform_cut_node % cutting_node
                    xml_transform = xml_transform + cutting_node_transform
                
                copying_nodes_array = hierarchical_copy_nodes[owner_type]
                for copying_node in copying_nodes_array:
                    copy_node_transform = xml_transform_copy_node % copying_node
                    xml_transform = xml_transform + copy_node_transform
                
                xml_transform = xml_transform + '</transformation>'
                secure_transform_file = os.path.join( self.__session_path, 'secure_transform')
                with open(secure_transform_file, 'wt') as secure_transform:
                    secure_transform.write('<?xml version="1.0" encoding="UTF-8"?>\n')
                    secure_transform.write(xml_transform)
                writer = pmm_dump_access_service.XMLSimpleWriter()
                xml_processor = pmm_dump_overview.InfoXMLProcessor( self.get_info_xml(),
                                                                    pmm_dump_overview.InfoXmlHandler( pmm_dump_overview.RestoreSpecificationXMLTransformWorker(pmm_dump_overview.RestoreSpecificationXMLTransformSpecification(secure_transform_file),
                                                                                                                                                               writer),
                                                                                                      pmm_dump_overview.XMLTransformPathProcessor()) )
                xml_processor.process()
                result_restore_specification_file = self.get_info_xml()
                with open(result_restore_specification_file, 'wt') as result_restore_specification:
                    result_restore_specification.write(writer.get_content())

