comparison src/browser/sat_browser/plugin_sec_otr.py @ 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 849ffb24d5bf
comparison
equal deleted inserted replaced
664:8449a5db0602 665:6a8a1103ad10
357 if start: 357 if start:
358 return self.startContext(other_jid) 358 return self.startContext(other_jid)
359 else: 359 else:
360 return self.contexts.get(other_jid, None) 360 return self.contexts.get(other_jid, None)
361 361
362 def getContextsForBareUser(self, bare_jid):
363 """Get all the contexts for the users sharing the given bare JID.
364
365 @param bare_jid (jid.JID): bare JID
366 @return: list[Context]
367 """
368 return [context for other_jid, context in self.contexts.iteritems() if other_jid.bare == bare_jid]
369
362 def fixResource(self, other_jid): 370 def fixResource(self, other_jid):
363 """Return the full JID in case the resource of the given JID is missing. 371 """Return the full JID in case the resource of the given JID is missing.
364 372
365 @param other_jid (jid.JID): JID to check 373 @param other_jid (jid.JID): JID to check
366 @return jid.JID 374 @return jid.JID
381 self.context_manager = None 389 self.context_manager = None
382 self.host.bridge._registerMethods(["skipOTR"]) 390 self.host.bridge._registerMethods(["skipOTR"])
383 self.host.trigger.add("newMessageTrigger", self.newMessageTg, priority=TriggerManager.MAX_PRIORITY) 391 self.host.trigger.add("newMessageTrigger", self.newMessageTg, priority=TriggerManager.MAX_PRIORITY)
384 self.host.trigger.add("sendMessageTrigger", self.sendMessageTg, priority=TriggerManager.MAX_PRIORITY) 392 self.host.trigger.add("sendMessageTrigger", self.sendMessageTg, priority=TriggerManager.MAX_PRIORITY)
385 393
394 # 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)
395 self._profilePluggedListener = self.profilePluggedListener
396 self._gotMenusListener = self.gotMenusListener
397 # FIXME: these listeners are never removed, can't be removed by themselves (it modifies the list while looping), maybe need a 'one_shot' argument
398 self.host.addListener('profilePlugged', self._profilePluggedListener)
399 self.host.addListener('gotMenus', self._gotMenusListener)
400
386 @classmethod 401 @classmethod
387 def getInfoText(self, state=otr.context.STATE_PLAINTEXT, trust=''): 402 def getInfoText(self, state=otr.context.STATE_PLAINTEXT, trust=''):
388 """Get the widget info text for a certain message state and trust. 403 """Get the widget info text for a certain message state and trust.
389 404
390 @param state (unicode): message state 405 @param state (unicode): message state
404 if otrctx is None: 419 if otrctx is None:
405 return OTR.getInfoText() 420 return OTR.getInfoText()
406 else: 421 else:
407 return OTR.getInfoText(otrctx.state, otrctx.getCurrentTrust()) 422 return OTR.getInfoText(otrctx.state, otrctx.getCurrentTrust())
408 423
409 def inhibitMenus(self): 424 def gotMenusListener(self, menus, profile):
410 """Tell the caller which dynamic menus should be inhibited""" 425 menus_to_delete = []
411 return ["OTR"] # menu categories name to inhibit 426 for menu in menus:
412 427 id_, type_, path, path_i18n = menu
413 def extraMenus(self): 428 if path[0] == 'OTR':
429 menus_to_delete.append(menu)
430 for menu in menus_to_delete:
431 menus.remove(menu)
414 # FIXME: handle help strings too 432 # FIXME: handle help strings too
415 return [(self._startRefresh, C.MENU_SINGLE, (MAIN_MENU, "Start / refresh"), (MAIN_MENU, D_("Start / refresh"))), 433 menus.extend([(self._startRefresh, C.MENU_SINGLE, (MAIN_MENU, "Start / refresh"), (MAIN_MENU, D_("Start / refresh"))),
416 (self._endSession, C.MENU_SINGLE, (MAIN_MENU, "Stop encryption"), (MAIN_MENU, D_("Stop encryption"))), 434 (self._endSession, C.MENU_SINGLE, (MAIN_MENU, "Stop encryption"), (MAIN_MENU, D_("Stop encryption"))),
417 (self._authenticate, C.MENU_SINGLE, (MAIN_MENU, "Authenticate correspondent"), (MAIN_MENU, D_("Authenticate correspondent"))), 435 (self._authenticate, C.MENU_SINGLE, (MAIN_MENU, "Authenticate correspondent"), (MAIN_MENU, D_("Authenticate correspondent"))),
418 (self._dropPrivkey, C.MENU_SINGLE, (MAIN_MENU, "Drop your private key"), (MAIN_MENU, D_("Drop your private key")))] 436 (self._dropPrivkey, C.MENU_SINGLE, (MAIN_MENU, "Drop your private key"), (MAIN_MENU, D_("Drop your private key"))),
419 437 ])
420 def profileConnected(self): 438
439 def profilePluggedListener(self, profile):
440 # 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)
441 self._presenceListener = self.presenceListener
442 self._disconnectListener = self.disconnectListener
443 self.host.addListener('presence', self._presenceListener, [C.PROF_KEY_NONE])
444 # FIXME: this listener is never removed, can't be removed by itself (it modifies the list while looping), maybe need a 'one_shot' argument
445 self.host.addListener('disconnect', self._disconnectListener, [C.PROF_KEY_NONE])
446
421 self.host.bridge.call('skipOTR', None) 447 self.host.bridge.call('skipOTR', None)
422 self.context_manager = ContextManager(self.host) 448 self.context_manager = ContextManager(self.host)
423 # TODO: retrieve the encrypted private key from a HTML5 persistent storage, 449 # TODO: retrieve the encrypted private key from a HTML5 persistent storage,
424 # decrypt it, parse it with otr.crypt.PK.parsePrivateKey(privkey) and 450 # decrypt it, parse it with otr.crypt.PK.parsePrivateKey(privkey) and
425 # assign it to self.context_manager.account.privkey 451 # assign it to self.context_manager.account.privkey
426 452
427 # 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) 453 def disconnectListener(self, profile):
428 self.presenceListener = self.onPresenceUpdate 454 """Things to do just before the profile disconnection"""
429 self.host.addListener('presence', self.presenceListener, [C.PROF_KEY_NONE]) 455 self.host.removeListener('presence', self._presenceListener)
430 456
431 def profileDisconnected(self):
432 for context in self.context_manager.contexts.values(): 457 for context in self.context_manager.contexts.values():
433 context.disconnect() 458 context.disconnect() # FIXME: no time to send the message before the profile has been disconnected
434 self.host.removeListener('presence', self.presenceListener) 459
460 def presenceListener(self, entity, show, priority, statuses, profile):
461 if show == C.PRESENCE_UNAVAILABLE:
462 self.endSession(entity, disconnect=False)
435 463
436 def newMessageTg(self, from_jid, msg, msg_type, to_jid, extra, profile): 464 def newMessageTg(self, from_jid, msg, msg_type, to_jid, extra, profile):
437 if msg_type != C.MESS_TYPE_CHAT: 465 if msg_type != C.MESS_TYPE_CHAT:
438 return True 466 return True
439 467
475 return False # interrupt the main process 503 return False # interrupt the main process
476 504
477 log.debug(u"sending message unencrypted") 505 log.debug(u"sending message unencrypted")
478 return True 506 return True
479 507
480 def onPresenceUpdate(self, entity, show, priority, statuses, profile): 508 def endSession(self, other_jid, disconnect=True):
481 if show == C.PRESENCE_UNAVAILABLE:
482 self.endSession(entity, finish=True)
483
484 def endSession(self, other_jid, finish=False):
485 """Finish or disconnect an OTR session 509 """Finish or disconnect an OTR session
486 510
487 @param other_jid (jid.JID): other JID 511 @param other_jid (jid.JID): other JID
488 @param finish: if True, finish the session but do not disconnect it 512 @param disconnect (bool): if False, finish the session but do not disconnect it
489 @return: True if the session has been finished or disconnected, False if there was nothing to do
490 """ 513 """
491 # checking for private key existence is not needed, context checking is enough 514 # checking for private key existence is not needed, context checking is enough
492 otrctx = self.context_manager.getContextForUser(other_jid, start=False) 515 if other_jid.resource:
493 if otrctx is None or otrctx.state == otr.context.STATE_PLAINTEXT: 516 contexts = [self.context_manager.getContextForUser(other_jid, start=False)]
494 if not finish: 517 else: # contact disconnected itself so we need to terminate the OTR session but the Chat panel lost its resource
495 self.host.newMessageHandler(unicode(other_jid), END_PLAIN_HAS_NOT.format(jid=other_jid), C.MESS_TYPE_INFO, unicode(self.host.whoami), {}) 518 contexts = self.context_manager.getContextsForBareUser(other_jid)
496 return 519 for otrctx in contexts:
497 if finish: 520 if otrctx is None or otrctx.state == otr.context.STATE_PLAINTEXT:
498 otrctx.finish() 521 if disconnect:
499 else: 522 self.host.newMessageHandler(unicode(other_jid), END_PLAIN_HAS_NOT.format(jid=other_jid), C.MESS_TYPE_INFO, unicode(self.host.whoami), {})
500 otrctx.disconnect() 523 return
524 if disconnect:
525 otrctx.disconnect()
526 else:
527 otrctx.finish()
501 528
502 # Menu callbacks 529 # Menu callbacks
503 530
504 def _startRefresh(self, menu_data): 531 def _startRefresh(self, menu_data):
505 """Start or refresh an OTR session 532 """Start or refresh an OTR session