Changeset 8467 for branches/jingle

Show
Ignore:
Timestamp:
08/07/07 18:34:09 (16 months ago)
Author:
liori
Message:

Jingle: fixes for content-description negotiation.

Location:
branches/jingle/src
Files:
2 modified

Legend:

Unmodified
Added
Removed
  • branches/jingle/src/chat_control.py

    r8461 r8467  
    11931193 
    11941194        def _on_start_voip_menuitem_activate(self, *things): 
    1195                 print 'Start VoiP' 
    1196                 gajim.connections[self.account].startVoiP(self.contact.jid) 
     1195                gajim.connections[self.account].startVoiP(self.contact.jid+'/'+self.contact.resource) 
    11971196 
    11981197        def _update_gpg(self): 
  • branches/jingle/src/common/jingle.py

    r8461 r8467  
    3131        ''' This represents one jingle session. ''' 
    3232        __metaclass__=meta.VerboseClassType 
    33         def __init__(self, con, weinitiate, jid): 
     33        def __init__(self, con, weinitiate, jid, sid=None): 
    3434                ''' con -- connection object, 
    3535                    weinitiate -- boolean, are we the initiator? 
     
    4848                # what state is session in? (one from JingleStates) 
    4949                self.state=JingleStates.ended 
    50                 self.sid=con.connection.getAnID()       # sessionid 
     50                if not sid: 
     51                        sid=con.connection.getAnID() 
     52                self.sid=sid            # sessionid 
    5153 
    5254                # callbacks to call on proper contents 
    53                 # use .prepend() to add new callbacks 
    54                 self.callbacks=dict((key, [self.__defaultCB]) for key in 
    55                         ('content-add', 'content-modify', 
    56                          'content-remove', 'session-accept', 'session-info', 
    57                          'session-initiate', 'session-terminate', 
    58                          'transport-info')) 
    59                 self.callbacks['iq-result']=[] 
    60                 self.callbacks['iq-error']=[] 
    61  
    62                 self.callbacks['content-accept']=[self.__contentAcceptCB, self.__defaultCB] 
     55                # use .prepend() to add new callbacks, especially when you're going 
     56                # to send error instead of ack 
     57                self.callbacks={ 
     58                        'content-accept':       [self.__contentAcceptCB, self.__defaultCB], 
     59                        'content-add':          [self.__defaultCB], 
     60                        'content-modify':       [self.__defaultCB], 
     61                        'content-remove':       [self.__defaultCB], 
     62                        'session-accept':       [self.__contentAcceptCB, self.__defaultCB], 
     63                        'session-info':         [self.__defaultCB], 
     64                        'session-initiate':     [self.__sessionInitiateCB, self.__defaultCB], 
     65                        'session-terminate':    [self.__defaultCB], 
     66                        'transport-info':       [self.__defaultCB], 
     67                        'iq-result':            [], 
     68                        'iq-error':             [], 
     69                } 
    6370 
    6471        ''' Middle-level functions to manage contents. Handle local content 
     
    94101        def startSession(self): 
    95102                ''' Start session. ''' 
    96                 self.__sessionInitiate(self) 
     103                self.__sessionInitiate() 
    97104 
    98105        def sendSessionInfo(self): pass 
     
    129136                and stop processing. ''' 
    130137                response = stanza.buildReply('result') 
    131                 self.connection.send(response) 
    132                 raise xmpp.NodeProcessed 
     138                self.connection.connection.send(response) 
    133139 
    134140        def __contentAcceptCB(self, stanza, jingle, error): 
     
    141147                         
    142148 
     149        def sessionInitiateCB(self, stanza): 
     150                ''' We got a jingle session request from other entity, 
     151                therefore we are the receiver... Unpack the data. ''' 
     152                jingle = stanza.getTag('jingle') 
     153                self.initiator = jingle['initiator'] 
     154                self.responder = self.ourjid 
     155                self.jid = self.initiator 
     156 
     157                fail = True 
     158                for element in jingle.iterTags('content'): 
     159                        # checking what kind of session this will be 
     160                        desc_ns = element.getTag('description').getNamespace() 
     161                        tran_ns = element.getTag('transport').getNamespace() 
     162                        if desc_ns==xmpp.NS_JINGLE_AUDIO and tran_ns==xmpp.NS_JINGLE_ICE_UDP: 
     163                                # we've got voip content 
     164                                self.addContent(element['name'], JingleVoiP(self, node=element), 'peer') 
     165                                fail = False 
     166 
     167                if fail: 
     168                        # TODO: we should send <unsupported-content/> inside too 
     169                        self.connection.connection.send( 
     170                                xmpp.Error(stanza, xmpp.NS_STANZAS + 'feature-not-implemented')) 
     171                        self.connection.deleteJingle(self) 
     172                        raise xmpp.NodeProcessed 
     173 
     174                self.state = JingleStates.pending 
     175 
    143176        ''' Methods that make/send proper pieces of XML. They check if the session 
    144177        is in appropriate state. ''' 
    145178        def __makeJingle(self, action): 
    146                 stanza = xmpp.Iq(typ='set', to=xmpp.JID(self.jid)) 
     179                stanza = xmpp.Iq(typ='set', to=xmpp.JID(self.peerjid)) 
    147180                jingle = stanza.addChild('jingle', attrs={ 
    148181                        'xmlns': 'http://www.xmpp.org/extensions/xep-0166.html#ns', 
     
    162195                        jingle.addChild('content', 
    163196                                attrs={'name': content.name, 'creator': content.creator}) 
    164                 return c 
    165197 
    166198        def __appendContents(self, jingle, full=True): 
     
    175207                stanza, jingle = self.__makeJingle('session-initiate') 
    176208                self.__appendContents(jingle) 
    177                 self.connection.send(jingle) 
     209                self.connection.connection.send(stanza) 
    178210 
    179211        def __sessionAccept(self): 
     
    181213                stanza, jingle = self.__jingle('session-accept') 
    182214                self.__appendContents(jingle, False) 
    183                 self.connection.send(stanza) 
     215                self.connection.connection.send(stanza) 
    184216                self.state=JingleStates.active 
    185217 
     
    189221                if payload: 
    190222                        jingle.addChild(node=payload) 
    191                 self.connection.send(stanza) 
     223                self.connection.connection.send(stanza) 
    192224 
    193225        def __sessionTerminate(self): 
    194226                assert self.state!=JingleStates.ended 
    195227                stanza, jingle = self.__jingle('session-terminate') 
    196                 self.connection.send(stanza) 
     228                self.connection.connection.send(stanza) 
    197229 
    198230        def __contentAdd(self): 
     
    212244 
    213245        '''Callbacks''' 
    214         def sessionInitiateCB(self, stanza): 
    215                 ''' We got a jingle session request from other entity, 
    216                 therefore we are the receiver... Unpack the data. ''' 
    217                 jingle = stanza.getTag('jingle') 
    218                 self.initiator = jingle['initiator'] 
    219                 self.responder = self.ourjid 
    220                 self.jid = self.initiator 
    221                 self.state = JingleStates.pending 
    222                 self.sid = jingle['sid'] 
    223                 for element in jingle.iterTags('content'): 
    224                         content={'creator': 'initiator', 
    225                                 'name': element['name'], 
    226                                 'description': element.getTag('description'), 
    227                                 'transport': element.getTag('transport')} 
    228                         if element.has_attr('profile'): 
    229                                 content['profile']=element['profile'] 
    230                         self.contents[('initiator', content['name'])]=content 
    231  
    232246        def sessionTerminateCB(self, stanza): pass 
     247 
     248class Codec(object): 
     249        ''' This class keeps description of a single codec. ''' 
     250        def __init__(self, name, id=None, **params): 
     251                ''' Create new codec description. ''' 
     252                self.name = name 
     253                self.id = id 
     254                self.attrs = {'name': self.name, 'id': self.id, 'channels': 1} 
     255                for key in ('channels', 'clockrate', 'maxptime', 'ptime'): 
     256                        if key in params: 
     257                                self.attrs[key]=params[key] 
     258                                del params[key] 
     259                self.params = params 
     260 
     261        def __eq__(a, b): 
     262                ''' Compare two codec descriptions. ''' 
     263                # TODO: check out what should be tested... 
     264                if a.name!=b.name: return False 
     265                # ... 
     266                return True 
     267 
     268        def toXML(self): 
     269                return xmpp.Node('payload', 
     270                        attrs=self.attrs, 
     271                        payload=(xmpp.Node('parameter', {'name': k, 'value': v}) for k,v in self.params)) 
    233272 
    234273class JingleAudioSession(object): 
    235274        __metaclass__=meta.VerboseClassType 
    236         class Codec(object): 
    237                 ''' This class keeps description of a single codec. ''' 
    238                 def __init__(self, name, id=None, **params): 
    239                         ''' Create new codec description. ''' 
    240                         self.name = name 
    241                         self.id = id 
    242                         self.attrs = {'name': self.name, 'id': self.id, 'channels': 1} 
    243                         for key in ('channels', 'clockrate', 'maxptime', 'ptime'): 
    244                                 if key in params: 
    245                                         self.attrs[key]=params[key] 
    246                                 del params[key] 
    247                         self.params = params 
    248  
    249                 def __eq__(a, b): 
    250                         ''' Compare two codec descriptions. ''' 
    251                         # TODO: check out what should be tested... 
    252                         if a.name!=b.name: return False 
    253                         # ... 
    254                         return True 
    255  
    256                 def toXML(self): 
    257                         return xmpp.Node('payload', 
    258                                 attrs=self.attrs, 
    259                                 payload=(xmpp.Node('parameter', {'name': k, 'value': v}) for k,v in self.params)) 
    260  
    261         def __init__(self, content): 
     275        def __init__(self, content, fromNode): 
    262276                self.content = content 
    263277 
    264278                self.initiator_codecs=[] 
    265279                self.responder_codecs=[] 
     280 
     281                if fromNode: 
     282                        # read all codecs peer understand 
     283                        for payload in fromNode.iterTags('payload-type'): 
     284                                attrs = fromNode.getAttrs().copy() 
     285                                for param in fromNode.iterTags('parameter'): 
     286                                        attrs[param['name']]=param['value'] 
     287                                self.initiator_codecs.append(Codec(**attrs)) 
    266288 
    267289        def sessionInitiateCB(self, stanza, ourcontent): 
     
    285307                out = [] 
    286308                ids = range(128) 
    287                 for codec in other: 
     309                for codec in other_l: 
    288310                        if codec in our_l: 
    289311                                out.append(codec) 
     
    305327        def __codecsList(self, codecs): 
    306328                ''' Prepares a description element with codecs given as a parameter. ''' 
    307                 return xmpp.Node('description', 
    308                         xmlns=xmpp.NS_JINGLE_AUDIO, 
     329                return xmpp.Node(xmpp.NS_JINGLE_AUDIO+' description', 
    309330                        payload=(codec.toXML() for codec in codecs)) 
    310331 
     
    330351        def toXML(self): 
    331352                ''' ICE-UDP doesn't send much in its transport stanza... ''' 
    332                 return xmpp.Node('transport', xmlns=xmpp.JINGLE_ICE_UDP) 
     353                return xmpp.Node(xmpp.NS_JINGLE_ICE_UDP+' transport') 
    333354 
    334355class JingleVoiP(object): 
     
    336357        over an ICE UDP protocol. ''' 
    337358        __metaclass__=meta.VerboseClassType 
    338         def __init__(self): 
    339                 self.audio = JingleAudioSession(self) 
     359        def __init__(self, session, node=None): 
     360                self.session = session 
     361 
     362                if node is None: 
     363                        self.audio = JingleAudioSession(self) 
     364                else: 
     365                        self.audio = JingleAudioSession(self, node.getTag('content')) 
    340366                self.transport = JingleICEUDPSession(self) 
    341367 
     
    344370                return xmpp.Node('content', 
    345371                        attrs={'name': self.name, 'creator': self.creator, 'profile': 'RTP/AVP'}, 
    346                         childs=[self.audio.toXML(), self.transport.toXML()]) 
    347  
    348         def _sessionInitiateCB(self): 
    349                 ''' Called when we initiate the session. ''' 
    350                 self.transport._sessionInitiateCB() 
     372                        payload=[self.audio.toXML(), self.transport.toXML()]) 
    351373 
    352374class ConnectionJingle(object): 
     
    392414                # do we need to create a new jingle object 
    393415                if (jid, sid) not in self.__sessions: 
    394                         # we should check its type here... 
    395                         newjingle = JingleAudioSession(con=self, weinitiate=False, jid=jid) 
     416                        # TODO: we should check its type here... 
     417                        newjingle = JingleSession(con=self, weinitiate=False, jid=jid, sid=sid) 
    396418                        self.addJingle(newjingle) 
    397419 
    398420                # we already have such session in dispatcher... 
    399                 return self.__sessions[(jid, sid)].stanzaCB(stanza) 
     421                self.__sessions[(jid, sid)].stanzaCB(stanza) 
     422 
     423                raise xmpp.NodeProcessed 
    400424 
    401425        def addJingleIqCallback(self, jid, id, jingle):