Changeset 8461
- Timestamp:
- 08/07/07 01:19:57 (17 months ago)
- Location:
- branches/jingle
- Files:
-
- 1 added
- 5 modified
-
data/glade/chat_control_popup_menu.glade (modified) (1 diff)
-
src/chat_control.py (modified) (3 diffs)
-
src/common/connection_handlers.py (modified) (4 diffs)
-
src/common/jingle.py (modified) (15 diffs)
-
src/common/meta.py (added)
-
src/common/xmpp/protocol.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
branches/jingle/data/glade/chat_control_popup_menu.glade
r8314 r8461 67 67 68 68 <child> 69 <widget class="GtkMenuItem" id="start_voip_menuitem"> 70 <property name="visible">True</property> 71 <property name="label" translatable="yes">Start _Voice chat</property> 72 <property name="use_underline">True</property> 73 <signal name="activate" handler="_on_start_voip_menuitem_activate" last_modification_time="Tue, 03 Jan 2006 04:26:46 GMT"/> 74 </widget> 75 </child> 76 77 <child> 69 78 <widget class="GtkImageMenuItem" id="add_to_roster_menuitem"> 70 79 <property name="visible">True</property> -
branches/jingle/src/chat_control.py
r8454 r8461 1192 1192 widget.get_active()) 1193 1193 1194 def _on_start_voip_menuitem_activate(self, *things): 1195 print 'Start VoiP' 1196 gajim.connections[self.account].startVoiP(self.contact.jid) 1197 1194 1198 def _update_gpg(self): 1195 1199 tb = self.xml.get_widget('gpg_togglebutton') … … 1534 1538 history_menuitem = xml.get_widget('history_menuitem') 1535 1539 toggle_gpg_menuitem = xml.get_widget('toggle_gpg_menuitem') 1540 start_voip_menuitem = xml.get_widget('start_voip_menuitem') 1536 1541 add_to_roster_menuitem = xml.get_widget('add_to_roster_menuitem') 1537 1542 send_file_menuitem = xml.get_widget('send_file_menuitem') … … 1584 1589 self._on_toggle_gpg_menuitem_activate) 1585 1590 self.handlers[id] = toggle_gpg_menuitem 1591 id = start_voip_menuitem.connect('activate', 1592 self._on_start_voip_menuitem_activate) 1593 self.handlers[id] = start_voip_menuitem 1586 1594 id = information_menuitem.connect('activate', 1587 1595 self._on_contact_information_menuitem_activate) -
branches/jingle/src/common/connection_handlers.py
r8453 r8461 39 39 from common.pubsub import ConnectionPubSub 40 40 from common.caps import ConnectionCaps 41 from common.jingle import ConnectionJingle 41 42 42 43 STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd', … … 1175 1176 self.dispatch('VCARD', vcard) 1176 1177 1177 class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionCaps ):1178 class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionCaps, ConnectionJingle): 1178 1179 def __init__(self): 1179 1180 ConnectionVcard.__init__(self) … … 1181 1182 ConnectionCommands.__init__(self) 1182 1183 ConnectionPubSub.__init__(self) 1184 ConnectionJingle.__init__(self) 1183 1185 self.gmail_url=None 1184 1186 # List of IDs we are waiting answers for {id: (type_of_request, data), } … … 2100 2102 common.xmpp.NS_SEARCH) 2101 2103 con.RegisterHandler('iq', self._PubSubCB, 'result') 2104 con.RegisterHandler('iq', self._JingleCB, 'result') 2105 con.RegisterHandler('iq', self._JingleCB, 'error') 2106 con.RegisterHandler('iq', self._JingleCB, 'set', 2107 common.xmpp.NS_JINGLE) 2102 2108 con.RegisterHandler('iq', self._ErrorCB, 'error') 2103 2109 con.RegisterHandler('iq', self._IqCB) -
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() -
branches/jingle/src/common/xmpp/protocol.py
r8410 r8461 56 56 NS_INVISIBLE ='presence-invisible' # Jabberd2 57 57 NS_IQ ='iq' # Jabberd2 58 NS_JINGLE ='http://www.xmpp.org/extensions/xep-0166.html#ns' # XEP-0166 59 NS_JINGLE_AUDIO ='http://www.xmpp.org/extensions/xep-0167.html#ns' # XEP-0167 60 NS_JINGLE_RAW_UDP='http://www.xmpp.org/extensions/xep-0177.html#ns' # XEP-0177 61 NS_JINGLE_ICE_UDP='http://www.xmpp.org/extensions/xep-0176.html#ns-udp' # XEP-0176 58 62 NS_LAST ='jabber:iq:last' 59 63 NS_MESSAGE ='message' # Jabberd2
