Exemplo n.º 1
0
    def deferredwrapper(self, tup, jidstr, id, msg, callname, callargs):
        """deferredwrapper() -- callback for deferred handlers.

        This is used by handlecall(), for the case where an RPC handler
        wants to undertake a deferral operation. You should not call it,
        or even try to understand it.
        """

        try:
            try:
                res = sched.Deferred.extract(tup)
            except CallNotFound:
                res = self.notfound(msg, callname, callargs)
            respnod = rpcdata.makeresponse(res)
        except rpcdata.RPCResponse, ex:
            respnod = rpcdata.makeresponse(ex.value)
Exemplo n.º 2
0
class RPCService(service.Service):
    """RPCService: A high-level Jabber facility for responding to Jabber-RPC
    calls from other entities.

    (Service label: 'rpcservice'.)

    The RPCService is a generic wrapper for RPC calls. It receives the RPC
    stanza, translates the Jabber-RPC arguments into Python values, and
    then passes everything on to its opset. You supply the opset when
    you create the RPCService; it contains the code which actually handles
    each RPC. (See the Opset class for more information.)

    The code in the opset has several options:

    * Return a Python value. This will become the RPC response value.
    * Return None. This will cause an RPC response with a boolean True value.
    * Raise an RPCResponse(value). This is equivalent to returning the
        value -- it causes an RPC response with that value.
    * Raise an RPCFault(code, string). This will cause an RPC fault reply,
        with the given *code* and *string*.
    * Raise a CallNotFound. This will become a stanza-level 'item-not-found'
        error, indicating that the method does not exist.
    * Raise an interface.StanzaError. This will cause the RPC to be replied
        to with the given stanza-level error.
    * Raise some other kind of Exception. This will become a stanza-level
        'internal-server-error' error.
    * Raise a sched.Deferred(op). This is a promise that you will eventually
        invoke the Deferred object. That will result in *op* being called;
        and the *op* should then do one of the things on this list.

    If you use an RPCService, you should add the string interface.NS_RPC
    ('jabber:iq:rpc') to your DiscoService features. (It would be better
    if this happened automatically, but it doesn't.)
        
    RPCService(opset=None, notfoundhandler=None) -- constructor.

    If you don't supply an opset, a bare Opset instance will be used. This
    is a minimal handler -- it doesn't accept *any* calls, and always raises
    CallNotFound. You can change the opset later with the setopset() method.

    The *notfoundhandler* is a function which is called when an RPC method
    name is not recognized by the opset. The function is invoked as
        notfoundhandler(msg, callname, callargs)
    where *msg* is a Node tree, *callname* is a string, and *callargs* is
    a tuple of values. The function must return a value or raise
    RPCResponse, RPCFault, or a StanzaError. If you do not supply
    a function, the service will raise StanzaItemNotFound for unrecognized
    RPCs.

    Public methods:

    setopset(opset) -- change the service's opset.
    getopset() -- return the service's current opset.

    Internal methods:

    attach() -- attach this Service to a JabberStream.
    handlecall() -- RPC stanza handler.
    deferredwrapper() -- callback for deferred handlers.
    """

    label = 'rpcservice'
    logprefix = 'zymb.jabber.rpc'

    def __init__(self, opset=None, notfoundhandler=None):
        service.Service.__init__(self)

        if (not opset):
            opset = Opset()
        self.opset = opset
        self.notfoundhandler = notfoundhandler

    def attach(self, agent):
        """attach() -- internal method to attach this Service to a
        JabberStream. Do not call this directly. Instead, call
        jstream.addservice(service).

        This calls the inherited class method, and then sets up the
        stanza dispatcher which catches incoming RPC calls.
        """

        service.Service.attach(self, agent)
        self.agent.adddispatcher(self.handlecall, name='iq', type='set')

    def setopset(self, opset):
        """setopset(opset) -> None

        Change the service's opset. An RPCService has exactly one opset
        at a time. (But opsets can contain other opsets. This method
        sets the top-level opset. See the Opset class for details.)
        """

        self.opset = opset

    def getopset(self):
        """getopset() -> Opset

        Return the service's current opset.
        """

        return self.opset

    def handlecall(self, msg):
        """handlecall() -- RPC stanza handler. Do not call.

        This parses the Jabber stanza, to extract the method name and
        arguments. It translates the arguments into Python objects.
        Then it invokes the service opset, and takes care of the various
        responses and exceptions which that may generate.
        """

        qnod = msg.getchild('query')
        if (not qnod):
            return

        if (qnod.getnamespace() != interface.NS_RPC):
            return

        jidstr = msg.getattr('from')
        id = msg.getattr('id')
        nod = qnod.getchild('methodCall')
        if (not nod):
            raise interface.StanzaBadRequest('no methodCall')

        try:
            (callname, callargs) = rpcdata.parsecall(nod)
        except Exception, ex:
            self.log.warning('Ill-formed RPC call from <%s>, id %s',
                             jidstr,
                             id,
                             exc_info=True)
            raise interface.StanzaBadRequest(str(ex))

        self.log.debug('RPC %s %s from <%s>, id %s', callname, callargs,
                       jidstr, id)

        try:
            if (not self.opset):
                raise CallNotFound
            jid = interface.JID(jidstr)
            try:
                res = self.opset(jid, callname, *callargs)
            except CallNotFound:
                res = self.notfound(msg, callname, callargs)
            respnod = rpcdata.makeresponse(res)
        except sched.Deferred, ex:
            ex.addcontext(self.deferredwrapper, jidstr, id, msg, callname,
                          callargs)
            raise
Exemplo n.º 3
0
        try:
            if (not self.opset):
                raise CallNotFound
            jid = interface.JID(jidstr)
            try:
                res = self.opset(jid, callname, *callargs)
            except CallNotFound:
                res = self.notfound(msg, callname, callargs)
            respnod = rpcdata.makeresponse(res)
        except sched.Deferred, ex:
            ex.addcontext(self.deferredwrapper, jidstr, id, msg, callname,
                          callargs)
            raise
        except rpcdata.RPCResponse, ex:
            respnod = rpcdata.makeresponse(ex.value)
        except rpcdata.RPCFault, ex:
            respnod = rpcdata.makefaultresponse(ex.code, ex.string)

        msg = interface.Node('iq',
                             attrs={
                                 'to': jidstr,
                                 'type': 'result',
                                 'id': id
                             })
        qnod = msg.setchild('query', namespace=interface.NS_RPC)
        qnod.addchild(respnod)

        self.agent.send(msg)
        raise interface.StanzaHandled