changeset 665:6a8a1103ad10 frontends_multi_profiles

browser_side: OTR uses 'profilePlugged', 'disconnect' and 'gotMenus' listeners
author souliane <souliane@mailoo.org>
date Tue, 03 Mar 2015 22:31:54 +0100
parents 8449a5db0602
children 1bf645e73fe8
files src/browser/libervia_main.py src/browser/sat_browser/menu.py src/browser/sat_browser/plugin_sec_otr.py
diffstat 3 files changed, 65 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/src/browser/libervia_main.py	Tue Mar 03 19:00:21 2015 +0100
+++ b/src/browser/libervia_main.py	Tue Mar 03 22:31:54 2015 +0100
@@ -138,18 +138,10 @@
 
     def importPlugins(self):
         self.plugins = {}
-        inhibited_menus = []
-        # FIXME: plugins import should be dynamic and generic like in sat
         try:
             self.plugins['otr'] = plugin_sec_otr.OTR(self)
         except TypeError:  # plugin_sec_otr has not been imported
-            inhibited_menus.append('OTR')
-
-        class DummyPlugin(object):
-            def inhibitMenus(self):
-                return inhibited_menus
-
-        self.plugins['dummy_plugin'] = DummyPlugin()
+            pass
 
     # def addSelectedListener(self, callback):
     #     self._selected_listeners.add(callback)
@@ -262,24 +254,10 @@
 
         @param menus (list[tuple]): menu data
         """
-        def process(menus, inhibited=None):
-            for raw_menu in menus:
-                id_, type_, path, path_i18n = raw_menu
-                if inhibited and path[0] in inhibited:
-                    continue
-                menus_data = self.menus.setdefault(type_, [])
-                menus_data.append((id_, path, path_i18n))
-
+        self.callListeners('gotMenus', menus)  # FIXME: to be done another way or moved to quick_app
         self.menus = {}
-        inhibited = set() # FIXME
-        extras = []
-        for plugin in self.plugins.values():
-            if hasattr(plugin, "inhibitMenus"):
-                inhibited.update(plugin.inhibitMenus())
-            if hasattr(plugin, "extraMenus"):
-                extras.extend(plugin.extraMenus())
-        process(menus, inhibited)
-        process(extras)
+        for id_, type_, path, path_i18n in menus:
+            self.menus.setdefault(type_, []).append((id_, path, path_i18n))
         self.panel.menu.createMenus()
 
     def _isRegisteredCB(self, result):
@@ -342,7 +320,7 @@
         # for cat, name in C.CACHED_PARAMS:
         #     self.bridge.call('asyncGetParamA', param_cb(cat, name, count), name, cat)
 
-    def profilePlugged(self, dummy):
+    def profilePlugged(self, dummy):  # FIXME: to be called as a "profilePlugged" listener?
         QuickApp.profilePlugged(self, dummy)
         # we fill the panels already here
         for wid in self.widgets.getWidgets(blog.MicroblogPanel):
@@ -354,11 +332,6 @@
         #we ask for our own microblogs:
         self.bridge.getMassiveLastMblogs('JID', (unicode(self.whoami.bare),), 10, profile=C.PROF_KEY_NONE, callback=self._ownBlogsFills)
 
-        # initialize plugins which waited for the connection to be done
-        for plugin in self.plugins.values():
-            if hasattr(plugin, 'profileConnected'):
-                plugin.profileConnected()
-
     def addContactList(self, dummy):
         contact_list = ContactList(self)
         self.panel.addContactList(contact_list)
--- a/src/browser/sat_browser/menu.py	Tue Mar 03 19:00:21 2015 +0100
+++ b/src/browser/sat_browser/menu.py	Tue Mar 03 22:31:54 2015 +0100
@@ -88,13 +88,7 @@
     def onDisconnect(self):
         def confirm_cb(answer):
             if answer:
-                # FIXME: are we sure the triggers finished their jobs when the backend disconnect?
-                # FIXME: disconnection on timeout is not handled yet...
-                for plugin in self.host.plugins.values():
-                    if hasattr(plugin, 'profileDisconnected'):
-                        plugin.profileDisconnected()
-                log.info("disconnection")
-                self.host.bridge.call('disconnect', None)
+                self.host.disconnect(C.PROF_KEY_NONE)
         _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to disconnect ?")
         _dialog.show()
 
--- a/src/browser/sat_browser/plugin_sec_otr.py	Tue Mar 03 19:00:21 2015 +0100
+++ b/src/browser/sat_browser/plugin_sec_otr.py	Tue Mar 03 22:31:54 2015 +0100
@@ -359,6 +359,14 @@
         else:
             return self.contexts.get(other_jid, None)
 
+    def getContextsForBareUser(self, bare_jid):
+        """Get all the contexts for the users sharing the given bare JID.
+
+        @param bare_jid (jid.JID): bare JID
+        @return: list[Context]
+        """
+        return [context for other_jid, context in self.contexts.iteritems() if other_jid.bare == bare_jid]
+
     def fixResource(self, other_jid):
         """Return the full JID in case the resource of the given JID is missing.
 
@@ -383,6 +391,13 @@
         self.host.trigger.add("newMessageTrigger", self.newMessageTg, priority=TriggerManager.MAX_PRIORITY)
         self.host.trigger.add("sendMessageTrigger", self.sendMessageTg, priority=TriggerManager.MAX_PRIORITY)
 
+        # FIXME: workaround for a pyjamas issue: calling hash on a class method always return a different value if that method is defined directly within the class (with the "def" keyword)
+        self._profilePluggedListener = self.profilePluggedListener
+        self._gotMenusListener = self.gotMenusListener
+        # FIXME: these listeners are never removed, can't be removed by themselves (it modifies the list while looping), maybe need a 'one_shot' argument
+        self.host.addListener('profilePlugged', self._profilePluggedListener)
+        self.host.addListener('gotMenus', self._gotMenusListener)
+
     @classmethod
     def getInfoText(self, state=otr.context.STATE_PLAINTEXT, trust=''):
         """Get the widget info text for a certain message state and trust.
@@ -406,32 +421,45 @@
         else:
             return OTR.getInfoText(otrctx.state, otrctx.getCurrentTrust())
 
-    def inhibitMenus(self):
-        """Tell the caller which dynamic menus should be inhibited"""
-        return ["OTR"]  # menu categories name to inhibit
-
-    def extraMenus(self):
+    def gotMenusListener(self, menus, profile):
+        menus_to_delete = []
+        for menu in menus:
+            id_, type_, path, path_i18n = menu
+            if path[0] == 'OTR':
+                menus_to_delete.append(menu)
+        for menu in menus_to_delete:
+            menus.remove(menu)
         # FIXME: handle help strings too
-        return [(self._startRefresh, C.MENU_SINGLE, (MAIN_MENU, "Start / refresh"), (MAIN_MENU, D_("Start / refresh"))),
-                (self._endSession, C.MENU_SINGLE, (MAIN_MENU, "Stop encryption"), (MAIN_MENU, D_("Stop encryption"))),
-                (self._authenticate, C.MENU_SINGLE, (MAIN_MENU, "Authenticate correspondent"), (MAIN_MENU, D_("Authenticate correspondent"))),
-                (self._dropPrivkey, C.MENU_SINGLE, (MAIN_MENU, "Drop your private key"), (MAIN_MENU, D_("Drop your private key")))]
+        menus.extend([(self._startRefresh, C.MENU_SINGLE, (MAIN_MENU, "Start / refresh"), (MAIN_MENU, D_("Start / refresh"))),
+                      (self._endSession, C.MENU_SINGLE, (MAIN_MENU, "Stop encryption"), (MAIN_MENU, D_("Stop encryption"))),
+                      (self._authenticate, C.MENU_SINGLE, (MAIN_MENU, "Authenticate correspondent"), (MAIN_MENU, D_("Authenticate correspondent"))),
+                      (self._dropPrivkey, C.MENU_SINGLE, (MAIN_MENU, "Drop your private key"), (MAIN_MENU, D_("Drop your private key"))),
+                      ])
 
-    def profileConnected(self):
+    def profilePluggedListener(self, profile):
+        # FIXME: workaround for a pyjamas issue: calling hash on a class method always return a different value if that method is defined directly within the class (with the "def" keyword)
+        self._presenceListener = self.presenceListener
+        self._disconnectListener = self.disconnectListener
+        self.host.addListener('presence', self._presenceListener, [C.PROF_KEY_NONE])
+        # FIXME: this listener is never removed, can't be removed by itself (it modifies the list while looping), maybe need a 'one_shot' argument
+        self.host.addListener('disconnect', self._disconnectListener, [C.PROF_KEY_NONE])
+
         self.host.bridge.call('skipOTR', None)
         self.context_manager = ContextManager(self.host)
         # TODO: retrieve the encrypted private key from a HTML5 persistent storage,
         # decrypt it, parse it with otr.crypt.PK.parsePrivateKey(privkey) and
         # assign it to self.context_manager.account.privkey
 
-        # FIXME: workaround for a pyjamas issue: calling hash on a class method always return a different value if that method is defined directly within the class (with the "def" keyword)
-        self.presenceListener = self.onPresenceUpdate
-        self.host.addListener('presence', self.presenceListener, [C.PROF_KEY_NONE])
+    def disconnectListener(self, profile):
+        """Things to do just before the profile disconnection"""
+        self.host.removeListener('presence', self._presenceListener)
 
-    def profileDisconnected(self):
         for context in self.context_manager.contexts.values():
-            context.disconnect()
-        self.host.removeListener('presence', self.presenceListener)
+            context.disconnect()  # FIXME: no time to send the message before the profile has been disconnected
+
+    def presenceListener(self, entity, show, priority, statuses, profile):
+        if show == C.PRESENCE_UNAVAILABLE:
+            self.endSession(entity, disconnect=False)
 
     def newMessageTg(self, from_jid, msg, msg_type, to_jid, extra, profile):
         if msg_type != C.MESS_TYPE_CHAT:
@@ -477,27 +505,26 @@
         log.debug(u"sending message unencrypted")
         return True
 
-    def onPresenceUpdate(self, entity, show, priority, statuses, profile):
-        if show == C.PRESENCE_UNAVAILABLE:
-            self.endSession(entity, finish=True)
-
-    def endSession(self, other_jid, finish=False):
+    def endSession(self, other_jid, disconnect=True):
         """Finish or disconnect an OTR session
 
         @param other_jid (jid.JID): other JID
-        @param finish: if True, finish the session but do not disconnect it
-        @return: True if the session has been finished or disconnected, False if there was nothing to do
+        @param disconnect (bool): if False, finish the session but do not disconnect it
         """
         # checking for private key existence is not needed, context checking is enough
-        otrctx = self.context_manager.getContextForUser(other_jid, start=False)
-        if otrctx is None or otrctx.state == otr.context.STATE_PLAINTEXT:
-            if not finish:
-                self.host.newMessageHandler(unicode(other_jid), END_PLAIN_HAS_NOT.format(jid=other_jid), C.MESS_TYPE_INFO, unicode(self.host.whoami), {})
-            return
-        if finish:
-            otrctx.finish()
-        else:
-            otrctx.disconnect()
+        if other_jid.resource:
+            contexts = [self.context_manager.getContextForUser(other_jid, start=False)]
+        else:  # contact disconnected itself so we need to terminate the OTR session but the Chat panel lost its resource
+            contexts = self.context_manager.getContextsForBareUser(other_jid)
+        for otrctx in contexts:
+            if otrctx is None or otrctx.state == otr.context.STATE_PLAINTEXT:
+                if disconnect:
+                    self.host.newMessageHandler(unicode(other_jid), END_PLAIN_HAS_NOT.format(jid=other_jid), C.MESS_TYPE_INFO, unicode(self.host.whoami), {})
+                return
+            if disconnect:
+                otrctx.disconnect()
+            else:
+                otrctx.finish()
 
     # Menu callbacks