diff src/plugins/plugin_sec_otr.py @ 2144:1d3f73e065e1

core, jp: component handling + client handling refactoring: - SàT can now handle components - plugin have now a "modes" key in PLUGIN_INFO where they declare if they can be used with clients and or components. They default to be client only. - components are really similar to clients, but with some changes in behaviour: * component has "entry point", which is a special plugin with a componentStart method, which is called just after component is connected * trigger end with a different suffixes (e.g. profileConnected vs profileConnectedComponent), so a plugin which manage both clients and components can have different workflow * for clients, only triggers of plugins handling client mode are launched * for components, only triggers of plugins needed in dependencies are launched. They all must handle component mode. * component have a sendHistory attribute (False by default) which can be set to True to allow saving sent messages into history * for convenience, "client" is still used in method even if it can now be a component * a new "component" boolean attribute tells if we have a component or a client * components have to add themselve Message protocol * roster and presence protocols are not added for components * component default port is 5347 (which is Prosody's default port) - asyncCreateProfile has been renamed for profileCreate, both to follow new naming convention and to prepare the transition to fully asynchronous bridge - createProfile has a new "component" attribute. When used to create a component, it must be set to a component entry point - jp: added --component argument to profile/create - disconnect bridge method is now asynchronous, this way frontends can know when disconnection is finished - new PI_* constants for PLUGIN_INFO values (not used everywhere yet) - client/component connection workflow has been moved to their classes instead of being a host methods - host.messageSend is now client.sendMessage, and former client.sendMessage is now client.sendMessageData. - identities are now handled in client.identities list, so it can be updated dynamically by plugins (in the future, frontends should be able to update them too through bridge) - profileConnecting* profileConnected* profileDisconnected* and getHandler now all use client instead of profile
author Goffi <goffi@goffi.org>
date Sun, 12 Feb 2017 17:55:43 +0100
parents 6e509ee853a8
children 33c8c4973743
line wrap: on
line diff
--- a/src/plugins/plugin_sec_otr.py	Tue Feb 07 00:15:03 2017 +0100
+++ b/src/plugins/plugin_sec_otr.py	Sun Feb 12 17:55:43 2017 +0100
@@ -81,7 +81,7 @@
     def inject(self, msg_str, appdata=None):
         """Inject encrypted data in the stream
 
-        if appdata is not None, we are sending a message in sendMessageFinishTrigger
+        if appdata is not None, we are sending a message in sendMessageDataTrigger
         stanza will be injected directly if appdata is None, else we just update the element
         and follow normal workflow
         @param msg_str(str): encrypted message body
@@ -103,7 +103,7 @@
                          'extra': {},
                          'timestamp': time.time(),
                         }
-            self.host.generateMessageXML(mess_data)
+            client.generateMessageXML(mess_data)
             client.send(mess_data['xml'])
         else:
             message_elt = appdata[u'xml']
@@ -234,8 +234,8 @@
         self._p_hints = host.plugins[u'XEP-0334']
         self._p_carbons = host.plugins[u'XEP-0280']
         host.trigger.add("MessageReceived", self.MessageReceivedTrigger, priority=100000)
-        host.trigger.add("messageSend", self.messageSendTrigger, priority=100000)
-        host.trigger.add("sendMessageFinish", self._sendMessageFinishTrigger)
+        host.trigger.add("sendMessage", self.sendMessageTrigger, priority=100000)
+        host.trigger.add("sendMessageData", self._sendMessageDataTrigger)
         host.bridge.addMethod("skipOTR", ".plugin", in_sign='s', out_sign='', method=self._skipOTR)  # FIXME: must be removed, must be done on per-message basis
         host.bridge.addSignal("otrState", ".plugin", signature='sss')  # args: state, destinee_jid, profile
         host.importMenu((OTR_MENU, D_(u"Start/Refresh")), self._otrStartRefresh, security_limit=0, help_string=D_(u"Start or refresh an OTR session"), type_=C.MENU_SINGLE)
@@ -255,26 +255,24 @@
         self.skipped_profiles.add(profile)
 
     @defer.inlineCallbacks
-    def profileConnected(self, profile):
-        if profile in self.skipped_profiles:
+    def profileConnected(self, client):
+        if client.profile in self.skipped_profiles:
             return
-        client = self.host.getClient(profile)
         ctxMng = client._otr_context_manager = ContextManager(self.host, client)
-        client._otr_data = persistent.PersistentBinaryDict(NS_OTR, profile)
+        client._otr_data = persistent.PersistentBinaryDict(NS_OTR, client.profile)
         yield client._otr_data.load()
         encrypted_priv_key = client._otr_data.get(PRIVATE_KEY, None)
         if encrypted_priv_key is not None:
-            priv_key = yield self.host.memory.decryptValue(encrypted_priv_key, profile)
+            priv_key = yield self.host.memory.decryptValue(encrypted_priv_key, client.profile)
             ctxMng.account.privkey = potr.crypt.PK.parsePrivateKey(priv_key.decode('hex'))[0]
         else:
             ctxMng.account.privkey = None
         ctxMng.account.loadTrusts()
 
-    def profileDisconnected(self, profile):
-        if profile in self.skipped_profiles:
-            self.skipped_profiles.remove(profile)
+    def profileDisconnected(self, client):
+        if client.profile in self.skipped_profiles:
+            self.skipped_profiles.remove(client.profile)
             return
-        client = self.host.getClient(profile)
         for context in client._otr_context_manager.contexts.values():
             context.disconnect()
         del client._otr_context_manager
@@ -454,7 +452,7 @@
                     jid = from_jid.full()))
 
                 feedback=D_(u"WARNING: received unencrypted data in a supposedly encrypted context"),
-                client.feedback(from_jid.full(), feedback)
+                client.feedback(from_jid, feedback)
         except StopIteration:
             return data
         else:
@@ -505,7 +503,7 @@
             post_treat.addCallback(self._receivedTreatment, client)
         return True
 
-    def _sendMessageFinishTrigger(self, client, mess_data):
+    def _sendMessageDataTrigger(self, client, mess_data):
         if not 'OTR' in mess_data:
             return
         otrctx = mess_data['OTR']
@@ -532,9 +530,11 @@
         else:
             feedback = D_(u"Your message was not sent because your correspondent closed the encrypted conversation on his/her side. "
                           u"Either close your own side, or refresh the session.")
-            client.feedback(to_jid.full(), feedback)
+            log.warning(_(u"Message discarded because closed encryption channel"))
+            client.feedback(to_jid, feedback)
+            raise failure.Failure(exceptions.CancelError(u'Cancelled by OTR plugin'))
 
-    def messageSendTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
+    def sendMessageTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
         if mess_data['type'] == 'groupchat':
             return True
         if client.profile in self.skipped_profiles:  # FIXME: should not be done on a per-profile basis
@@ -546,7 +546,7 @@
         if otrctx.state != potr.context.STATE_PLAINTEXT:
             self._p_hints.addHint(mess_data, self._p_hints.HINT_NO_COPY)
             self._p_hints.addHint(mess_data, self._p_hints.HINT_NO_PERMANENT_STORE)
-            mess_data['OTR'] = otrctx  # this indicate that encryption is needed in sendMessageFinish trigger
+            mess_data['OTR'] = otrctx  # this indicate that encryption is needed in sendMessageData trigger
             if not mess_data['to'].resource:  # if not resource was given, we force it here
                 mess_data['to'] = to_jid
         return True