changeset 1b61572222a0 in /home/hg/repos/gajim
details:http://hg.gajim.org/gajim?cmd=changeset;node=1b61572222a0 description: support for content-{add,reject,accept}, new helpers, and other few things
diffstat:
src/chat_control.py | 18 +++- src/common/jingle.py | 223 ++++++++++++++++++++++++++++++++++++++----------------- src/dialogs.py | 18 ++++- src/gajim.py | 16 +++- 4 files changed, 195 insertions(+), 80 deletions(-)
diffs (truncated from 602 to 300 lines):
diff -r cbd3eacacb0c -r 1b61572222a0 src/chat_control.py --- a/src/chat_control.py Fri Sep 25 15:47:43 2009 +0200 +++ b/src/chat_control.py Fri Sep 25 19:32:13 2009 +0200 @@ -1579,14 +1579,17 @@ elif state == 'connecting': self.audio_state = self.JINGLE_STATE_CONNECTING self.audio_sid = sid + self._audio_button.set_active(True) elif state == 'connection_received': self.audio_state = self.JINGLE_STATE_CONNECTION_RECEIVED self.audio_sid = sid + self._audio_button.set_active(True) elif state == 'connected': self.audio_state = self.JINGLE_STATE_CONNECTED elif state == 'stop': self.audio_state = self.JINGLE_STATE_AVAILABLE self.audio_sid = None + self._audio_button.set_active(False) elif state == 'error': self.audio_state = self.JINGLE_STATE_ERROR self.update_audio() @@ -1605,15 +1608,18 @@ self.video_sid = None elif state == 'connecting': self.video_state = self.JINGLE_STATE_CONNECTING + self._video_button.set_active(True) self.video_sid = sid elif state == 'connection_received': self.video_state = self.JINGLE_STATE_CONNECTION_RECEIVED + self._video_button.set_active(True) self.video_sid = sid elif state == 'connected': self.video_state = self.JINGLE_STATE_CONNECTED elif state == 'stop': self.video_state = self.JINGLE_STATE_AVAILABLE self.video_sid = None + self._video_button.set_active(False) elif state == 'error': self.video_state = self.JINGLE_STATE_ERROR self.update_video() @@ -1825,10 +1831,11 @@ self.set_audio_state('connecting', sid) else: session = gajim.connections[self.account].get_jingle_session( - self.contact.get_full_jid(), self.audio_sid) + self.contact.get_full_jid(), self.video_sid) if session: - # TODO: end only audio - session.end_session() + content = session.get_content('audio') + if content: + session.remove_content(content.creator, content.name)
def on_video_button_toggled(self, widget): if widget.get_active(): @@ -1839,8 +1846,9 @@ session = gajim.connections[self.account].get_jingle_session( self.contact.get_full_jid(), self.video_sid) if session: - # TODO: end only video - session.end_session() + content = session.get_content('video') + if content: + session.remove_content(content.creator, content.name)
def _toggle_gpg(self): if not self.gpg_is_active and not self.contact.keyID: diff -r cbd3eacacb0c -r 1b61572222a0 src/common/jingle.py --- a/src/common/jingle.py Fri Sep 25 15:47:43 2009 +0200 +++ b/src/common/jingle.py Fri Sep 25 19:32:13 2009 +0200 @@ -17,7 +17,10 @@ # - 'senders' attribute of 'content' element # - security preconditions # * actions: -# - content-accept, content-reject, content-add, content-modify +# - content-accept: see content-add +# - content-reject: sending it ; receiving is ok +# - content-add: handling ; sending is ok +# - content-modify: both # - description-info, session-info # - security-info # - transport-accept, transport-reject @@ -88,7 +91,7 @@ # our full jid self.ourjid = gajim.get_jid_from_account(self.connection.name) + '/' + \ con.server_resource - self.peerjid = jid # jid we connect to + self.peerjid = str(jid) # jid we connect to # jid we use as the initiator self.initiator = weinitiate and self.ourjid or self.peerjid # jid we use as the responder @@ -107,10 +110,12 @@ # use .prepend() to add new callbacks, especially when you're going # to send error instead of ack self.callbacks = { - 'content-accept': [self.__contentAcceptCB, self.__defaultCB], - 'content-add': [self.__defaultCB], #TODO + 'content-accept': [self.__contentAcceptCB, self.__broadcastCB, + self.__defaultCB], + 'content-add': [self.__contentAddCB, self.__broadcastCB, + self.__defaultCB], #TODO 'content-modify': [self.__defaultCB], #TODO - 'content-reject': [self.__defaultCB], #TODO + 'content-reject': [self.__defaultCB, self.__contentRemoveCB], #TODO 'content-remove': [self.__defaultCB, self.__contentRemoveCB], 'description-info': [self.__defaultCB], #TODO 'security-info': [self.__defaultCB], #TODO @@ -133,7 +138,6 @@ def approve_session(self): ''' Called when user accepts session in UI (when we aren't the initiator). ''' - self.accepted = True self.accept_session()
def decline_session(self): @@ -154,10 +158,23 @@
''' Middle-level functions to manage contents. Handle local content cache and send change notifications. ''' + def get_content(self, media=None): + if media == 'audio': + cls = JingleVoIP + elif media == 'video': + cls = JingleVideo + #elif media == None: + # cls = JingleContent + else: + return None + + for content in self.contents.values(): + if isinstance(content, cls): + return content + def add_content(self, name, content, creator='we'): ''' Add new content to session. If the session is active, this will send proper stanza to update session. - The protocol prohibits changing that when pending. Creator must be one of ('we', 'peer', 'initiator', 'responder')''' assert creator in ('we', 'peer', 'initiator', 'responder')
@@ -171,34 +188,53 @@ content.name = name self.contents[(creator, name)] = content
- if self.state == JingleStates.active: - pass # TODO: send proper stanza, shouldn't be needed now - def remove_content(self, creator, name): ''' We do not need this now ''' - pass + #TODO: + if (creator, name) in self.contents: + content = self.contents[(creator, name)] + if len(self.contents) > 1: + self.__content_remove(content) + self.contents[(creator, name)].destroy() + if len(self.contents) == 0: + self.end_session()
def modify_content(self, creator, name, *someother): ''' We do not need this now ''' pass
- def accept_session(self): - ''' Check if all contents and user agreed to start session. ''' - if not self.weinitiate and self.accepted and \ - self.state == JingleStates.pending and self.is_ready(): - self.__session_accept() + def on_session_state_changed(self, content=None): + if self.state == JingleStates.active and self.accepted: + if not content: + return + if (content.creator == 'initiator') == self.weinitiate: + # We initiated this content. It's a pending content-add. + self.__content_add(content) + else: + # The other side created this content, we accept it. + self.__content_accept(content) + elif self.is_ready(): + if not self.weinitiate and self.state == JingleStates.pending: + self.__session_accept() + elif self.weinitiate and self.state == JingleStates.ended: + self.__session_initiate()
def is_ready(self): ''' Returns True when all codecs and candidates are ready (for all contents). ''' - return all((content.is_ready() for content in self.contents.itervalues())) + return (all((content.is_ready() for content in self.contents.itervalues())) + and self.accepted)
''' Middle-level function to do stanza exchange. ''' + def accept_session(self): + ''' Mark the session as accepted. ''' + self.accepted = True + self.on_session_state_changed() + def start_session(self): - ''' Start session. ''' - #FIXME: Start only once - if self.weinitiate and self.state == JingleStates.ended and self.is_ready(): - self.__session_initiate() + ''' Mark the session as ready to be started. ''' + self.accepted = True + self.on_session_state_changed()
def send_session_info(self): pass @@ -309,10 +345,10 @@ creator = content['creator'] name = content['name'] if (creator, name) in self.contents: - del self.contents[(creator, name)] + self.contents[(creator, name)].destroy() if len(self.contents) == 0: reason = xmpp.Node('reason') - reason.setTag('success') #FIXME: Is it the good one? + reason.setTag('success') self._session_terminate(reason)
def __sessionAcceptCB(self, stanza, jingle, error, action): @@ -328,6 +364,27 @@ creator = content['creator'] name = content['name']#TODO...
+ def __contentAddCB(self, stanza, jingle, error, action): + #TODO: Needs to be rewritten + if self.state == JingleStates.ended: + raise OutOfOrder + for element in jingle.iterTags('content'): + # checking what kind of session this will be + desc_ns = element.getTag('description').getNamespace() + media = element.getTag('description')['media'] + tran_ns = element.getTag('transport').getNamespace() + if desc_ns == xmpp.NS_JINGLE_RTP and media in ('audio', 'video') \ + and tran_ns == xmpp.NS_JINGLE_ICE_UDP: + if media == 'audio': + self.add_content(element['name'], JingleVoIP(self), 'peer') + else: + self.add_content(element['name'], JingleVideo(self), 'peer') + else: + content = JingleContent() + self.add_content(element['name'], content, 'peer') + self.__content_reject(content) + self.contents[(content.creator, content.name)].destroy() + def __sessionInitiateCB(self, stanza, jingle, error, action): ''' We got a jingle session request from other entity, therefore we are the receiver... Unpack the data, @@ -511,20 +568,40 @@ text = '%s (%s)' % (reason, text) else: text = reason + self.connection.delete_jingle(self) self.connection.dispatch('JINGLE_DISCONNECTED', (self.peerjid, self.sid, text)) - self.connection.delete_jingle(self)
- def __content_add(self): - assert self.state == JingleStates.active + def __content_add(self, content): + #TODO: test + assert self.state != JingleStates.ended + stanza, jingle = self.__make_jingle('content-add') + self.__append_content(jingle, content) + self.__broadcastCB(stanza, jingle, None, 'content-add-sent') + self.connection.connection.send(stanza)
- def __content_accept(self): + def __content_accept(self, content): + #TODO: test assert self.state != JingleStates.ended + stanza, jingle = self.__make_jingle('content-accept') + self.__append_content(jingle, content) + self.__broadcastCB(stanza, jingle, None, 'content-accept-sent') + self.connection.connection.send(stanza) + + def __content_reject(self, content): + assert self.state != JingleStates.ended + stanza, jingle = self.__make_jingle('content-reject') + self.__append_content(jingle, content) + self.connection.connection.send(stanza)
def __content_modify(self): assert self.state != JingleStates.ended
- def __content_remove(self): + def __content_remove(self, content): assert self.state != JingleStates.ended + stanza, jingle = self.__make_jingle('content-remove') + self.__append_content(jingle, content) + self.connection.connection.send(stanza) + #TODO: dispatch something?
def content_negociated(self, media): self.connection.dispatch('JINGLE_CONNECTED', (self.peerjid, self.sid, @@ -554,8 +631,8 @@
self.callbacks = { # these are called when *we* get stanzas - 'content-accept': [],