changeset 2131:628c1c95f442

plugin XEP-0334: fixed and improved message processing hints: - wrong elements were used in this plugging, it could not work properly - use constants for hints - add addHint method so other plugins can add a hint easily - hints can be added by frontends in mess_data['extra']['hints'] (serialized dict) or by other plugins using addHint - if history is skipped on message reception, mess_data['extra']['history']['skipped'] is set - introduced PLUGIN_INFO['usage'] for usage information for developers
author Goffi <goffi@goffi.org>
date Sun, 05 Feb 2017 14:55:19 +0100
parents f0bc29dc8157
children c0577837680a
files src/core/sat_main.py src/plugins/plugin_xep_0334.py
diffstat 2 files changed, 62 insertions(+), 54 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/sat_main.py	Sat Feb 04 17:59:15 2017 +0100
+++ b/src/core/sat_main.py	Sun Feb 05 14:55:19 2017 +0100
@@ -672,7 +672,7 @@
         @param data: message data dictionnary
         @param client: profile's client
         """
-        client.send(data['xml'])
+        client.send(data[u'xml'])
         return data
 
     def messageAddToHistory(self, data, client):
@@ -681,10 +681,10 @@
         @param data: message data dictionnary
         @param client: profile's client
         """
-        if data["type"] != C.MESS_TYPE_GROUPCHAT:
+        if data[u"type"] != C.MESS_TYPE_GROUPCHAT:
             # we don't add groupchat message to history, as we get them back
             # and they will be added then
-            if data['message'] or data['subject']: # we need a message to store
+            if data[u'message'] or data[u'subject']: # we need a message to store
                 self.memory.addToHistory(client, data)
             else:
                log.warning(u"No message found") # empty body should be managed by plugins before this point
@@ -696,14 +696,14 @@
         @param data: message data dictionnary
         @param client: profile's client
         """
-        if data["type"] != C.MESS_TYPE_GROUPCHAT:
+        if data[u"type"] != C.MESS_TYPE_GROUPCHAT:
             # we don't send groupchat message to bridge, as we get them back
             # and they will be added the
-            if data['message'] or data['subject']: # we need a message to send something
+            if data[u'message'] or data[u'subject']: # we need a message to send something
                 # We send back the message, so all frontends are aware of it
-                self.bridge.messageNew(data['uid'], data['timestamp'], data['from'].full(), data['to'].full(), data['message'], data['subject'], data['type'], data['extra'], profile=client.profile)
+                self.bridge.messageNew(data[u'uid'], data[u'timestamp'], data[u'from'].full(), data[u'to'].full(), data[u'message'], data[u'subject'], data[u'type'], data[u'extra'], profile=client.profile)
             else:
-               log.warning(_("No message found"))
+               log.warning(_(u"No message found"))
         return data
 
     def _setPresence(self, to="", show="", statuses=None, profile_key=C.PROF_KEY_NONE):
--- a/src/plugins/plugin_xep_0334.py	Sat Feb 04 17:59:15 2017 +0100
+++ b/src/plugins/plugin_xep_0334.py	Sun Feb 05 14:55:19 2017 +0100
@@ -18,11 +18,12 @@
 # You should have received a copy of the GNU Affero General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from sat.core.i18n import _
+from sat.core.i18n import _, D_
 from sat.core.log import getLogger
 log = getLogger(__name__)
 
 from sat.core import exceptions
+from sat.tools.common import data_format
 
 from wokkel import disco, iwokkel
 try:
@@ -31,81 +32,88 @@
     from wokkel.subprotocols import XMPPHandler
 from twisted.python import failure
 from zope.interface import implements
-
+from textwrap import dedent
 
-NS_MPH = 'urn:xmpp:hints'
 
 PLUGIN_INFO = {
-    "name": "Message Processing Hints",
-    "import_name": "XEP-0334",
-    "type": "XEP",
-    "protocols": ["XEP-0334"],
+    "name": u"Message Processing Hints",
+    "import_name": u"XEP-0334",
+    "type": u"XEP",
+    "protocols": [u"XEP-0334"],
     "main": "XEP_0334",
-    "handler": "yes",
-    "description": _("""Implementation of Message Processing Hints""")
+    "handler": u"yes",
+    "description": D_(u"""Implementation of Message Processing Hints"""),
+    "usage": dedent(D_(u"""\
+             Frontends can use HINT_* constants in mess_data['extra'] in a serialized 'hints' dict.
+             Internal plugins can use directly addHint([HINT_* constant]).
+             Will set mess_data['extra']['history'] to 'skipped' when no store is requested and message is not saved in history."""))
+
 }
 
+NS_HINTS = u'urn:xmpp:hints'
+
 
 class XEP_0334(object):
+    HINT_NO_PERMANENT_STORE = u'no-permanent-store'
+    HINT_NO_STORE = u'no-store'
+    HINT_NO_COPY = u'no-copy'
+    HINT_STORE = u'store'
+    HINTS = (HINT_NO_PERMANENT_STORE, HINT_NO_STORE, HINT_NO_COPY, HINT_STORE)
 
     def __init__(self, host):
         log.info(_("Message Processing Hints plugin initialization"))
         self.host = host
         host.trigger.add("messageSend", self.messageSendTrigger)
-        host.trigger.add("MessageReceived", self.messageReceivedTrigger)
+        host.trigger.add("MessageReceived", self.messageReceivedTrigger, priority=-1000)
 
     def getHandler(self, profile):
-        return XEP_0334_handler(self, profile)
+        return XEP_0334_handler()
+
+    def addHint(self, mess_data, hint):
+        if hint == self.HINT_NO_COPY and not mess_data['to'].resource:
+            log.error(u"{hint} can only be used with full jids! Ignoring it.".format(hint=hint))
+            return
+        hints = mess_data.setdefault('hints', set())
+        if hint in self.HINTS:
+            hints.add(hint)
+        else:
+            log.error(u"Unknown hint: {}".format(hint))
+
+    def _sendPostXmlTreatment(self, mess_data):
+        if 'hints' in mess_data:
+            for hint in mess_data['hints']:
+                mess_data[u'xml'].addElement((NS_HINTS, hint))
+        return mess_data
 
     def messageSendTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
         """Add the hints element to the message to be sent"""
-        hints = []
-        for key in ('no-permanent-storage', 'no-storage', 'no-copy'):
-            if mess_data['extra'].get(key, None):
-                hints.append(key)
+        if u'hints' in mess_data[u'extra']:
+            for hint in data_format.dict2iter(u'hints', mess_data[u'extra'], pop=True):
+                self.addHint(hint)
 
-        def treatment(mess_data):
-            message = mess_data['xml']
-            for key in hints:
-                message.addElement((NS_MPH, key))
-                if key in ('no-permanent-storage', 'no-storage'):
-                    mess_data['extra']['no_storage'] = True
-                    # TODO: the core doesn't process this 'no_storage' info yet
-                    # it will be added after the frontends refactorization
-            return mess_data
-
-        if hints:
-            post_xml_treatments.addCallback(treatment)
+        post_xml_treatments.addCallback(self._sendPostXmlTreatment)
         return True
 
-    def messageReceivedTrigger(self, client, message, post_treat):
+    def _receivedSkipHistory(self, mess_data):
+        mess_data[u'extra'][u'history'] == u'skipped'
+        raise failure.Failure(exceptions.SkipHistory())
+
+
+    def messageReceivedTrigger(self, client, message_elt, post_treat):
         """Check for hints in the received message"""
-        hints = []
-        for key in ('no-permanent-storage', 'no-storage'):
-            try:
-                message.elements(uri=NS_MPH, name=key).next()
-                hints.append(key)
-            except StopIteration:
-                pass
-
-        def post_treat_hints(data):
-            raise failure.Failure(exceptions.SkipHistory())
-
-        if hints:
-            post_treat.addCallback(post_treat_hints)
+        for elt in message_elt.elements():
+            if elt.uri == NS_HINTS and elt.name in (self.HINT_NO_PERMANENT_STORE, self.HINT_NO_STORE):
+                log.debug(u"history will be skipped for this message, as requested")
+                post_treat.addCallback(self._receivedSkipHistory)
+                break
         return True
 
 
 class XEP_0334_handler(XMPPHandler):
     implements(iwokkel.IDisco)
 
-    def __init__(self, plugin_parent, profile):
-        self.plugin_parent = plugin_parent
-        self.host = plugin_parent.host
-        self.profile = profile
-
     def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
-        return [disco.DiscoFeature(NS_MPH)]
+        return [disco.DiscoFeature(NS_HINTS)]
 
     def getDiscoItems(self, requestor, target, nodeIdentifier=''):
         return []