# HG changeset patch # User souliane # Date 1425418314 -3600 # Node ID 6a8a1103ad1068f17a962e5a9ce117530da21903 # Parent 8449a5db06026c056e3f42d7516f6b2e6dfd3cb6 browser_side: OTR uses 'profilePlugged', 'disconnect' and 'gotMenus' listeners diff -r 8449a5db0602 -r 6a8a1103ad10 src/browser/libervia_main.py --- 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) diff -r 8449a5db0602 -r 6a8a1103ad10 src/browser/sat_browser/menu.py --- 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() diff -r 8449a5db0602 -r 6a8a1103ad10 src/browser/sat_browser/plugin_sec_otr.py --- 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