From fbf9a769d88629c314e71c2b44bd4f905ad1c126 Mon Sep 17 00:00:00 2001
From: Thibaut GIRKA <thib@sitedethib.com>
Date: Sun, 21 Mar 2010 21:45:45 +0100
Subject: [PATCH] Delete invalid jingle sessions. Kick audio/video availability
 from jingle states. Fixes #5668, #5651

---
 src/chat_control.py                           | 81 ++++++++-----------
 src/command_system/implementation/standard.py |  6 +-
 src/common/jingle.py                          |  5 +-
 src/common/jingle_session.py                  |  2 -
 src/dialogs.py                                |  9 +++
 src/gui_interface.py                          | 26 +++---
 6 files changed, 63 insertions(+), 66 deletions(-)

diff --git a/src/chat_control.py b/src/chat_control.py
index 10c128bc27..f8d7b0a9aa 100644
--- a/src/chat_control.py
+++ b/src/chat_control.py
@@ -1259,13 +1259,12 @@ class ChatControl(ChatControlBase):
     A control for standard 1-1 chat
     """
     (
-            JINGLE_STATE_NOT_AVAILABLE,
-            JINGLE_STATE_AVAILABLE,
+            JINGLE_STATE_NULL,
             JINGLE_STATE_CONNECTING,
             JINGLE_STATE_CONNECTION_RECEIVED,
             JINGLE_STATE_CONNECTED,
             JINGLE_STATE_ERROR
-    ) = range(6)
+    ) = range(5)
 
     TYPE_ID = message_control.TYPE_CHAT
     old_msg_kind = None # last kind of the printed message
@@ -1352,9 +1351,11 @@ class ChatControl(ChatControlBase):
         self._audio_banner_image = self.xml.get_object('audio_banner_image')
         self._video_banner_image = self.xml.get_object('video_banner_image')
         self.audio_sid = None
-        self.audio_state = self.JINGLE_STATE_NOT_AVAILABLE
+        self.audio_state = self.JINGLE_STATE_NULL
+        self.audio_available = False
         self.video_sid = None
-        self.video_state = self.JINGLE_STATE_NOT_AVAILABLE
+        self.video_state = self.JINGLE_STATE_NULL
+        self.video_available = False
 
         self.update_toolbar()
 
@@ -1480,34 +1481,19 @@ class ChatControl(ChatControlBase):
         # Jingle detection
         if self.contact.supports(NS_JINGLE_ICE_UDP) and \
         gajim.HAVE_FARSIGHT and self.contact.resource:
-            if self.contact.supports(NS_JINGLE_RTP_AUDIO):
-                if self.audio_state == self.JINGLE_STATE_NOT_AVAILABLE:
-                    self.set_audio_state('available')
-            else:
-                self.set_audio_state('not_available')
-
-            if self.contact.supports(NS_JINGLE_RTP_VIDEO):
-                if self.video_state == self.JINGLE_STATE_NOT_AVAILABLE:
-                    self.set_video_state('available')
-            else:
-                self.set_video_state('not_available')
+            self.audio_available = self.contact.supports(NS_JINGLE_RTP_AUDIO)
+            self.video_available = self.contact.supports(NS_JINGLE_RTP_VIDEO)
         else:
-            if self.audio_state != self.JINGLE_STATE_NOT_AVAILABLE:
-                self.set_audio_state('not_available')
-            if self.video_state != self.JINGLE_STATE_NOT_AVAILABLE:
-                self.set_video_state('not_available')
+            if self.video_available or self.audio_available:
+                self.stop_jingle()
+            self.video_available = False
+            self.audio_available = False
 
         # Audio buttons
-        if self.audio_state == self.JINGLE_STATE_NOT_AVAILABLE:
-            self._audio_button.set_sensitive(False)
-        else:
-            self._audio_button.set_sensitive(True)
+        self._audio_button.set_sensitive(self.audio_available)
 
         # Video buttons
-        if self.video_state == self.JINGLE_STATE_NOT_AVAILABLE:
-            self._video_button.set_sensitive(False)
-        else:
-            self._video_button.set_sensitive(True)
+        self._video_button.set_sensitive(self.video_available)
 
         # Send file
         if self.contact.supports(NS_FILE) and self.contact.resource:
@@ -1551,8 +1537,7 @@ class ChatControl(ChatControlBase):
             return
         banner_image = getattr(self, '_' + jingle_type + '_banner_image')
         state = getattr(self, jingle_type + '_state')
-        if state in (self.JINGLE_STATE_NOT_AVAILABLE,
-        self.JINGLE_STATE_AVAILABLE):
+        if state == self.JINGLE_STATE_NULL:
             banner_image.hide()
         else:
             banner_image.show()
@@ -1604,24 +1589,29 @@ class ChatControl(ChatControlBase):
         # update MessageWindow._controls
         self.parent_win.change_jid(self.account, old_full_jid, new_full_jid)
 
+    def stop_jingle(self, sid=None, reason=None):
+        if self.audio_sid and sid in (self.audio_sid, None):
+            self.close_jingle_content('audio')
+        if self.video_sid and sid in (self.video_sid, None):
+            self.close_jingle_content('video')
+
+
     def _set_jingle_state(self, jingle_type, state, sid=None, reason=None):
         if jingle_type not in ('audio', 'video'):
             return
-        if state in ('connecting', 'connected', 'stop') and reason:
+        if state in ('connecting', 'connected', 'stop', 'error') and reason:
             str = _('%(type)s state : %(state)s, reason: %(reason)s') % {
                     'type': jingle_type.capitalize(), 'state': state, 'reason': reason}
             self.print_conversation(str, 'info')
 
-        states = {'not_available': self.JINGLE_STATE_NOT_AVAILABLE,
-                'available': self.JINGLE_STATE_AVAILABLE,
-                'connecting': self.JINGLE_STATE_CONNECTING,
+        states = {'connecting': self.JINGLE_STATE_CONNECTING,
                 'connection_received': self.JINGLE_STATE_CONNECTION_RECEIVED,
                 'connected': self.JINGLE_STATE_CONNECTED,
-                'stop': self.JINGLE_STATE_AVAILABLE,
+                'stop': self.JINGLE_STATE_NULL,
                 'error': self.JINGLE_STATE_ERROR}
 
         jingle_state = states[state]
-        if getattr(self, jingle_type + '_state') == jingle_state:
+        if getattr(self, jingle_type + '_state') == jingle_state or state == 'error':
             return
 
         if state == 'stop' and getattr(self, jingle_type + '_sid') not in (None, sid):
@@ -1629,21 +1619,12 @@ class ChatControl(ChatControlBase):
 
         setattr(self, jingle_type + '_state', jingle_state)
 
-        # Destroy existing session with the user when he signs off
-        # We need to do that before modifying the sid
-        if state == 'not_available':
-            gajim.connections[self.account].delete_jingle_session(
-                    getattr(self, jingle_type + '_sid'))
-
-        if state in ('not_available', 'available', 'stop'):
+        if jingle_state == self.JINGLE_STATE_NULL:
             setattr(self, jingle_type + '_sid', None)
         if state in ('connection_received', 'connecting'):
             setattr(self, jingle_type + '_sid', sid)
 
-        if state in ('connecting', 'connected', 'connection_received'):
-            getattr(self, '_' + jingle_type + '_button').set_active(True)
-        elif state in ('not_available', 'stop'):
-            getattr(self, '_' + jingle_type + '_button').set_active(False)
+        getattr(self, '_' + jingle_type + '_button').set_active(jingle_state != self.JINGLE_STATE_NULL)
 
         getattr(self, 'update_' + jingle_type)()
 
@@ -1892,12 +1873,16 @@ class ChatControl(ChatControlBase):
         sid = getattr(self, jingle_type + '_sid')
         if not sid:
             return
+        setattr(self, jingle_type + '_sid', None)
+        setattr(self, jingle_type + '_state', self.JINGLE_STATE_NULL)
         session = gajim.connections[self.account].get_jingle_session(
                 self.contact.get_full_jid(), sid)
         if session:
             content = session.get_content(jingle_type)
             if content:
                 session.remove_content(content.creator, content.name)
+        getattr(self, '_' + jingle_type + '_button').set_active(False)
+        getattr(self, 'update_' + jingle_type)()
 
     def on_jingle_button_toggled(self, widget, jingle_type):
         img_name = 'gajim-%s_%s' % ({'audio': 'mic', 'video': 'cam'}[jingle_type],
@@ -1906,7 +1891,7 @@ class ChatControl(ChatControlBase):
 
         if widget.get_active():
             if getattr(self, jingle_type + '_state') == \
-            self.JINGLE_STATE_AVAILABLE:
+            self.JINGLE_STATE_NULL:
                 sid = getattr(gajim.connections[self.account],
                         'start_' + jingle_type)(self.contact.get_full_jid())
                 getattr(self, 'set_' + jingle_type + '_state')('connecting', sid)
diff --git a/src/command_system/implementation/standard.py b/src/command_system/implementation/standard.py
index 68e41a00f0..2b064610c1 100644
--- a/src/command_system/implementation/standard.py
+++ b/src/command_system/implementation/standard.py
@@ -195,8 +195,8 @@ class StandardCommonChatCommands(CommandContainer):
     @command
     @doc(_("Toggle audio session"))
     def audio(self):
-        if self.audio_state == self.JINGLE_STATE_NOT_AVAILABLE:
-            raise CommandError(_("Video sessions are not available"))
+        if not self.audio_available:
+            raise CommandError(_("Audio sessions are not available"))
         else:
             # A state of an audio session is toggled by inverting a state of the
             # appropriate button.
@@ -206,7 +206,7 @@ class StandardCommonChatCommands(CommandContainer):
     @command
     @doc(_("Toggle video session"))
     def video(self):
-        if self.video_state == self.JINGLE_STATE_NOT_AVAILABLE:
+        if not self.video_available:
             raise CommandError(_("Video sessions are not available"))
         else:
             # A state of a video session is toggled by inverting a state of the
diff --git a/src/common/jingle.py b/src/common/jingle.py
index edc9331566..da2a5d0319 100644
--- a/src/common/jingle.py
+++ b/src/common/jingle.py
@@ -33,7 +33,7 @@ Handles the jingle signalling protocol
 import xmpp
 import helpers
 
-from jingle_session import JingleSession
+from jingle_session import JingleSession, JingleStates
 from jingle_rtp import JingleAudio, JingleVideo
 
 
@@ -92,6 +92,9 @@ class ConnectionJingle(object):
 
         # we already have such session in dispatcher...
         self.__sessions[sid].on_stanza(stanza)
+        # Delete invalid/unneeded sessions
+        if sid in self.__sessions and self.__sessions[sid].state == JingleStates.ended:
+            self.delete_jingle_session(sid)
 
         raise xmpp.NodeProcessed
 
diff --git a/src/common/jingle_session.py b/src/common/jingle_session.py
index df13dcc017..f756b5b7e1 100644
--- a/src/common/jingle_session.py
+++ b/src/common/jingle_session.py
@@ -320,8 +320,6 @@ class JingleSession(object):
                 xmpp_error = child.getName()
         self.__dispatch_error(xmpp_error, jingle_error, text)
         # FIXME: Not sure when we would want to do that...
-        if xmpp_error == 'item-not-found':
-            self.connection.delete_jingle_session(self.sid)
 
     def __on_transport_replace(self, stanza, jingle, error, action):
         for content in jingle.iterTags('content'):
diff --git a/src/dialogs.py b/src/dialogs.py
index 7bcb308fab..5c1ece1e41 100644
--- a/src/dialogs.py
+++ b/src/dialogs.py
@@ -4907,6 +4907,15 @@ class VoIPCallReceivedDialog(object):
                 self.content_types.add(type_)
         self.set_secondary_text()
 
+    def remove_contents(self, content_types):
+        for type_ in content_types:
+            if type_ in self.content_types:
+                self.content_types.remove(type_)
+        if not self.content_types:
+            self.dialog.destroy()
+        else:
+            self.set_secondary_text()
+
     def on_voip_call_received_messagedialog_destroy(self, dialog):
         if (self.fjid, self.sid) in self.instances:
             del self.instances[(self.fjid, self.sid)]
diff --git a/src/gui_interface.py b/src/gui_interface.py
index 7035b69997..81aeaca79d 100644
--- a/src/gui_interface.py
+++ b/src/gui_interface.py
@@ -1810,9 +1810,8 @@ class Interface:
         if media in ('audio', 'video'):
             jid = gajim.get_jid_without_resource(peerjid)
             resource = gajim.get_resource_from_jid(peerjid)
-            ctrl = self.msg_win_mgr.get_control(peerjid, account)
-            if not ctrl:
-                ctrl = self.msg_win_mgr.get_control(jid, account)
+            ctrl = (self.msg_win_mgr.get_control(peerjid, account)
+                or self.msg_win_mgr.get_control(jid, account))
             if ctrl:
                 if media == 'audio':
                     ctrl.set_audio_state('connected', sid)
@@ -1824,26 +1823,29 @@ class Interface:
         peerjid, sid, media, reason = data
         jid = gajim.get_jid_without_resource(peerjid)
         resource = gajim.get_resource_from_jid(peerjid)
-        ctrl = self.msg_win_mgr.get_control(peerjid, account)
-        if not ctrl:
-            ctrl = self.msg_win_mgr.get_control(jid, account)
+        ctrl = (self.msg_win_mgr.get_control(peerjid, account)
+            or self.msg_win_mgr.get_control(jid, account))
         if ctrl:
-            if media in ('audio', None):
+            if media is None:
+                ctrl.stop_jingle(sid=sid, reason=reason)
+            elif media == 'audio':
                 ctrl.set_audio_state('stop', sid=sid, reason=reason)
-            if media in ('video', None):
+            elif media == 'video':
                 ctrl.set_video_state('stop', sid=sid, reason=reason)
         dialog = dialogs.VoIPCallReceivedDialog.get_dialog(peerjid, sid)
         if dialog:
-            dialog.dialog.destroy()
+            if media is None:
+                dialog.dialog.destroy()
+            else:
+                dialog.remove_contents((media, ))
 
     def handle_event_jingle_error(self, account, data):
         # ('JINGLE_ERROR', account, (peerjid, sid, reason))
         peerjid, sid, reason = data
         jid = gajim.get_jid_without_resource(peerjid)
         resource = gajim.get_resource_from_jid(peerjid)
-        ctrl = self.msg_win_mgr.get_control(peerjid, account)
-        if not ctrl:
-            ctrl = self.msg_win_mgr.get_control(jid, account)
+        ctrl = (self.msg_win_mgr.get_control(peerjid, account)
+            or self.msg_win_mgr.get_control(jid, account))
         if ctrl:
             ctrl.set_audio_state('error', reason=reason)
 
-- 
GitLab