Michal Koutný ccf7f1
# This work is licensed under the GNU GPLv2 or later.
Michal Koutný ccf7f1
# See the COPYING file in the top-level directory.
Michal Koutný ccf7f1
Michal Koutný ccf7f1
from logging import getLogger
Michal Koutný ccf7f1
import sys
Michal Koutný ccf7f1
from xmlrpc.client import (Binary, Fault, ProtocolError,
Michal Koutný ccf7f1
                           ServerProxy, Transport)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
from requests import RequestException
Michal Koutný ccf7f1
Michal Koutný ccf7f1
from ._backendbase import _BackendBase
Michal Koutný ccf7f1
from .exceptions import BugzillaError
Michal Koutný ccf7f1
from ._util import listify
Michal Koutný ccf7f1
Michal Koutný ccf7f1
Michal Koutný ccf7f1
log = getLogger(__name__)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
Michal Koutný ccf7f1
class _BugzillaXMLRPCTransport(Transport):
Michal Koutný ccf7f1
    def __init__(self, bugzillasession):
Michal Koutný ccf7f1
        if hasattr(Transport, "__init__"):
Michal Koutný ccf7f1
            Transport.__init__(self, use_datetime=False)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        self.__bugzillasession = bugzillasession
Michal Koutný ccf7f1
        self.__bugzillasession.set_xmlrpc_defaults()
Michal Koutný ccf7f1
        self.__seen_valid_xml = False
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        # Override Transport.user_agent
Michal Koutný ccf7f1
        self.user_agent = self.__bugzillasession.get_user_agent()
Michal Koutný ccf7f1
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    ############################
Michal Koutný ccf7f1
    # Bugzilla private helpers #
Michal Koutný ccf7f1
    ############################
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def __request_helper(self, url, request_body):
Michal Koutný ccf7f1
        """
Michal Koutný ccf7f1
        A helper method to assist in making a request and parsing the response.
Michal Koutný ccf7f1
        """
Michal Koutný ccf7f1
        response = None
Michal Koutný ccf7f1
        # pylint: disable=try-except-raise
Michal Koutný ccf7f1
        # pylint: disable=raise-missing-from
Michal Koutný ccf7f1
        try:
Michal Koutný ccf7f1
            response = self.__bugzillasession.request(
Michal Koutný ccf7f1
                "POST", url, data=request_body)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
            return self.parse_response(response)
Michal Koutný ccf7f1
        except RequestException as e:
Michal Koutný ccf7f1
            if not response:
Michal Koutný ccf7f1
                raise
Michal Koutný ccf7f1
            raise ProtocolError(  # pragma: no cover
Michal Koutný ccf7f1
                url, response.status_code, str(e), response.headers)
Michal Koutný ccf7f1
        except Fault:
Michal Koutný ccf7f1
            raise
Michal Koutný ccf7f1
        except Exception:
Michal Koutný ccf7f1
            msg = str(sys.exc_info()[1])
Michal Koutný ccf7f1
            if not self.__seen_valid_xml:
Michal Koutný ccf7f1
                msg += "\nThe URL may not be an XMLRPC URL: %s" % url
Michal Koutný ccf7f1
            e = BugzillaError(msg)
Michal Koutný ccf7f1
            # pylint: disable=attribute-defined-outside-init
Michal Koutný ccf7f1
            e.__traceback__ = sys.exc_info()[2]
Michal Koutný ccf7f1
            # pylint: enable=attribute-defined-outside-init
Michal Koutný ccf7f1
            raise e
Michal Koutný ccf7f1
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    ######################
Michal Koutný ccf7f1
    # Tranport overrides #
Michal Koutný ccf7f1
    ######################
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def parse_response(self, response):
Michal Koutný ccf7f1
        """
Michal Koutný ccf7f1
        Override Transport.parse_response
Michal Koutný ccf7f1
        """
Michal Koutný ccf7f1
        parser, unmarshaller = self.getparser()
Michal Koutný ccf7f1
        msg = response.text.encode('utf-8')
Michal Koutný ccf7f1
        try:
Michal Koutný ccf7f1
            parser.feed(msg)
Michal Koutný ccf7f1
        except Exception:  # pragma: no cover
Michal Koutný ccf7f1
            log.debug("Failed to parse this XMLRPC response:\n%s", msg)
Michal Koutný ccf7f1
            raise
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        self.__seen_valid_xml = True
Michal Koutný ccf7f1
        parser.close()
Michal Koutný ccf7f1
        return unmarshaller.close()
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def request(self, host, handler, request_body, verbose=0):
Michal Koutný ccf7f1
        """
Michal Koutný ccf7f1
        Override Transport.request
Michal Koutný ccf7f1
        """
Michal Koutný ccf7f1
        # Setting self.verbose here matches overrided request() behavior
Michal Koutný ccf7f1
        # pylint: disable=attribute-defined-outside-init
Michal Koutný ccf7f1
        self.verbose = verbose
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        url = "%s://%s%s" % (self.__bugzillasession.get_scheme(),
Michal Koutný ccf7f1
                host, handler)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        # xmlrpclib fails to escape \r
Michal Koutný ccf7f1
        request_body = request_body.replace(b'\r', b'
')
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        return self.__request_helper(url, request_body)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
Michal Koutný ccf7f1
class _BugzillaXMLRPCProxy(ServerProxy, object):
Michal Koutný ccf7f1
    """
Michal Koutný ccf7f1
    Override of xmlrpc ServerProxy, to insert bugzilla API auth
Michal Koutný ccf7f1
    into the XMLRPC request data
Michal Koutný ccf7f1
    """
Michal Koutný ccf7f1
    def __init__(self, uri, bugzillasession, *args, **kwargs):
Michal Koutný ccf7f1
        self.__bugzillasession = bugzillasession
Michal Koutný ccf7f1
        transport = _BugzillaXMLRPCTransport(self.__bugzillasession)
Michal Koutný ccf7f1
        ServerProxy.__init__(self, uri, transport, *args, **kwargs)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def _ServerProxy__request(self, methodname, params):
Michal Koutný ccf7f1
        """
Michal Koutný ccf7f1
        Overrides ServerProxy _request method
Michal Koutný ccf7f1
        """
Michal Koutný ccf7f1
        # params is a singleton tuple, enforced by xmlrpc.client.dumps
Michal Koutný ccf7f1
        newparams = params and params[0].copy() or {}
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        log.debug("XMLRPC call: %s(%s)", methodname, newparams)
Michal Koutný ccf7f1
        authparams = self.__bugzillasession.get_auth_params()
Michal Koutný ccf7f1
        authparams.update(newparams)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        # pylint: disable=no-member
Michal Koutný ccf7f1
        ret = ServerProxy._ServerProxy__request(
Michal Koutný ccf7f1
            self, methodname, (authparams,))
Michal Koutný ccf7f1
        # pylint: enable=no-member
Michal Koutný ccf7f1
Michal Koutný ccf7f1
        return ret
Michal Koutný ccf7f1
Michal Koutný ccf7f1
Michal Koutný ccf7f1
class _BackendXMLRPC(_BackendBase):
Michal Koutný ccf7f1
    """
Michal Koutný ccf7f1
    Internal interface for direct calls to bugzilla's XMLRPC API
Michal Koutný ccf7f1
    """
Michal Koutný ccf7f1
    def __init__(self, url, bugzillasession):
Michal Koutný ccf7f1
        _BackendBase.__init__(self, url, bugzillasession)
Michal Koutný ccf7f1
        self._xmlrpc_proxy = _BugzillaXMLRPCProxy(url, self._bugzillasession)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def get_xmlrpc_proxy(self):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy
Michal Koutný ccf7f1
    def is_xmlrpc(self):
Michal Koutný ccf7f1
        return True
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def bugzilla_version(self):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bugzilla.version()
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def bug_attachment_get(self, attachment_ids, paramdict):
Michal Koutný ccf7f1
        data = paramdict.copy()
Michal Koutný ccf7f1
        data["attachment_ids"] = listify(attachment_ids)
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.attachments(data)
Michal Koutný ccf7f1
    def bug_attachment_get_all(self, bug_ids, paramdict):
Michal Koutný ccf7f1
        data = paramdict.copy()
Michal Koutný ccf7f1
        data["ids"] = listify(bug_ids)
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.attachments(data)
Michal Koutný ccf7f1
    def bug_attachment_create(self, bug_ids, data, paramdict):
Michal Koutný ccf7f1
        pdata = paramdict.copy()
Michal Koutný ccf7f1
        pdata["ids"] = listify(bug_ids)
Michal Koutný ccf7f1
        if data is not None and "data" not in paramdict:
Michal Koutný ccf7f1
            pdata["data"] = Binary(data)
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.add_attachment(pdata)
Michal Koutný ccf7f1
    def bug_attachment_update(self, attachment_ids, paramdict):
Michal Koutný ccf7f1
        data = paramdict.copy()
Michal Koutný ccf7f1
        data["ids"] = listify(attachment_ids)
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.update_attachment(data)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def bug_comments(self, bug_ids, paramdict):
Michal Koutný ccf7f1
        data = paramdict.copy()
Michal Koutný ccf7f1
        data["ids"] = listify(bug_ids)
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.comments(data)
Michal Koutný ccf7f1
    def bug_create(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.create(paramdict)
Michal Koutný ccf7f1
    def bug_fields(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.fields(paramdict)
Michal Koutný ccf7f1
    def bug_get(self, bug_ids, aliases, paramdict):
Michal Koutný ccf7f1
        data = paramdict.copy()
Michal Koutný ccf7f1
        data["ids"] = listify(bug_ids) or []
Michal Koutný ccf7f1
        data["ids"] += listify(aliases) or []
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.get(data)
Michal Koutný ccf7f1
    def bug_history(self, bug_ids, paramdict):
Michal Koutný ccf7f1
        data = paramdict.copy()
Michal Koutný ccf7f1
        data["ids"] = listify(bug_ids)
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.history(data)
Michal Koutný ccf7f1
    def bug_search(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.search(paramdict)
Michal Koutný ccf7f1
    def bug_update(self, bug_ids, paramdict):
Michal Koutný ccf7f1
        data = paramdict.copy()
Michal Koutný ccf7f1
        data["ids"] = listify(bug_ids)
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.update(data)
Michal Koutný ccf7f1
    def bug_update_tags(self, bug_ids, paramdict):
Michal Koutný ccf7f1
        data = paramdict.copy()
Michal Koutný ccf7f1
        data["ids"] = listify(bug_ids)
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Bug.update_tags(data)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def component_create(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Component.create(paramdict)
Michal Koutný ccf7f1
    def component_update(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Component.update(paramdict)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def externalbugs_add(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.ExternalBugs.add_external_bug(paramdict)
Michal Koutný ccf7f1
    def externalbugs_update(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.ExternalBugs.update_external_bug(paramdict)
Michal Koutný ccf7f1
    def externalbugs_remove(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.ExternalBugs.remove_external_bug(paramdict)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def group_get(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Group.get(paramdict)
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def product_get(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Product.get(paramdict)
Michal Koutný ccf7f1
    def product_get_accessible(self):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Product.get_accessible_products()
Michal Koutný ccf7f1
    def product_get_enterable(self):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Product.get_enterable_products()
Michal Koutný ccf7f1
    def product_get_selectable(self):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.Product.get_selectable_products()
Michal Koutný ccf7f1
Michal Koutný ccf7f1
    def user_create(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.User.create(paramdict)
Michal Koutný ccf7f1
    def user_get(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.User.get(paramdict)
Michal Koutný ccf7f1
    def user_login(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.User.login(paramdict)
Michal Koutný ccf7f1
    def user_logout(self):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.User.logout()
Michal Koutný ccf7f1
    def user_update(self, paramdict):
Michal Koutný ccf7f1
        return self._xmlrpc_proxy.User.update(paramdict)