Changeset 8461 for branches/jingle/src/common/jingle.py
- Timestamp:
- 08/07/07 01:19:57 (17 months ago)
- Files:
-
- 1 modified
-
branches/jingle/src/common/jingle.py (modified) (15 diffs)
Legend:
- Unmodified
- Added
- Removed
-
branches/jingle/src/common/jingle.py
r8456 r8461 13 13 ''' Handles the jingle signalling protocol. ''' 14 14 15 import gajim 15 16 import xmpp 17 18 import meta 16 19 17 20 class JingleStates(object): … … 21 24 active=2 22 25 23 class WrongState(exception): pass 24 class NoCommonCodec(exception): pass 26 class Exception(object): pass 27 class WrongState(Exception): pass 28 class NoCommonCodec(Exception): pass 25 29 26 30 class JingleSession(object): 27 31 ''' This represents one jingle session. ''' 32 __metaclass__=meta.VerboseClassType 28 33 def __init__(self, con, weinitiate, jid): 29 34 ''' con -- connection object, … … 33 38 self.connection=con # connection to use 34 39 # our full jid 35 self.ourjid=gajim.get_ full_jid_from_account(self.connection.name)36 self. jid=jid# jid we connect to40 self.ourjid=gajim.get_jid_from_account(self.connection.name)+'/'+con.server_resource 41 self.peerjid=jid # jid we connect to 37 42 # jid we use as the initiator 38 self.initiator=weinitiate and self.ourjid or self. jid43 self.initiator=weinitiate and self.ourjid or self.peerjid 39 44 # jid we use as the responder 40 self.responder=weinitiate and self. jid or self.ourjid45 self.responder=weinitiate and self.peerjid or self.ourjid 41 46 # are we an initiator? 42 47 self.weinitiate=weinitiate 43 48 # what state is session in? (one from JingleStates) 44 49 self.state=JingleStates.ended 45 self.sid=con. getAnID() # sessionid50 self.sid=con.connection.getAnID() # sessionid 46 51 47 52 # callbacks to call on proper contents 48 53 # use .prepend() to add new callbacks 49 54 self.callbacks=dict((key, [self.__defaultCB]) for key in 50 ('content-a ccept', 'content-add', 'content-modify',55 ('content-add', 'content-modify', 51 56 'content-remove', 'session-accept', 'session-info', 52 57 'session-initiate', 'session-terminate', … … 55 60 self.callbacks['iq-error']=[] 56 61 62 self.callbacks['content-accept']=[self.__contentAcceptCB, self.__defaultCB] 63 57 64 ''' Middle-level functions to manage contents. Handle local content 58 65 cache and send change notifications. ''' 59 def addContent(self, name, description, transport, profile=None):66 def addContent(self, name, content, initiator='we'): 60 67 ''' Add new content to session. If the session is active, 61 68 this will send proper stanza to update session. 62 The protocol prohibits changing that when pending.''' 69 The protocol prohibits changing that when pending. 70 Initiator must be one of ('we', 'peer', 'initiator', 'responder')''' 63 71 if self.state==JingleStates.pending: 64 72 raise WrongState 65 73 66 content={'creator': 'initiator',67 'name': name,68 'description': description,69 'transport': transport}70 if profile is not None:71 content['profile']=profile72 self.contents[( 'initiator',name)]=content74 if (initiator=='we' and self.weinitiate) or (initiator=='peer' and not self.weinitiate): 75 initiator='initiator' 76 elif (initiator=='peer' and self.weinitiate) or (initiator=='we' and not self.weinitiate): 77 initiator='responder' 78 content.creator = initiator 79 content.name = name 80 self.contents[(initiator,name)]=content 73 81 74 82 if self.state==JingleStates.active: 75 83 pass # TODO: send proper stanza, shouldn't be needed now 84 85 def removeContent(self, creator, name): 86 ''' We do not need this now ''' 87 pass 88 89 def modifyContent(self, creator, name, *someother): 90 ''' We do not need this now ''' 91 pass 76 92 77 93 ''' Middle-level function to do stanza exchange. ''' … … 93 109 # it's an iq-error stanza 94 110 callables = 'iq-error' 95 el seif jingle:111 elif jingle: 96 112 # it's a jingle action 97 113 action = jingle.getAttr('action') … … 116 132 raise xmpp.NodeProcessed 117 133 134 def __contentAcceptCB(self, stanza, jingle, error): 135 ''' Called when we get content-accept stanza or equivalent one 136 (like session-accept).''' 137 # check which contents are accepted, call their callbacks 138 for content in jingle.iterTags('content'): 139 creator = content['creator'] 140 name = content['name'] 141 142 118 143 ''' Methods that make/send proper pieces of XML. They check if the session 119 144 is in appropriate state. ''' 120 def makeJingle(self, action):145 def __makeJingle(self, action): 121 146 stanza = xmpp.Iq(typ='set', to=xmpp.JID(self.jid)) 122 jingle = stanza.addChild('jingle', attrs= 147 jingle = stanza.addChild('jingle', attrs={ 123 148 'xmlns': 'http://www.xmpp.org/extensions/xep-0166.html#ns', 124 149 'action': action, … … 128 153 return stanza, jingle 129 154 130 def appendContent(self, jingle, content, full=True):155 def __appendContent(self, jingle, content, full=True): 131 156 ''' Append <content/> element to <jingle/> element, 132 157 with (full=True) or without (full=False) <content/> 133 158 children. ''' 134 c=jingle.addChild('content', attrs={135 'creator': content['creator'],136 'name': content['name']})137 if 'profile' in content:138 c['profile']=content['profile']139 159 if full: 140 c.addChild(node=content['description']) 141 c.addChild(node=content['transport']) 160 jingle.addChild(node=content.toXML()) 161 else: 162 jingle.addChild('content', 163 attrs={'name': content.name, 'creator': content.creator}) 142 164 return c 143 165 144 def appendContents(self, jingle, full=True):166 def __appendContents(self, jingle, full=True): 145 167 ''' Append all <content/> elements to <jingle/>.''' 146 168 # TODO: integrate with __appendContent? … … 151 173 def __sessionInitiate(self): 152 174 assert self.state==JingleStates.ended 175 stanza, jingle = self.__makeJingle('session-initiate') 176 self.__appendContents(jingle) 177 self.connection.send(jingle) 153 178 154 179 def __sessionAccept(self): … … 198 223 for element in jingle.iterTags('content'): 199 224 content={'creator': 'initiator', 200 'name': element['name'] 225 'name': element['name'], 201 226 'description': element.getTag('description'), 202 227 'transport': element.getTag('transport')} … … 208 233 209 234 class JingleAudioSession(object): 235 __metaclass__=meta.VerboseClassType 210 236 class Codec(object): 211 237 ''' This class keeps description of a single codec. ''' … … 233 259 payload=(xmpp.Node('parameter', {'name': k, 'value': v}) for k,v in self.params)) 234 260 235 def __init__(self, con, weinitiate, jid): 236 JingleSession.__init__(self, con, weinitiate, jid) 237 if weinitiate: 238 pass #add voice content 239 self.callbacks['session-initiate'].prepend( 261 def __init__(self, content): 262 self.content = content 240 263 241 264 self.initiator_codecs=[] 242 265 self.responder_codecs=[] 266 267 def sessionInitiateCB(self, stanza, ourcontent): 268 pass 243 269 244 270 ''' "Negotiation" of codecs... simply presenting what *we* can do, nothing more... ''' … … 283 309 payload=(codec.toXML() for codec in codecs)) 284 310 311 def toXML(self): 312 if not self.initiator_codecs: 313 # we are the initiator, so just send our codecs 314 self.initiator_codecs = self.getOurCodecs() 315 return self.__codecsList(self.initiator_codecs) 316 else: 317 # we are the responder, we SHOULD adjust our codec list 318 self.responder_codecs = self.getOurCodecs(self.initiator_codecs) 319 return self.__codecsList(self.responder_codecs) 320 285 321 class JingleICEUDPSession(object): 286 def __init__(self, con, weinitiate, jid): 322 __metaclass__=meta.VerboseClassType 323 def __init__(self, content): 324 self.content = content 325 326 def _sessionInitiateCB(self): 327 ''' Called when we initiate the session. ''' 287 328 pass 288 329 289 class JingleVoiP(JingleSession): 330 def toXML(self): 331 ''' ICE-UDP doesn't send much in its transport stanza... ''' 332 return xmpp.Node('transport', xmlns=xmpp.JINGLE_ICE_UDP) 333 334 class JingleVoiP(object): 290 335 ''' Jingle VoiP sessions consist of audio content transported 291 336 over an ICE UDP protocol. ''' 292 def __init__(*data): 293 JingleAudioSession.__init__(*data) 294 JingleICEUDPSession.__init__(*data) 337 __metaclass__=meta.VerboseClassType 338 def __init__(self): 339 self.audio = JingleAudioSession(self) 340 self.transport = JingleICEUDPSession(self) 341 342 def toXML(self): 343 ''' Return proper XML for <content/> element. ''' 344 return xmpp.Node('content', 345 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() 295 351 296 352 class ConnectionJingle(object): … … 308 364 jingle - a JingleSession object. 309 365 ''' 310 self.__sessions[(jingle. jid, jingle.sid)]=jingle366 self.__sessions[(jingle.peerjid, jingle.sid)]=jingle 311 367 312 368 def deleteJingle(self, jingle): 313 369 ''' Remove a jingle session from a jingle stanza dispatcher ''' 314 del self.__session[(jingle. jid, jingle.sid)]315 316 def _ jingleCB(self, con, stanza):370 del self.__session[(jingle.peerjid, jingle.sid)] 371 372 def _JingleCB(self, con, stanza): 317 373 ''' The jingle stanza dispatcher. 318 374 Route jingle stanza to proper JingleSession object, … … 331 387 332 388 jingle = stanza.getTag('jingle') 389 if not jingle: return 333 390 sid = jingle.getAttr('sid') 334 391 … … 342 399 return self.__sessions[(jid, sid)].stanzaCB(stanza) 343 400 344 def addJingleIqCallback( jid, id, jingle):401 def addJingleIqCallback(self, jid, id, jingle): 345 402 self.__iq_responses[(jid, id)]=jingle 403 404 def startVoiP(self, jid): 405 jingle = JingleSession(self, weinitiate=True, jid=jid) 406 self.addJingle(jingle) 407 jingle.addContent('voice', JingleVoiP()) 408 jingle.startSession()
