Back to index

moin  1.9.0~rc2
capat.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 
00003 """
00004     MoinMoin - Entity Capabilities (XEP-0115) implementation
00005 
00006     Enables Jabber/XMPP clients to save bandwidth by caching
00007     information about extensions supported by various client
00008     implementations.
00009 
00010     @copyright: 2007 by Robert Lehmann <lehmannro@gmail.com>
00011                 2008 by Bolesław Kulbabiński <bolekk@gmail.com>
00012     @license: GNU GPL, see COPYING for details.
00013 """
00014 
00015 import base64
00016 import itertools
00017 from MoinMoin.support.python_compatibility import hash_new
00018 from pyxmpp.presence import Presence
00019 
00020 HASHALIASES = { # IANA Hash Function Textual Names Registry
00021                 # to `hashlib.new` mapping
00022     'sha-1': 'sha1',
00023     'sha-224': 'sha224',
00024     'sha-256': 'sha256',
00025     'sha-384': 'sha384',
00026     'sha-512': 'sha512',
00027     'md5': 'md5',
00028     'md2': 'md2',
00029 }
00030 
00031 
00032 def generate_ver(identities, features, algo='sha-1'):
00033     """Generate the 'ver' attribute according to XEP-0115.
00034 
00035     See http://www.xmpp.org/extensions/xep-0115.html#ver
00036 
00037     @param identities: a number of (category, type) identity pairs
00038     @param algo: optional algo attribute with IANA aliasing
00039 
00040     @type identities: iterable of 2-tuples of strings
00041     @type features: iterable of strings
00042     @type algo: string (IANA Hash Function Textual Name)
00043     """
00044 
00045     # only IANA aliases are supported
00046     if algo not in HASHALIASES:
00047         raise ValueError("undefined hash algorithm")
00048     algo = hash_new(HASHALIASES[algo])
00049 
00050     ident = list(identities)
00051     # default sorting already considers both, category and type
00052     ident.sort()
00053     ident = ['%s/%s' % (idcat, idtype) for idcat, idtype in ident]
00054 
00055     feat = list(features)
00056     # strings (byte arrays) are ordered by i;octet by default
00057     feat.sort()
00058 
00059     s = '<'.join(itertools.chain(ident, feat, ('', )))
00060     # the trailing empty string adds a trailing '<' to the result
00061     algo.update(s)
00062     s = base64.b64encode(algo.digest())
00063 
00064     return s
00065 
00066 def hash_iq(stanza, algo='sha-1'):
00067     """Search an <Iq/> entity for features/identities and generate a
00068     'ver' attribute hash.
00069 
00070     @type stanza: pyxmpp.iq.Iq
00071     """
00072     stanza = iter(stanza.get_query())
00073     stanza.next() # drop first item: whole query
00074 
00075     feat = []
00076     ident = []
00077 
00078     # traverse all child nodes
00079     for item in stanza:
00080         if item.name == 'identity':
00081             ident.append((item.prop('category'), item.prop('type')))
00082         elif item.name == 'feature':
00083             feat.append(item.prop('var'))
00084 
00085     return generate_ver(ident, feat, algo)
00086 
00087 # <identity /> and <feature /> attributes
00088 IDENT = (('category', 'client'),
00089          ('type', 'bot'))
00090 FEAT = ('http://jabber.org/protocol/disco#info',
00091         'jabber:x:data') # data forms
00092 NODE = "http://moinmo.in/#1.7"
00093 
00094 def create_presence(jid):
00095     """ Creates a presence stanza (as described in XEP-0115)
00096 
00097     @param jid: bot's jabber ID
00098     """
00099     pres = Presence(from_jid=jid)
00100 
00101     c = pres.add_new_content('http://jabber.org/protocol/caps', 'c')
00102     c.setProp('node', NODE)
00103 
00104     ver = generate_ver(IDENT, FEAT)
00105     c.setProp('ver', ver)
00106 
00107     return pres
00108 
00109 def create_response(disco_query):
00110     """ Creates an <Iq /> tag as a response to a service discovery query
00111 
00112     @param disco_query: received query
00113     """
00114     response = disco_query.make_result_response()
00115     query = response.new_query(ns_uri='http://jabber.org/protocol/disco#info')
00116 
00117     ident = query.newChild(None, 'identity', None)
00118     for item in IDENT:
00119         ident.setProp(item[0], item[1])
00120 
00121     for item in FEAT:
00122         query.newChild(None, 'feature', None).setProp('var', item)
00123 
00124     return response
00125