comparison src/plugins/plugin_xep_0334.py @ 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 633b5c21aefd
children c0577837680a
comparison
equal deleted inserted replaced
2130:f0bc29dc8157 2131:628c1c95f442
16 # GNU Affero General Public License for more details. 16 # GNU Affero General Public License for more details.
17 17
18 # You should have received a copy of the GNU Affero General Public License 18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>. 19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 20
21 from sat.core.i18n import _ 21 from sat.core.i18n import _, D_
22 from sat.core.log import getLogger 22 from sat.core.log import getLogger
23 log = getLogger(__name__) 23 log = getLogger(__name__)
24 24
25 from sat.core import exceptions 25 from sat.core import exceptions
26 from sat.tools.common import data_format
26 27
27 from wokkel import disco, iwokkel 28 from wokkel import disco, iwokkel
28 try: 29 try:
29 from twisted.words.protocols.xmlstream import XMPPHandler 30 from twisted.words.protocols.xmlstream import XMPPHandler
30 except ImportError: 31 except ImportError:
31 from wokkel.subprotocols import XMPPHandler 32 from wokkel.subprotocols import XMPPHandler
32 from twisted.python import failure 33 from twisted.python import failure
33 from zope.interface import implements 34 from zope.interface import implements
35 from textwrap import dedent
34 36
35 37
36 NS_MPH = 'urn:xmpp:hints' 38 PLUGIN_INFO = {
39 "name": u"Message Processing Hints",
40 "import_name": u"XEP-0334",
41 "type": u"XEP",
42 "protocols": [u"XEP-0334"],
43 "main": "XEP_0334",
44 "handler": u"yes",
45 "description": D_(u"""Implementation of Message Processing Hints"""),
46 "usage": dedent(D_(u"""\
47 Frontends can use HINT_* constants in mess_data['extra'] in a serialized 'hints' dict.
48 Internal plugins can use directly addHint([HINT_* constant]).
49 Will set mess_data['extra']['history'] to 'skipped' when no store is requested and message is not saved in history."""))
37 50
38 PLUGIN_INFO = {
39 "name": "Message Processing Hints",
40 "import_name": "XEP-0334",
41 "type": "XEP",
42 "protocols": ["XEP-0334"],
43 "main": "XEP_0334",
44 "handler": "yes",
45 "description": _("""Implementation of Message Processing Hints""")
46 } 51 }
52
53 NS_HINTS = u'urn:xmpp:hints'
47 54
48 55
49 class XEP_0334(object): 56 class XEP_0334(object):
57 HINT_NO_PERMANENT_STORE = u'no-permanent-store'
58 HINT_NO_STORE = u'no-store'
59 HINT_NO_COPY = u'no-copy'
60 HINT_STORE = u'store'
61 HINTS = (HINT_NO_PERMANENT_STORE, HINT_NO_STORE, HINT_NO_COPY, HINT_STORE)
50 62
51 def __init__(self, host): 63 def __init__(self, host):
52 log.info(_("Message Processing Hints plugin initialization")) 64 log.info(_("Message Processing Hints plugin initialization"))
53 self.host = host 65 self.host = host
54 host.trigger.add("messageSend", self.messageSendTrigger) 66 host.trigger.add("messageSend", self.messageSendTrigger)
55 host.trigger.add("MessageReceived", self.messageReceivedTrigger) 67 host.trigger.add("MessageReceived", self.messageReceivedTrigger, priority=-1000)
56 68
57 def getHandler(self, profile): 69 def getHandler(self, profile):
58 return XEP_0334_handler(self, profile) 70 return XEP_0334_handler()
71
72 def addHint(self, mess_data, hint):
73 if hint == self.HINT_NO_COPY and not mess_data['to'].resource:
74 log.error(u"{hint} can only be used with full jids! Ignoring it.".format(hint=hint))
75 return
76 hints = mess_data.setdefault('hints', set())
77 if hint in self.HINTS:
78 hints.add(hint)
79 else:
80 log.error(u"Unknown hint: {}".format(hint))
81
82 def _sendPostXmlTreatment(self, mess_data):
83 if 'hints' in mess_data:
84 for hint in mess_data['hints']:
85 mess_data[u'xml'].addElement((NS_HINTS, hint))
86 return mess_data
59 87
60 def messageSendTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments): 88 def messageSendTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments):
61 """Add the hints element to the message to be sent""" 89 """Add the hints element to the message to be sent"""
62 hints = [] 90 if u'hints' in mess_data[u'extra']:
63 for key in ('no-permanent-storage', 'no-storage', 'no-copy'): 91 for hint in data_format.dict2iter(u'hints', mess_data[u'extra'], pop=True):
64 if mess_data['extra'].get(key, None): 92 self.addHint(hint)
65 hints.append(key)
66 93
67 def treatment(mess_data): 94 post_xml_treatments.addCallback(self._sendPostXmlTreatment)
68 message = mess_data['xml']
69 for key in hints:
70 message.addElement((NS_MPH, key))
71 if key in ('no-permanent-storage', 'no-storage'):
72 mess_data['extra']['no_storage'] = True
73 # TODO: the core doesn't process this 'no_storage' info yet
74 # it will be added after the frontends refactorization
75 return mess_data
76
77 if hints:
78 post_xml_treatments.addCallback(treatment)
79 return True 95 return True
80 96
81 def messageReceivedTrigger(self, client, message, post_treat): 97 def _receivedSkipHistory(self, mess_data):
98 mess_data[u'extra'][u'history'] == u'skipped'
99 raise failure.Failure(exceptions.SkipHistory())
100
101
102 def messageReceivedTrigger(self, client, message_elt, post_treat):
82 """Check for hints in the received message""" 103 """Check for hints in the received message"""
83 hints = [] 104 for elt in message_elt.elements():
84 for key in ('no-permanent-storage', 'no-storage'): 105 if elt.uri == NS_HINTS and elt.name in (self.HINT_NO_PERMANENT_STORE, self.HINT_NO_STORE):
85 try: 106 log.debug(u"history will be skipped for this message, as requested")
86 message.elements(uri=NS_MPH, name=key).next() 107 post_treat.addCallback(self._receivedSkipHistory)
87 hints.append(key) 108 break
88 except StopIteration:
89 pass
90
91 def post_treat_hints(data):
92 raise failure.Failure(exceptions.SkipHistory())
93
94 if hints:
95 post_treat.addCallback(post_treat_hints)
96 return True 109 return True
97 110
98 111
99 class XEP_0334_handler(XMPPHandler): 112 class XEP_0334_handler(XMPPHandler):
100 implements(iwokkel.IDisco) 113 implements(iwokkel.IDisco)
101 114
102 def __init__(self, plugin_parent, profile):
103 self.plugin_parent = plugin_parent
104 self.host = plugin_parent.host
105 self.profile = profile
106
107 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): 115 def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
108 return [disco.DiscoFeature(NS_MPH)] 116 return [disco.DiscoFeature(NS_HINTS)]
109 117
110 def getDiscoItems(self, requestor, target, nodeIdentifier=''): 118 def getDiscoItems(self, requestor, target, nodeIdentifier=''):
111 return [] 119 return []