comparison sat/plugins/plugin_exp_invitation.py @ 3028:ab2696e34d29

Python 3 port: /!\ this is a huge commit /!\ starting from this commit, SàT is needs Python 3.6+ /!\ SàT maybe be instable or some feature may not work anymore, this will improve with time This patch port backend, bridge and frontends to Python 3. Roughly this has been done this way: - 2to3 tools has been applied (with python 3.7) - all references to python2 have been replaced with python3 (notably shebangs) - fixed files not handled by 2to3 (notably the shell script) - several manual fixes - fixed issues reported by Python 3 that where not handled in Python 2 - replaced "async" with "async_" when needed (it's a reserved word from Python 3.7) - replaced zope's "implements" with @implementer decorator - temporary hack to handle data pickled in database, as str or bytes may be returned, to be checked later - fixed hash comparison for password - removed some code which is not needed anymore with Python 3 - deactivated some code which needs to be checked (notably certificate validation) - tested with jp, fixed reported issues until some basic commands worked - ported Primitivus (after porting dependencies like urwid satext) - more manual fixes
author Goffi <goffi@goffi.org>
date Tue, 13 Aug 2019 19:08:41 +0200
parents 83cbd4545274
children 9d0df638c8b4
comparison
equal deleted inserted replaced
3027:ff5bcb12ae60 3028:ab2696e34d29
1 #!/usr/bin/env python2 1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*- 2 # -*- coding: utf-8 -*-
3 3
4 # SAT plugin to detect language (experimental) 4 # SAT plugin to detect language (experimental)
5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org) 5 # Copyright (C) 2009-2019 Jérôme Poisson (goffi@goffi.org)
6 6
22 from sat.core.constants import Const as C 22 from sat.core.constants import Const as C
23 from sat.core.log import getLogger 23 from sat.core.log import getLogger
24 from twisted.internet import defer 24 from twisted.internet import defer
25 from twisted.words.protocols.jabber import jid 25 from twisted.words.protocols.jabber import jid
26 from wokkel import disco, iwokkel 26 from wokkel import disco, iwokkel
27 from zope.interface import implements 27 from zope.interface import implementer
28 from twisted.words.protocols.jabber.xmlstream import XMPPHandler 28 from twisted.words.protocols.jabber.xmlstream import XMPPHandler
29 29
30 log = getLogger(__name__) 30 log = getLogger(__name__)
31 31
32 32
33 PLUGIN_INFO = { 33 PLUGIN_INFO = {
34 C.PI_NAME: "Invitation", 34 C.PI_NAME: "Invitation",
35 C.PI_IMPORT_NAME: "INVITATION", 35 C.PI_IMPORT_NAME: "INVITATION",
36 C.PI_TYPE: "EXP", 36 C.PI_TYPE: "EXP",
37 C.PI_PROTOCOLS: [], 37 C.PI_PROTOCOLS: [],
38 C.PI_DEPENDENCIES: [u"XEP-0060", u"XEP-0329"], 38 C.PI_DEPENDENCIES: ["XEP-0060", "XEP-0329"],
39 C.PI_RECOMMENDATIONS: [], 39 C.PI_RECOMMENDATIONS: [],
40 C.PI_MAIN: "Invitation", 40 C.PI_MAIN: "Invitation",
41 C.PI_HANDLER: "yes", 41 C.PI_HANDLER: "yes",
42 C.PI_DESCRIPTION: _(u"Experimental handling of invitations"), 42 C.PI_DESCRIPTION: _("Experimental handling of invitations"),
43 } 43 }
44 44
45 NS_INVITATION = u"https://salut-a-toi/protocol/invitation:0" 45 NS_INVITATION = "https://salut-a-toi/protocol/invitation:0"
46 INVITATION = '/message/invitation[@xmlns="{ns_invit}"]'.format( 46 INVITATION = '/message/invitation[@xmlns="{ns_invit}"]'.format(
47 ns_invit=NS_INVITATION 47 ns_invit=NS_INVITATION
48 ) 48 )
49 NS_INVITATION_LIST = NS_INVITATION + u"#list" 49 NS_INVITATION_LIST = NS_INVITATION + "#list"
50 50
51 51
52 class Invitation(object): 52 class Invitation(object):
53 53
54 def __init__(self, host): 54 def __init__(self, host):
55 log.info(_(u"Invitation plugin initialization")) 55 log.info(_("Invitation plugin initialization"))
56 self.host = host 56 self.host = host
57 self._p = self.host.plugins["XEP-0060"] 57 self._p = self.host.plugins["XEP-0060"]
58 # map from namespace of the invitation to callback handling it 58 # map from namespace of the invitation to callback handling it
59 self._ns_cb = {} 59 self._ns_cb = {}
60 60
86 - path(unicode, None): path of the repository 86 - path(unicode, None): path of the repository
87 @raise exceptions.ConflictError: this namespace is already registered 87 @raise exceptions.ConflictError: this namespace is already registered
88 """ 88 """
89 if namespace in self._ns_cb: 89 if namespace in self._ns_cb:
90 raise exceptions.ConflictError( 90 raise exceptions.ConflictError(
91 u"invitation namespace {namespace} is already register with {callback}" 91 "invitation namespace {namespace} is already register with {callback}"
92 .format(namespace=namespace, callback=self._ns_cb[namespace])) 92 .format(namespace=namespace, callback=self._ns_cb[namespace]))
93 self._ns_cb[namespace] = callback 93 self._ns_cb[namespace] = callback
94 94
95 def _generateBaseInvitation(self, client, invitee_jid, name, extra): 95 def _generateBaseInvitation(self, client, invitee_jid, name, extra):
96 """Generate common mess_data end invitation_elt 96 """Generate common mess_data end invitation_elt
111 "extra": {}, 111 "extra": {},
112 } 112 }
113 client.generateMessageXML(mess_data) 113 client.generateMessageXML(mess_data)
114 invitation_elt = mess_data["xml"].addElement("invitation", NS_INVITATION) 114 invitation_elt = mess_data["xml"].addElement("invitation", NS_INVITATION)
115 if name is not None: 115 if name is not None:
116 invitation_elt[u"name"] = name 116 invitation_elt["name"] = name
117 thumb_url = extra.get(u'thumb_url') 117 thumb_url = extra.get('thumb_url')
118 if thumb_url: 118 if thumb_url:
119 if not thumb_url.startswith(u'http'): 119 if not thumb_url.startswith('http'):
120 log.warning( 120 log.warning(
121 u"only http URLs are allowed for thumbnails, got {url}, ignoring" 121 "only http URLs are allowed for thumbnails, got {url}, ignoring"
122 .format(url=thumb_url)) 122 .format(url=thumb_url))
123 else: 123 else:
124 invitation_elt[u'thumb_url'] = thumb_url 124 invitation_elt['thumb_url'] = thumb_url
125 return mess_data, invitation_elt 125 return mess_data, invitation_elt
126 126
127 def sendPubsubInvitation(self, client, invitee_jid, service, node, 127 def sendPubsubInvitation(self, client, invitee_jid, service, node,
128 item_id, name, extra): 128 item_id, name, extra):
129 """Send an pubsub invitation in a <message> stanza 129 """Send an pubsub invitation in a <message> stanza
137 """ 137 """
138 if extra is None: 138 if extra is None:
139 extra = {} 139 extra = {}
140 mess_data, invitation_elt = self._generateBaseInvitation( 140 mess_data, invitation_elt = self._generateBaseInvitation(
141 client, invitee_jid, name, extra) 141 client, invitee_jid, name, extra)
142 pubsub_elt = invitation_elt.addElement(u"pubsub") 142 pubsub_elt = invitation_elt.addElement("pubsub")
143 pubsub_elt[u"service"] = service.full() 143 pubsub_elt["service"] = service.full()
144 pubsub_elt[u"node"] = node 144 pubsub_elt["node"] = node
145 pubsub_elt[u"item"] = item_id 145 pubsub_elt["item"] = item_id
146 return client.send(mess_data[u"xml"]) 146 return client.send(mess_data["xml"])
147 147
148 def sendFileSharingInvitation(self, client, invitee_jid, service, repos_type=None, 148 def sendFileSharingInvitation(self, client, invitee_jid, service, repos_type=None,
149 namespace=None, path=None, name=None, extra=None): 149 namespace=None, path=None, name=None, extra=None):
150 """Send a file sharing invitation in a <message> stanza 150 """Send a file sharing invitation in a <message> stanza
151 151
161 """ 161 """
162 if extra is None: 162 if extra is None:
163 extra = {} 163 extra = {}
164 mess_data, invitation_elt = self._generateBaseInvitation( 164 mess_data, invitation_elt = self._generateBaseInvitation(
165 client, invitee_jid, name, extra) 165 client, invitee_jid, name, extra)
166 file_sharing_elt = invitation_elt.addElement(u"file_sharing") 166 file_sharing_elt = invitation_elt.addElement("file_sharing")
167 file_sharing_elt[u"service"] = service.full() 167 file_sharing_elt["service"] = service.full()
168 if repos_type is not None: 168 if repos_type is not None:
169 if repos_type not in (u"files", "photos"): 169 if repos_type not in ("files", "photos"):
170 msg = u"unknown repository type: {repos_type}".format( 170 msg = "unknown repository type: {repos_type}".format(
171 repos_type=repos_type) 171 repos_type=repos_type)
172 log.warning(msg) 172 log.warning(msg)
173 raise exceptions.DateError(msg) 173 raise exceptions.DateError(msg)
174 file_sharing_elt[u"type"] = repos_type 174 file_sharing_elt["type"] = repos_type
175 if namespace is not None: 175 if namespace is not None:
176 file_sharing_elt[u"namespace"] = namespace 176 file_sharing_elt["namespace"] = namespace
177 if path is not None: 177 if path is not None:
178 file_sharing_elt[u"path"] = path 178 file_sharing_elt["path"] = path
179 return client.send(mess_data[u"xml"]) 179 return client.send(mess_data["xml"])
180 180
181 @defer.inlineCallbacks 181 @defer.inlineCallbacks
182 def _parsePubsubElt(self, client, pubsub_elt): 182 def _parsePubsubElt(self, client, pubsub_elt):
183 try: 183 try:
184 service = jid.JID(pubsub_elt["service"]) 184 service = jid.JID(pubsub_elt["service"])
185 node = pubsub_elt["node"] 185 node = pubsub_elt["node"]
186 item_id = pubsub_elt.getAttribute("item") 186 item_id = pubsub_elt.getAttribute("item")
187 except (RuntimeError, KeyError): 187 except (RuntimeError, KeyError):
188 log.warning(_(u"Bad invitation, ignoring")) 188 log.warning(_("Bad invitation, ignoring"))
189 raise exceptions.DataError 189 raise exceptions.DataError
190 190
191 try: 191 try:
192 items, metadata = yield self._p.getItems(client, service, node, 192 items, metadata = yield self._p.getItems(client, service, node,
193 item_ids=[item_id]) 193 item_ids=[item_id])
194 except Exception as e: 194 except Exception as e:
195 log.warning(_(u"Can't get item linked with invitation: {reason}").format( 195 log.warning(_("Can't get item linked with invitation: {reason}").format(
196 reason=e)) 196 reason=e))
197 try: 197 try:
198 item_elt = items[0] 198 item_elt = items[0]
199 except IndexError: 199 except IndexError:
200 log.warning(_(u"Invitation was linking to a non existing item")) 200 log.warning(_("Invitation was linking to a non existing item"))
201 raise exceptions.DataError 201 raise exceptions.DataError
202 202
203 try: 203 try:
204 namespace = item_elt.firstChildElement().uri 204 namespace = item_elt.firstChildElement().uri
205 except Exception as e: 205 except Exception as e:
206 log.warning(_(u"Can't retrieve namespace of invitation: {reason}").format( 206 log.warning(_("Can't retrieve namespace of invitation: {reason}").format(
207 reason = e)) 207 reason = e))
208 raise exceptions.DataError 208 raise exceptions.DataError
209 209
210 args = [service, node, item_id, item_elt] 210 args = [service, node, item_id, item_elt]
211 defer.returnValue((namespace, args)) 211 defer.returnValue((namespace, args))
212 212
213 def _parseFileSharingElt(self, client, file_sharing_elt): 213 def _parseFileSharingElt(self, client, file_sharing_elt):
214 try: 214 try:
215 service = jid.JID(file_sharing_elt["service"]) 215 service = jid.JID(file_sharing_elt["service"])
216 except (RuntimeError, KeyError): 216 except (RuntimeError, KeyError):
217 log.warning(_(u"Bad invitation, ignoring")) 217 log.warning(_("Bad invitation, ignoring"))
218 raise exceptions.DataError 218 raise exceptions.DataError
219 repos_type = file_sharing_elt.getAttribute(u"type", u"files") 219 repos_type = file_sharing_elt.getAttribute("type", "files")
220 namespace = file_sharing_elt.getAttribute(u"namespace") 220 namespace = file_sharing_elt.getAttribute("namespace")
221 path = file_sharing_elt.getAttribute(u"path") 221 path = file_sharing_elt.getAttribute("path")
222 args = [service, repos_type, namespace, path] 222 args = [service, repos_type, namespace, path]
223 ns_fis = self.host.getNamespace(u"fis") 223 ns_fis = self.host.getNamespace("fis")
224 return ns_fis, args 224 return ns_fis, args
225 225
226 @defer.inlineCallbacks 226 @defer.inlineCallbacks
227 def onInvitation(self, message_elt, client): 227 def onInvitation(self, message_elt, client):
228 log.debug(u"invitation received [{profile}]".format(profile=client.profile)) 228 log.debug("invitation received [{profile}]".format(profile=client.profile))
229 invitation_elt = message_elt.invitation 229 invitation_elt = message_elt.invitation
230 230
231 name = invitation_elt.getAttribute(u"name") 231 name = invitation_elt.getAttribute("name")
232 extra = {} 232 extra = {}
233 if invitation_elt.hasAttribute(u"thumb_url"): 233 if invitation_elt.hasAttribute("thumb_url"):
234 extra[u'thumb_url'] = invitation_elt[u'thumb_url'] 234 extra['thumb_url'] = invitation_elt['thumb_url']
235 235
236 for elt in invitation_elt.elements(): 236 for elt in invitation_elt.elements():
237 if elt.uri != NS_INVITATION: 237 if elt.uri != NS_INVITATION:
238 log.warning(u"unexpected element: {xml}".format(xml=elt.toXml())) 238 log.warning("unexpected element: {xml}".format(xml=elt.toXml()))
239 continue 239 continue
240 if elt.name == u"pubsub": 240 if elt.name == "pubsub":
241 method = self._parsePubsubElt 241 method = self._parsePubsubElt
242 elif elt.name == u"file_sharing": 242 elif elt.name == "file_sharing":
243 method = self._parseFileSharingElt 243 method = self._parseFileSharingElt
244 else: 244 else:
245 log.warning(u"not implemented invitation element: {xml}".format( 245 log.warning("not implemented invitation element: {xml}".format(
246 xml = elt.toXml())) 246 xml = elt.toXml()))
247 continue 247 continue
248 try: 248 try:
249 namespace, args = yield method(client, elt) 249 namespace, args = yield method(client, elt)
250 except exceptions.DataError: 250 except exceptions.DataError:
251 log.warning(u"Can't parse invitation element: {xml}".format( 251 log.warning("Can't parse invitation element: {xml}".format(
252 xml = elt.toXml())) 252 xml = elt.toXml()))
253 continue 253 continue
254 254
255 try: 255 try:
256 cb = self._ns_cb[namespace] 256 cb = self._ns_cb[namespace]
257 except KeyError: 257 except KeyError:
258 log.warning(_( 258 log.warning(_(
259 u'No handler for namespace "{namespace}", invitation ignored') 259 'No handler for namespace "{namespace}", invitation ignored')
260 .format(namespace=namespace)) 260 .format(namespace=namespace))
261 else: 261 else:
262 cb(client, name, extra, *args) 262 cb(client, name, extra, *args)
263 263
264 264
265 @implementer(iwokkel.IDisco)
265 class PubsubInvitationHandler(XMPPHandler): 266 class PubsubInvitationHandler(XMPPHandler):
266 implements(iwokkel.IDisco)
267 267
268 def __init__(self, plugin_parent): 268 def __init__(self, plugin_parent):
269 self.plugin_parent = plugin_parent 269 self.plugin_parent = plugin_parent
270 270
271 def connectionInitialized(self): 271 def connectionInitialized(self):