Show
Ignore:
Timestamp:
04/21/08 00:58:47 (7 months ago)
Author:
asterix
Message:

new XEP-0115 implementation (version 1.5)

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • trunk/src/common/caps.py

    r8926 r9508  
    2121import xmpp.features_nb 
    2222import gajim 
     23import helpers 
    2324 
    2425class CapsCache(object): 
     
    9495                class CacheItem(object): 
    9596                        ''' TODO: logging data into db ''' 
    96                         def __init__(ciself, node, version, ext=None): 
     97                        def __init__(ciself, hash_method, hash): 
    9798                                # cached into db 
    98                                 ciself.node = node 
    99                                 ciself.version = version 
    100                                 ciself.features = set() 
    101                                 ciself.ext = ext 
    102                                 ciself.exts = {} 
    103  
    104                                 # set of tuples: (category, type, name) 
    105                                 # (dictionaries are not hashable, so cannot be in sets) 
    106                                 ciself.identities = set() 
     99                                ciself.hash_method = hash_method 
     100                                ciself.hash = hash 
     101 
     102                                @property 
     103                                def features(): 
     104                                        def fget(self): 
     105                                                return self.getAttr('features') 
     106                                        def fset(self, value): 
     107                                                list_ = [] 
     108                                                for feature in value: 
     109                                                        list_.append(self.__names.setdefault(feature, feature)) 
     110                                                self.setAttr('features', list_) 
     111 
     112                                @property 
     113                                def identities(): 
     114                                        def fget(self): 
     115                                                return self.getAttr('identities') 
     116                                        def fset(self, value): 
     117                                                list_ = [] 
     118                                                for identity in value: 
     119                                                        list_.append(self.__names.setdefault(identity, identity)) 
     120                                                self.setAttr('identities', list_) 
    107121 
    108122                                # not cached into db: 
     
    113127                                ciself.queried = 0 
    114128 
    115                         class CacheQuery(object): 
    116                                 def __init__(cqself, proxied): 
    117                                         cqself.proxied=proxied 
    118  
    119                                 def __getattr__(cqself, obj): 
    120                                         if obj!='exts': return getattr(cqself.proxied[0], obj) 
    121                                         return set(chain(ci.features for ci in cqself.proxied)) 
    122  
    123                         def __getitem__(ciself, exts): 
    124                                 if not exts:    # (), [], None, False, whatever 
    125                                         return ciself 
    126                                 if isinstance(exts, basestring): 
    127                                         exts=(exts,) 
    128                                 if len(exts)==1: 
    129                                         ext=exts[0] 
    130                                         if ext in ciself.exts: 
    131                                                 return ciself.exts[ext] 
    132                                         x=CacheItem(ciself.node, ciself.version, ext) 
    133                                         ciself.exts[ext]=x 
    134                                         return x 
    135                                 proxied = [ciself] 
    136                                 proxied.extend(ciself[(e,)] for e in exts) 
    137                                 return ciself.CacheQuery(proxied) 
    138  
    139129                        def update(ciself, identities, features): 
    140130                                # NOTE: self refers to CapsCache object, not to CacheItem 
    141                                 self.identities=identities 
    142                                 self.features=features 
    143                                 self.logger.add_caps_entry( 
    144                                         ciself.node, ciself.version, ciself.ext, 
     131                                ciself.identities=identities 
     132                                ciself.features=features 
     133                                self.logger.add_caps_entry(ciself.hash_method, ciself.hash, 
    145134                                        identities, features) 
    146135 
     
    148137 
    149138                # prepopulate data which we are sure of; note: we do not log these info 
    150                 gajimnode = 'http://gajim.org/caps' 
    151  
    152                 gajimcaps=self[(gajimnode, '0.11.1')] 
    153                 gajimcaps.category='client' 
    154                 gajimcaps.type='pc' 
    155                 gajimcaps.features=set((xmpp.NS_BYTESTREAM, xmpp.NS_SI, 
    156                         xmpp.NS_FILE, xmpp.NS_MUC, xmpp.NS_COMMANDS, 
    157                         xmpp.NS_DISCO_INFO, xmpp.NS_PING, xmpp.NS_TIME_REVISED)) 
    158                 gajimcaps['cstates'].features=set((xmpp.NS_CHATSTATES,)) 
    159                 gajimcaps['xhtml'].features=set((xmpp.NS_XHTML_IM,)) 
    160  
    161                 # TODO: older gajim versions 
     139 
     140                gajimcaps = self[('sha-1', gajim.caps_hash)] 
     141                gajimcaps.identities = [gajim.gajim_identity] 
     142                gajimcaps.features = gajim.gajim_common_features + \ 
     143                        gajim.gajim_optional_features 
    162144 
    163145                # start logging data from the net 
     
    167149                # get data from logger... 
    168150                if self.logger is not None: 
    169                         for node, ver, ext, identities, features in self.logger.iter_caps_data(): 
    170                                 x=self[(node, ver, ext)] 
    171                                 x.identities=identities 
    172                                 x.features=features 
    173                                 x.queried=2 
     151                        for hash_method, hash, identities, features in \ 
     152                        self.logger.iter_caps_data(): 
     153                                x = self[(hash_method, hash)] 
     154                                x.identities = identities 
     155                                x.features = features 
     156                                x.queried = 2 
    174157 
    175158        def __getitem__(self, caps): 
    176                 node_version = caps[:2] 
    177                 if node_version in self.__cache: 
    178                         return self.__cache[node_version][caps[2]] 
    179                 node, version = self.__names.setdefault(caps[0], caps[0]), caps[1] 
    180                 x=self.__CacheItem(node, version) 
    181                 self.__cache[(node, version)]=x 
     159                if caps in self.__cache: 
     160                        return self.__cache[caps] 
     161                hash_method, hash = caps[0], caps[1] 
     162                x = self.__CacheItem(hash_method, hash) 
     163                self.__cache[(hash_method, hash)] = x 
    182164                return x 
    183165 
    184         def preload(self, account, jid, node, ver, exts): 
     166        def preload(self, con, jid, node, hash_method, hash): 
    185167                ''' Preload data about (node, ver, exts) caps using disco 
    186168                query to jid using proper connection. Don't query if 
    187169                the data is already in cache. ''' 
    188                 q=self[(node, ver, ())] 
    189                 qq=q 
     170                q = self[(hash_method, hash)] 
    190171 
    191172                if q.queried==0: 
    192                         # do query for bare node+version pair 
     173                        # do query for bare node+hash pair 
    193174                        # this will create proper object 
    194175                        q.queried=1 
    195                         account.discoverInfo(jid, '%s#%s' % (node, ver)) 
    196  
    197                 for ext in exts: 
    198                         qq=q[ext] 
    199                         if qq.queried==0: 
    200                                 # do query for node+version+ext triple 
    201                                 qq.queried=1 
    202                                 account.discoverInfo(jid, '%s#%s' % (node, ext)) 
     176                        con.discoverInfo(jid, '%s#%s' % (node, hash)) 
    203177 
    204178gajim.capscache = CapsCache(gajim.logger) 
     
    211185 
    212186                # get the caps element 
    213                 caps=presence.getTag('c') 
    214                 if not caps: return 
    215  
    216                 node, ver=caps['node'], caps['ver'] 
    217                 if node is None or ver is None: 
     187                caps = presence.getTag('c') 
     188                if not caps: 
     189                        return 
     190 
     191                hash_method, node, hash = caps['hash'], caps['node'], caps['ver'] 
     192                if hash_method is None or node is None or hash is None: 
    218193                        # improper caps in stanza, ignoring 
    219194                        return 
    220  
    221                 try: 
    222                         exts=caps['ext'].split(' ') 
    223                 except AttributeError: 
    224                         # no exts means no exts, a perfectly valid case 
    225                         exts=[] 
    226195 
    227196                # we will put these into proper Contact object and ask 
     
    232201 
    233202                # start disco query... 
    234                 gajim.capscache.preload(self, jid, node, ver, exts) 
     203                gajim.capscache.preload(self, jid, node, hash_method, hash) 
    235204 
    236205                contact=gajim.contacts.get_contact_from_full_jid(self.name, jid) 
     
    241210 
    242211                # overwriting old data 
    243                 contact.caps_node=node 
    244                 contact.caps_ver=ver 
    245                 contact.caps_exts=exts 
     212                contact.caps_node = node 
     213                contact.caps_hash_method = hash_method 
     214                contact.caps_hash = hash 
    246215 
    247216        def _capsDiscoCB(self, jid, node, identities, features, data): 
    248                 contact=gajim.contacts.get_contact_from_full_jid(self.name, jid) 
    249                 if not contact: return 
    250                 if not contact.caps_node: return # we didn't asked for that? 
    251                 if not node.startswith(contact.caps_node+'#'): return 
    252                 node, ext = node.split('#', 1) 
    253                 if ext==contact.caps_ver:       # this can be also version (like '0.9') 
    254                         exts=None 
    255                 else: 
    256                         exts=(ext,) 
     217                contact = gajim.contacts.get_contact_from_full_jid(self.name, jid) 
     218                if not contact: 
     219                        return 
     220                if not contact.caps_node: 
     221                        return # we didn't asked for that? 
     222                if not node.startswith(contact.caps_node + '#'): 
     223                        return 
     224                node, hash = node.split('#', 1) 
     225                computed_hash = helpers.compute_caps_hash(identities, features, 
     226                        contact.caps_hash_method) 
     227                if computed_hash != hash: 
     228                        # wrong hash, forget it 
     229                        contact.caps_node = '' 
     230                        contact.caps_hash_method = '' 
     231                        contact.caps_hash = '' 
     232                        return 
    257233 
    258234                # if we don't have this info already... 
    259                 caps=gajim.capscache[(node, contact.caps_ver, exts)] 
    260                 if caps.queried==2: return 
    261  
    262                 identities=set((i['category'], i['type'], i.get('name')) for i in identities) 
     235                caps = gajim.capscache[(contact.caps_hash_method, hash)] 
     236                if caps.queried == 2: 
     237                        return 
     238 
    263239                caps.update(identities, features) 
    264