comparison sat/plugins/plugin_xep_0231.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 003b8b4b56a7
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 for Bit of Binary handling (XEP-0231) 4 # SAT plugin for Bit of Binary handling (XEP-0231)
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
23 from sat.core.log import getLogger 23 from sat.core.log import getLogger
24 24
25 log = getLogger(__name__) 25 log = getLogger(__name__)
26 from sat.tools import xml_tools 26 from sat.tools import xml_tools
27 from wokkel import disco, iwokkel 27 from wokkel import disco, iwokkel
28 from zope.interface import implements 28 from zope.interface import implementer
29 from twisted.python import failure 29 from twisted.python import failure
30 from twisted.words.protocols.jabber import xmlstream 30 from twisted.words.protocols.jabber import xmlstream
31 from twisted.words.protocols.jabber import jid 31 from twisted.words.protocols.jabber import jid
32 from twisted.words.protocols.jabber import error as jabber_error 32 from twisted.words.protocols.jabber import error as jabber_error
33 from twisted.internet import defer 33 from twisted.internet import defer
47 C.PI_DESCRIPTION: _( 47 C.PI_DESCRIPTION: _(
48 """Implementation of bits of binary (used for small images/files)""" 48 """Implementation of bits of binary (used for small images/files)"""
49 ), 49 ),
50 } 50 }
51 51
52 NS_BOB = u"urn:xmpp:bob" 52 NS_BOB = "urn:xmpp:bob"
53 IQ_BOB_REQUEST = C.IQ_GET + '/data[@xmlns="' + NS_BOB + '"]' 53 IQ_BOB_REQUEST = C.IQ_GET + '/data[@xmlns="' + NS_BOB + '"]'
54 54
55 55
56 class XEP_0231(object): 56 class XEP_0231(object):
57 def __init__(self, host): 57 def __init__(self, host):
58 log.info(_(u"plugin Bits of Binary initialization")) 58 log.info(_("plugin Bits of Binary initialization"))
59 self.host = host 59 self.host = host
60 host.registerNamespace("bob", NS_BOB) 60 host.registerNamespace("bob", NS_BOB)
61 host.trigger.add("xhtml_post_treat", self.XHTMLTrigger) 61 host.trigger.add("xhtml_post_treat", self.XHTMLTrigger)
62 host.bridge.addMethod( 62 host.bridge.addMethod(
63 "bobGetFile", 63 "bobGetFile",
64 ".plugin", 64 ".plugin",
65 in_sign="sss", 65 in_sign="sss",
66 out_sign="s", 66 out_sign="s",
67 method=self._getFile, 67 method=self._getFile,
68 async=True, 68 async_=True,
69 ) 69 )
70 70
71 def dumpData(self, cache, data_elt, cid): 71 def dumpData(self, cache, data_elt, cid):
72 """save file encoded in data_elt to cache 72 """save file encoded in data_elt to cache
73 73
81 try: 81 try:
82 max_age = int(data_elt["max-age"]) 82 max_age = int(data_elt["max-age"])
83 if max_age < 0: 83 if max_age < 0:
84 raise ValueError 84 raise ValueError
85 except (KeyError, ValueError): 85 except (KeyError, ValueError):
86 log.warning(u"invalid max-age found") 86 log.warning("invalid max-age found")
87 max_age = None 87 max_age = None
88 88
89 with cache.cacheData( 89 with cache.cacheData(
90 PLUGIN_INFO[C.PI_IMPORT_NAME], cid, data_elt.getAttribute("type"), max_age 90 PLUGIN_INFO[C.PI_IMPORT_NAME], cid, data_elt.getAttribute("type"), max_age
91 ) as f: 91 ) as f:
97 97
98 def getHandler(self, client): 98 def getHandler(self, client):
99 return XEP_0231_handler(self) 99 return XEP_0231_handler(self)
100 100
101 def _requestCb(self, iq_elt, cache, cid): 101 def _requestCb(self, iq_elt, cache, cid):
102 for data_elt in iq_elt.elements(NS_BOB, u"data"): 102 for data_elt in iq_elt.elements(NS_BOB, "data"):
103 if data_elt.getAttribute("cid") == cid: 103 if data_elt.getAttribute("cid") == cid:
104 file_path = self.dumpData(cache, data_elt, cid) 104 file_path = self.dumpData(cache, data_elt, cid)
105 return file_path 105 return file_path
106 106
107 log.warning( 107 log.warning(
108 u"invalid data stanza received, requested cid was not found:\n{iq_elt}\nrequested cid: {cid}".format( 108 "invalid data stanza received, requested cid was not found:\n{iq_elt}\nrequested cid: {cid}".format(
109 iq_elt=iq_elt, cid=cid 109 iq_elt=iq_elt, cid=cid
110 ) 110 )
111 ) 111 )
112 raise failure.Failure(exceptions.DataError("missing data")) 112 raise failure.Failure(exceptions.DataError("missing data"))
113 113
114 def _requestEb(self, failure_): 114 def _requestEb(self, failure_):
115 """Log the error and continue errback chain""" 115 """Log the error and continue errback chain"""
116 log.warning(u"Can't get requested data:\n{reason}".format(reason=failure_)) 116 log.warning("Can't get requested data:\n{reason}".format(reason=failure_))
117 return failure_ 117 return failure_
118 118
119 def requestData(self, client, to_jid, cid, cache=None): 119 def requestData(self, client, to_jid, cid, cache=None):
120 """Request data if we don't have it in cache 120 """Request data if we don't have it in cache
121 121
135 d.addCallback(self._requestCb, cache, cid) 135 d.addCallback(self._requestCb, cache, cid)
136 d.addErrback(self._requestEb) 136 d.addErrback(self._requestEb)
137 return d 137 return d
138 138
139 def _setImgEltSrc(self, path, img_elt): 139 def _setImgEltSrc(self, path, img_elt):
140 img_elt[u"src"] = u"file://{}".format(path) 140 img_elt["src"] = "file://{}".format(path)
141 141
142 def XHTMLTrigger(self, client, message_elt, body_elt, lang, treat_d): 142 def XHTMLTrigger(self, client, message_elt, body_elt, lang, treat_d):
143 for img_elt in xml_tools.findAll(body_elt, C.NS_XHTML, u"img"): 143 for img_elt in xml_tools.findAll(body_elt, C.NS_XHTML, "img"):
144 source = img_elt.getAttribute(u"src", "") 144 source = img_elt.getAttribute("src", "")
145 if source.startswith(u"cid:"): 145 if source.startswith("cid:"):
146 cid = source[4:] 146 cid = source[4:]
147 file_path = client.cache.getFilePath(cid) 147 file_path = client.cache.getFilePath(cid)
148 if file_path is not None: 148 if file_path is not None:
149 #  image is in cache, we change the url 149 #  image is in cache, we change the url
150 img_elt[u"src"] = u"file://{}".format(file_path) 150 img_elt["src"] = "file://{}".format(file_path)
151 continue 151 continue
152 else: 152 else:
153 # image is not in cache, is it given locally? 153 # image is not in cache, is it given locally?
154 for data_elt in message_elt.elements(NS_BOB, u"data"): 154 for data_elt in message_elt.elements(NS_BOB, "data"):
155 if data_elt.getAttribute("cid") == cid: 155 if data_elt.getAttribute("cid") == cid:
156 file_path = self.dumpData(client.cache, data_elt, cid) 156 file_path = self.dumpData(client.cache, data_elt, cid)
157 img_elt[u"src"] = u"file://{}".format(file_path) 157 img_elt["src"] = "file://{}".format(file_path)
158 break 158 break
159 else: 159 else:
160 # cid not found locally, we need to request it 160 # cid not found locally, we need to request it
161 # so we use the deferred 161 # so we use the deferred
162 d = self.requestData(client, jid.JID(message_elt["from"]), cid) 162 d = self.requestData(client, jid.JID(message_elt["from"]), cid)
170 # An access check should be implemented though. 170 # An access check should be implemented though.
171 171
172 iq_elt.handled = True 172 iq_elt.handled = True
173 data_elt = next(iq_elt.elements(NS_BOB, "data")) 173 data_elt = next(iq_elt.elements(NS_BOB, "data"))
174 try: 174 try:
175 cid = data_elt[u"cid"] 175 cid = data_elt["cid"]
176 except KeyError: 176 except KeyError:
177 error_elt = jabber_error.StanzaError("not-acceptable").toResponse(iq_elt) 177 error_elt = jabber_error.StanzaError("not-acceptable").toResponse(iq_elt)
178 client.send(error_elt) 178 client.send(error_elt)
179 return 179 return
180 180
187 with open(metadata["path"]) as f: 187 with open(metadata["path"]) as f:
188 data = f.read() 188 data = f.read()
189 189
190 result_elt = xmlstream.toResponse(iq_elt, "result") 190 result_elt = xmlstream.toResponse(iq_elt, "result")
191 data_elt = result_elt.addElement((NS_BOB, "data"), content=data.encode("base64")) 191 data_elt = result_elt.addElement((NS_BOB, "data"), content=data.encode("base64"))
192 data_elt[u"cid"] = cid 192 data_elt["cid"] = cid
193 data_elt[u"type"] = metadata[u"mime_type"] 193 data_elt["type"] = metadata["mime_type"]
194 data_elt[u"max-age"] = unicode(int(max(0, metadata["eol"] - time.time()))) 194 data_elt["max-age"] = str(int(max(0, metadata["eol"] - time.time())))
195 client.send(result_elt) 195 client.send(result_elt)
196 196
197 def _getFile(self, peer_jid_s, cid, profile): 197 def _getFile(self, peer_jid_s, cid, profile):
198 peer_jid = jid.JID(peer_jid_s) 198 peer_jid = jid.JID(peer_jid_s)
199 assert cid 199 assert cid
215 #  file is in cache 215 #  file is in cache
216 return defer.succeed(file_path) 216 return defer.succeed(file_path)
217 else: 217 else:
218 # file not in cache, is it given locally? 218 # file not in cache, is it given locally?
219 if parent_elt is not None: 219 if parent_elt is not None:
220 for data_elt in parent_elt.elements(NS_BOB, u"data"): 220 for data_elt in parent_elt.elements(NS_BOB, "data"):
221 if data_elt.getAttribute("cid") == cid: 221 if data_elt.getAttribute("cid") == cid:
222 return defer.succeed(self.dumpData(client.cache, data_elt, cid)) 222 return defer.succeed(self.dumpData(client.cache, data_elt, cid))
223 223
224 # cid not found locally, we need to request it 224 # cid not found locally, we need to request it
225 # so we use the deferred 225 # so we use the deferred
226 return self.requestData(client, peer_jid, cid) 226 return self.requestData(client, peer_jid, cid)
227 227
228 228
229 @implementer(iwokkel.IDisco)
229 class XEP_0231_handler(xmlstream.XMPPHandler): 230 class XEP_0231_handler(xmlstream.XMPPHandler):
230 implements(iwokkel.IDisco)
231 231
232 def __init__(self, plugin_parent): 232 def __init__(self, plugin_parent):
233 self.plugin_parent = plugin_parent 233 self.plugin_parent = plugin_parent
234 self.host = plugin_parent.host 234 self.host = plugin_parent.host
235 235