def sendop(self, jid, methname, *methargs, **keywords): """sendop() -- utility function for send(). Do not call. """ timeout = keywords.pop('timeout', None) if (keywords): raise ValueError, 'unknown keyword argument to send' msg = interface.Node('iq', attrs={'to': unicode(jid), 'type': 'set'}) qnod = msg.setchild('query', namespace=interface.NS_RPC) methnod = rpcdata.makecall(methname, *methargs) qnod.addchild(methnod) id = self.agent.send(msg) defer = sched.Deferred(self.handlesend) dsp = self.agent.adddispatcher(defer.queue, self.agent, 'result', name='iq', type=('result', 'error'), id=id, accept=True) if (timeout != None): ac = self.agent.addtimer(defer, 'timeout', delay=timeout) defer.addaction(ac) raise defer
def makeparams(argls): """makeparams(argls) -> Node Return a <params> node containing the list of arguments *argls*, in XML-RPC format. The arguments are converted with makevalue(), so you can pass in either RPCType objects or Python data. """ parnod = interface.Node('params') for arg in argls: pnod = interface.Node('param') valnod = makevalue(arg) pnod.addchild(valnod) parnod.addchild(pnod) return parnod
def makefaultresponse(faultcode, faultstring=''): """makefaultresponse(faultcode [, faultstring='']) -> Node Return a <methodResponse> node containing a <fault> with the given *faultcode* and *faultstring*. *faultcode* must be an int, and *faultstring* a string (or values convertable to those types). Do not pass RPCInt or RPCString objects. """ rpccode = RPCInt(faultcode) rpcstring = RPCString(faultstring) callnod = interface.Node('methodResponse') faultnod = interface.Node('fault') callnod.addchild(faultnod) valnod = makevalue(RPCStruct(faultCode=rpccode, faultString=rpcstring)) faultnod.addchild(valnod) return callnod
def makediscoitems(items): """makediscoitems(items) -> Node Given a DiscoItems object, return a <query> node in the disco#items namespace, containing the item list. """ qnod = interface.Node('query') qnod.setnamespace(interface.NS_DISCO_ITEMS) for (jid, name, node) in items.items: nod = interface.Node('item', attrs={'jid': jid}) if (name): nod.setattr('name', name) if (node): nod.setattr('node', node) qnod.addchild(nod) return qnod
def makediscofeatures(features): """makediscofeatures(list) -> list Given a list of strings, return a list of <feature> nodes. """ ls = [] for st in features: nod = interface.Node('feature', attrs={'var': st}) ls.append(nod) return ls
def set(self, avail=True): """set(avail=True) -> None Initially, you are unavailable. If you call set(), you are available. If you call set(False), you're unavailable. I told you it was simplistic. You should not call this method until the Jabber connection has reached the 'authresource' state. """ avail = bool(avail) if (self.available == avail): return if (avail): self.log.debug('announcing available') msg = interface.Node('presence') try: for hook in self.hooklist: res = hook(msg) if (res != None): msg = res self.agent.send(msg, addid=False, addfrom=False) except interface.StanzaHandled: pass else: self.log.debug('announcing unavailable') msg = interface.Node('presence', attrs={'type':'unavailable'}) try: for hook in self.hooklist: res = hook(msg) if (res != None): msg = res self.agent.send(msg, addid=False) except interface.StanzaHandled: pass self.available = avail
def makenode(self): """makenode() -> Node Convert the form to a Node tree. The returned node will be an <x> node in the 'jabber:x:data' namespace. """ dic = { 'type':'result' } formnod = interface.Node('x', dic) formnod.setnamespace(interface.NS_DATA) for (var, val, label, typ) in self.fields: dic = { 'var':var } if (label): dic['label'] = label if (typ): dic['type'] = typ nod = interface.Node('field', dic) nod.setchilddata('value', val) formnod.addchild(nod) return formnod
def makediscoidentities(identities): """makediscoidentities(list) -> list Given a list of (category, type, name) tuples, return a list of <identity> nodes. """ ls = [] for (cat, typ, name) in identities: nod = interface.Node('identity', attrs={'category': cat, 'type': typ}) if (name): nod.setattr('name', name) ls.append(nod) return ls
def makecall(method, *argls): """makecall(method, [ arg1, arg2, ... ]) -> Node Return a <methodCall> node containing the <methodName> *method*, and the <params> given by the succeeding arguments *argls*. The arguments (if any) are converted with makevalue(), so you can pass in either RPCType objects or Python data. """ if (not nameisvalid(method)): raise ValueError('\'' + method + '\' is not a valid method name') callnod = interface.Node('methodCall') callnod.setchilddata('methodName', method) if (argls): parnod = makeparams(argls) callnod.addchild(parnod) return callnod
def deferreditemswrapper(self, tup, jidstr, id): """deferreditemswrapper(tup, jidstr, id) -- callback for deferred handlers. This is used by handlegetitems(), for the case where a disco handler wants to undertake a deferral operation. You should not call it, or even try to understand it. """ items = sched.Deferred.extract(tup) if (not isinstance(items, DiscoItems)): raise TypeError('items must be or return a DiscoItems') msg = interface.Node('iq', attrs={ 'to':jidstr, 'type':'result', 'id':id }) qnod = discodata.makediscoitems(items) msg.addchild(qnod) self.agent.send(msg)
def makediscoinfo(info): """makediscoinfo(info) -> Node Given a DiscoInfo object, return a <query> node in the disco#info namespace, containing the identities, features, and extended info. """ qnod = interface.Node('query') qnod.setnamespace(interface.NS_DISCO_INFO) for nod in makediscoidentities(info.identities): qnod.addchild(nod) for nod in makediscofeatures(info.features): qnod.addchild(nod) if (info.extended): nod = makediscoextended(info.extended) qnod.addchild(nod) return qnod
def makeresponse(arg=True): """makeresponse(arg=True) -> Node Return a <methodResponse> node containing the <params> given by the argument *arg*. The argument is converted with makevalue(), so you can pass in either an RPCType object or Python data. An RPC response requires a value. There is no equivalent of null/None, so if you pass that in here, it is taken to be (boolean) True. """ callnod = interface.Node('methodResponse') if (arg == None): arg = True parnod = makeparams([arg]) callnod.addchild(parnod) return callnod
def queryitemsop(self, jid, node=None, timeout=None): """queryitemsop() -- utility function for queryitems(). Do not call. """ msg = interface.Node('iq', attrs={ 'to':unicode(jid), 'type':'get' }) qnod = msg.setchild('query', namespace=interface.NS_DISCO_ITEMS) if (node): qnod.setattr('node', unicode(node)) id = self.agent.send(msg) defer = sched.Deferred(self.handlequeryitems) dsp = self.agent.adddispatcher(defer.queue, self.agent, 'result', name='iq', type=('result','error'), id=id, accept=True) if (timeout != None): ac = self.agent.addtimer(defer, 'timeout', delay=timeout) defer.addaction(ac) raise defer
class DiscoService(service.Service): """DiscoService: A high-level Jabber facility for responding to service discovery queries from other entities. (Service label: 'discoservice'.) The DiscoService is a generic framework for disco queries (both info and items). It receives the query stanza and then looks up the results in the information you have supplied. You can set up info and items replies for any query node (including the default "no node specified" query). You do this by calling the service's addinfo() and additems() methods. You can provide either an DiscoInfo / DiscoItems instance (see the discodata module for these classes), or a callable which does one of the following: * Return a DiscoInfo / DiscoItems instance (as appropriate). * Raise an interface.StanzaError. This will cause the query 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. DiscoService() -- constructor. Public methods: addinfo() -- add an info query response. getinfo() -- get an info query response. additems() -- add an items query response. getitems() -- get an items query response. Internal methods: attach() -- attach this Service to a JabberStream. handleget() -- disco stanza handler. handlegetinfo() -- disco stanza handler. handlegetitems() -- disco stanza handler. deferredinfowrapper() -- callback for deferred handlers. deferreditemswrapper() -- callback for deferred handlers. """ label = 'discoservice' logprefix = 'zymb.jabber.disco' def __init__(self): service.Service.__init__(self) self.info = {} self.items = {} 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 disco queries. """ service.Service.attach(self, agent) self.agent.adddispatcher(self.handleget, name='iq', type='get') def addinfo(self, node=None, info=None): """addinfo(node=None, info=None) -> DiscoInfo or callable Add a response for disco-info queries to the given *node*. If *node* is None, the response applies to queries that have no node attribute. The *info* should be either a DiscoInfo object, a function which returns a DiscoInfo object, or None. (If None, a new DiscoInfo is generated and initialized with the 'http://jabber.org/protocol/disco#info' and 'http://jabber.org/protocol/disco#items' features.) The return value is the *info* you passed in, or the newly-generated DiscoInfo if you passed None. """ if (not info): info = DiscoInfo() info.addfeature(interface.NS_DISCO_INFO) info.addfeature(interface.NS_DISCO_ITEMS) self.info[node] = info return info def getinfo(self, node=None): """getinfo(node=None) -> DiscoInfo or callable Return the response set for disco-info queries to the given *node*. If *node* is None, the response applies to queries that have no node attribute. """ return self.info.get(node) def additems(self, node=None, items=None): """additems(node=None, items=None) -> DiscoItems or callable Add a response for disco-items queries to the given *node*. If *node* is None, the response applies to queries that have no node attribute. The *items* should be either a DiscoItems object, a function which returns a DiscoItems object, or None. (If None, a new DiscoItems is generated.) The return value is the *items* you passed in, or the newly-generated DiscoItems if you passed None. """ if (not items): items = DiscoItems() self.items[node] = items return items def getitems(self, node=None): """getitems(node=None) -> DiscoItems or callable Return the response set for disco-items queries to the given *node*. If *node* is None, the response applies to queries that have no node attribute. """ return self.items.get(node) def handleget(self, msg): """handleget() -- disco stanza handler. Do not call. This checks to see if the query stanza is in a disco query namespace. If so, it calls handlegetinfo or handlegetitems. """ qnod = msg.getchild('query') if (not qnod): return ns = qnod.getnamespace() if (ns == interface.NS_DISCO_INFO): fromstr = msg.getattr('from') id = msg.getattr('id') node = qnod.getattr('node') self.handlegetinfo(fromstr, id, node) return if (ns == interface.NS_DISCO_ITEMS): fromstr = msg.getattr('from') id = msg.getattr('id') node = qnod.getattr('node') self.handlegetitems(fromstr, id, node) return # Not a disco query after all return def handlegetinfo(self, jidstr, id, node=None): """handlegetinfo() -- disco stanza handler. Do not call. """ self.log.debug('Disco#info from <%s>, id %s, node %s', jidstr, id, node) if (not self.info.has_key(node)): if (not node): raise interface.StanzaItemNotFound('info does not exist') else: raise interface.StanzaItemNotFound('info for node "%s" does not exist' % node) info = self.info[node] if (not isinstance(info, DiscoInfo)): try: info = info() except sched.Deferred, ex: ex.addcontext(self.deferredinfowrapper, jidstr, id) raise if (not isinstance(info, DiscoInfo)): raise TypeError('info must be or return a DiscoInfo') msg = interface.Node('iq', attrs={ 'to':jidstr, 'type':'result', 'id':id }) qnod = discodata.makediscoinfo(info) msg.addchild(qnod) self.agent.send(msg) raise interface.StanzaHandled
if (not node): raise interface.StanzaItemNotFound('items do not exist') else: raise interface.StanzaItemNotFound('items for node "%s" do not exist' % node) items = self.items[node] if (not isinstance(items, DiscoItems)): try: items = items() except sched.Deferred, ex: ex.addcontext(self.deferreditemswrapper, jidstr, id) raise if (not isinstance(items, DiscoItems)): raise TypeError('items must be or return a DiscoItems') msg = interface.Node('iq', attrs={ 'to':jidstr, 'type':'result', 'id':id }) qnod = discodata.makediscoitems(items) msg.addchild(qnod) self.agent.send(msg) raise interface.StanzaHandled def deferreditemswrapper(self, tup, jidstr, id): """deferreditemswrapper(tup, jidstr, id) -- callback for deferred handlers. This is used by handlegetitems(), for the case where a disco handler wants to undertake a deferral operation. You should not call it, or even try to understand it. """
def buildnode(self): valnod = interface.Node('value') valnod.setchilddata(self.typename, self.argstring) return valnod