Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0231.py @ 2624:56f94936df1e
code style reformatting using black
author | Goffi <goffi@goffi.org> |
---|---|
date | Wed, 27 Jun 2018 20:14:46 +0200 |
parents | 26edcf3a30eb |
children | 378188abe941 |
comparison
equal
deleted
inserted
replaced
2623:49533de4540b | 2624:56f94936df1e |
---|---|
19 | 19 |
20 from sat.core.i18n import _ | 20 from sat.core.i18n import _ |
21 from sat.core.constants import Const as C | 21 from sat.core.constants import Const as C |
22 from sat.core import exceptions | 22 from sat.core import exceptions |
23 from sat.core.log import getLogger | 23 from sat.core.log import getLogger |
24 | |
24 log = getLogger(__name__) | 25 log = getLogger(__name__) |
25 from sat.tools import xml_tools | 26 from sat.tools import xml_tools |
26 from wokkel import disco, iwokkel | 27 from wokkel import disco, iwokkel |
27 from zope.interface import implements | 28 from zope.interface import implements |
28 from twisted.python import failure | 29 from twisted.python import failure |
41 C.PI_TYPE: "XEP", | 42 C.PI_TYPE: "XEP", |
42 C.PI_MODES: C.PLUG_MODE_BOTH, | 43 C.PI_MODES: C.PLUG_MODE_BOTH, |
43 C.PI_PROTOCOLS: ["XEP-0231"], | 44 C.PI_PROTOCOLS: ["XEP-0231"], |
44 C.PI_MAIN: "XEP_0231", | 45 C.PI_MAIN: "XEP_0231", |
45 C.PI_HANDLER: "yes", | 46 C.PI_HANDLER: "yes", |
46 C.PI_DESCRIPTION: _("""Implementation of bits of binary (used for small images/files)""") | 47 C.PI_DESCRIPTION: _( |
48 """Implementation of bits of binary (used for small images/files)""" | |
49 ), | |
47 } | 50 } |
48 | 51 |
49 NS_BOB = u'urn:xmpp:bob' | 52 NS_BOB = u"urn:xmpp:bob" |
50 IQ_BOB_REQUEST = C.IQ_GET + '/data[@xmlns="' + NS_BOB + '"]' | 53 IQ_BOB_REQUEST = C.IQ_GET + '/data[@xmlns="' + NS_BOB + '"]' |
51 | 54 |
52 | 55 |
53 class XEP_0231(object): | 56 class XEP_0231(object): |
54 | |
55 def __init__(self, host): | 57 def __init__(self, host): |
56 log.info(_(u"plugin Bits of Binary initialization")) | 58 log.info(_(u"plugin Bits of Binary initialization")) |
57 self.host = host | 59 self.host = host |
58 host.registerNamespace('bob', NS_BOB) | 60 host.registerNamespace("bob", NS_BOB) |
59 host.trigger.add("xhtml_post_treat", self.XHTMLTrigger) | 61 host.trigger.add("xhtml_post_treat", self.XHTMLTrigger) |
60 host.bridge.addMethod("bobGetFile", ".plugin", | 62 host.bridge.addMethod( |
61 in_sign='sss', out_sign='s', | 63 "bobGetFile", |
62 method=self._getFile, | 64 ".plugin", |
63 async=True) | 65 in_sign="sss", |
66 out_sign="s", | |
67 method=self._getFile, | |
68 async=True, | |
69 ) | |
64 | 70 |
65 def dumpData(self, cache, data_elt, cid): | 71 def dumpData(self, cache, data_elt, cid): |
66 """save file encoded in data_elt to cache | 72 """save file encoded in data_elt to cache |
67 | 73 |
68 @param cache(memory.cache.Cache): cache to use to store the data | 74 @param cache(memory.cache.Cache): cache to use to store the data |
69 @param data_elt(domish.Element): <data> as in XEP-0231 | 75 @param data_elt(domish.Element): <data> as in XEP-0231 |
70 @param cid(unicode): content-id | 76 @param cid(unicode): content-id |
71 @return(unicode): full path to dumped file | 77 @return(unicode): full path to dumped file |
72 """ | 78 """ |
73 # FIXME: is it needed to use a separate thread? | 79 # FIXME: is it needed to use a separate thread? |
74 # probably not with the little data expected with BoB | 80 # probably not with the little data expected with BoB |
75 try: | 81 try: |
76 max_age = int(data_elt['max-age']) | 82 max_age = int(data_elt["max-age"]) |
77 if max_age < 0: | 83 if max_age < 0: |
78 raise ValueError | 84 raise ValueError |
79 except (KeyError, ValueError): | 85 except (KeyError, ValueError): |
80 log.warning(u'invalid max-age found') | 86 log.warning(u"invalid max-age found") |
81 max_age = None | 87 max_age = None |
82 | 88 |
83 with cache.cacheData( | 89 with cache.cacheData( |
84 PLUGIN_INFO[C.PI_IMPORT_NAME], | 90 PLUGIN_INFO[C.PI_IMPORT_NAME], cid, data_elt.getAttribute("type"), max_age |
85 cid, | 91 ) as f: |
86 data_elt.getAttribute('type'), | |
87 max_age) as f: | |
88 | 92 |
89 file_path = f.name | 93 file_path = f.name |
90 f.write(base64.b64decode(str(data_elt))) | 94 f.write(base64.b64decode(str(data_elt))) |
91 | 95 |
92 return file_path | 96 return file_path |
93 | 97 |
94 def getHandler(self, client): | 98 def getHandler(self, client): |
95 return XEP_0231_handler(self) | 99 return XEP_0231_handler(self) |
96 | 100 |
97 def _requestCb(self, iq_elt, cache, cid): | 101 def _requestCb(self, iq_elt, cache, cid): |
98 for data_elt in iq_elt.elements(NS_BOB, u'data'): | 102 for data_elt in iq_elt.elements(NS_BOB, u"data"): |
99 if data_elt.getAttribute('cid') == cid: | 103 if data_elt.getAttribute("cid") == cid: |
100 file_path = self.dumpData(cache, data_elt, cid) | 104 file_path = self.dumpData(cache, data_elt, cid) |
101 return file_path | 105 return file_path |
102 | 106 |
103 log.warning(u"invalid data stanza received, requested cid was not found:\n{iq_elt}\nrequested cid: {cid}".format( | 107 log.warning( |
104 iq_elt = iq_elt, | 108 u"invalid data stanza received, requested cid was not found:\n{iq_elt}\nrequested cid: {cid}".format( |
105 cid = cid | 109 iq_elt=iq_elt, cid=cid |
106 )) | 110 ) |
111 ) | |
107 raise failure.Failure(exceptions.DataError("missing data")) | 112 raise failure.Failure(exceptions.DataError("missing data")) |
108 | 113 |
109 def _requestEb(self, failure_): | 114 def _requestEb(self, failure_): |
110 """Log the error and continue errback chain""" | 115 """Log the error and continue errback chain""" |
111 log.warning(u"Can't get requested data:\n{reason}".format(reason=failure_)) | 116 log.warning(u"Can't get requested data:\n{reason}".format(reason=failure_)) |
120 client.cache will be used if None | 125 client.cache will be used if None |
121 @return D(unicode): path to file with data | 126 @return D(unicode): path to file with data |
122 """ | 127 """ |
123 if cache is None: | 128 if cache is None: |
124 cache = client.cache | 129 cache = client.cache |
125 iq_elt = client.IQ('get') | 130 iq_elt = client.IQ("get") |
126 iq_elt['to'] = to_jid.full() | 131 iq_elt["to"] = to_jid.full() |
127 data_elt = iq_elt.addElement((NS_BOB, 'data')) | 132 data_elt = iq_elt.addElement((NS_BOB, "data")) |
128 data_elt['cid'] = cid | 133 data_elt["cid"] = cid |
129 d = iq_elt.send() | 134 d = iq_elt.send() |
130 d.addCallback(self._requestCb, cache, cid) | 135 d.addCallback(self._requestCb, cache, cid) |
131 d.addErrback(self._requestEb) | 136 d.addErrback(self._requestEb) |
132 return d | 137 return d |
133 | 138 |
134 def _setImgEltSrc(self, path, img_elt): | 139 def _setImgEltSrc(self, path, img_elt): |
135 img_elt[u'src'] = u'file://{}'.format(path) | 140 img_elt[u"src"] = u"file://{}".format(path) |
136 | 141 |
137 def XHTMLTrigger(self, client, message_elt, body_elt, lang, treat_d): | 142 def XHTMLTrigger(self, client, message_elt, body_elt, lang, treat_d): |
138 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, u"img"): |
139 source = img_elt.getAttribute(u'src','') | 144 source = img_elt.getAttribute(u"src", "") |
140 if source.startswith(u'cid:'): | 145 if source.startswith(u"cid:"): |
141 cid = source[4:] | 146 cid = source[4:] |
142 file_path = client.cache.getFilePath(cid) | 147 file_path = client.cache.getFilePath(cid) |
143 if file_path is not None: | 148 if file_path is not None: |
144 # image is in cache, we change the url | 149 # image is in cache, we change the url |
145 img_elt[u'src'] = u'file://{}'.format(file_path) | 150 img_elt[u"src"] = u"file://{}".format(file_path) |
146 continue | 151 continue |
147 else: | 152 else: |
148 # image is not in cache, is it given locally? | 153 # image is not in cache, is it given locally? |
149 for data_elt in message_elt.elements(NS_BOB, u'data'): | 154 for data_elt in message_elt.elements(NS_BOB, u"data"): |
150 if data_elt.getAttribute('cid') == cid: | 155 if data_elt.getAttribute("cid") == cid: |
151 file_path = self.dumpData(client.cache, data_elt, cid) | 156 file_path = self.dumpData(client.cache, data_elt, cid) |
152 img_elt[u'src'] = u'file://{}'.format(file_path) | 157 img_elt[u"src"] = u"file://{}".format(file_path) |
153 break | 158 break |
154 else: | 159 else: |
155 # cid not found locally, we need to request it | 160 # cid not found locally, we need to request it |
156 # so we use the deferred | 161 # so we use the deferred |
157 d = self.requestData(client, jid.JID(message_elt['from']), cid) | 162 d = self.requestData(client, jid.JID(message_elt["from"]), cid) |
158 d.addCallback(partial(self._setImgEltSrc, img_elt=img_elt)) | 163 d.addCallback(partial(self._setImgEltSrc, img_elt=img_elt)) |
159 treat_d.addCallback(lambda dummy: d) | 164 treat_d.addCallback(lambda dummy: d) |
160 | 165 |
161 def onComponentRequest(self, iq_elt, client): | 166 def onComponentRequest(self, iq_elt, client): |
162 """cache data is retrieve from common cache for components""" | 167 """cache data is retrieve from common cache for components""" |
163 # FIXME: this is a security/privacy issue as no access check is done | 168 # FIXME: this is a security/privacy issue as no access check is done |
164 # but this is mitigated by the fact that the cid must be known. | 169 # but this is mitigated by the fact that the cid must be known. |
165 # An access check should be implemented though. | 170 # An access check should be implemented though. |
166 | 171 |
167 iq_elt.handled = True | 172 iq_elt.handled = True |
168 data_elt = next(iq_elt.elements(NS_BOB, 'data')) | 173 data_elt = next(iq_elt.elements(NS_BOB, "data")) |
169 try: | 174 try: |
170 cid = data_elt[u'cid'] | 175 cid = data_elt[u"cid"] |
171 except KeyError: | 176 except KeyError: |
172 error_elt = jabber_error.StanzaError('not-acceptable').toResponse(iq_elt) | 177 error_elt = jabber_error.StanzaError("not-acceptable").toResponse(iq_elt) |
173 client.send(error_elt) | 178 client.send(error_elt) |
174 return | 179 return |
175 | 180 |
176 metadata = self.host.common_cache.getMetadata(cid) | 181 metadata = self.host.common_cache.getMetadata(cid) |
177 if metadata is None: | 182 if metadata is None: |
178 error_elt = jabber_error.StanzaError('item-not-found').toResponse(iq_elt) | 183 error_elt = jabber_error.StanzaError("item-not-found").toResponse(iq_elt) |
179 client.send(error_elt) | 184 client.send(error_elt) |
180 return | 185 return |
181 | 186 |
182 with open(metadata['path']) as f: | 187 with open(metadata["path"]) as f: |
183 data = f.read() | 188 data = f.read() |
184 | 189 |
185 result_elt = xmlstream.toResponse(iq_elt, 'result') | 190 result_elt = xmlstream.toResponse(iq_elt, "result") |
186 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")) |
187 data_elt[u'cid'] = cid | 192 data_elt[u"cid"] = cid |
188 data_elt[u'type'] = metadata[u'mime_type'] | 193 data_elt[u"type"] = metadata[u"mime_type"] |
189 data_elt[u'max-age'] = unicode(int(max(0, metadata['eol'] - time.time()))) | 194 data_elt[u"max-age"] = unicode(int(max(0, metadata["eol"] - time.time()))) |
190 client.send(result_elt) | 195 client.send(result_elt) |
191 | 196 |
192 def _getFile(self, peer_jid_s, cid, profile): | 197 def _getFile(self, peer_jid_s, cid, profile): |
193 peer_jid = jid.JID(peer_jid_s) | 198 peer_jid = jid.JID(peer_jid_s) |
194 assert cid | 199 assert cid |
205 None to ignore | 210 None to ignore |
206 @return D(unicode): path to cached data | 211 @return D(unicode): path to cached data |
207 """ | 212 """ |
208 file_path = client.cache.getFilePath(cid) | 213 file_path = client.cache.getFilePath(cid) |
209 if file_path is not None: | 214 if file_path is not None: |
210 # file is in cache | 215 # file is in cache |
211 return defer.succeed(file_path) | 216 return defer.succeed(file_path) |
212 else: | 217 else: |
213 # file not in cache, is it given locally? | 218 # file not in cache, is it given locally? |
214 if parent_elt is not None: | 219 if parent_elt is not None: |
215 for data_elt in parent_elt.elements(NS_BOB, u'data'): | 220 for data_elt in parent_elt.elements(NS_BOB, u"data"): |
216 if data_elt.getAttribute('cid') == cid: | 221 if data_elt.getAttribute("cid") == cid: |
217 return defer.succeed(self.dumpData(client.cache, data_elt, cid)) | 222 return defer.succeed(self.dumpData(client.cache, data_elt, cid)) |
218 | 223 |
219 # cid not found locally, we need to request it | 224 # cid not found locally, we need to request it |
220 # so we use the deferred | 225 # so we use the deferred |
221 return self.requestData(client, peer_jid, cid) | 226 return self.requestData(client, peer_jid, cid) |
228 self.plugin_parent = plugin_parent | 233 self.plugin_parent = plugin_parent |
229 self.host = plugin_parent.host | 234 self.host = plugin_parent.host |
230 | 235 |
231 def connectionInitialized(self): | 236 def connectionInitialized(self): |
232 if self.parent.is_component: | 237 if self.parent.is_component: |
233 self.xmlstream.addObserver(IQ_BOB_REQUEST, self.plugin_parent.onComponentRequest, client=self.parent) | 238 self.xmlstream.addObserver( |
234 | 239 IQ_BOB_REQUEST, self.plugin_parent.onComponentRequest, client=self.parent |
235 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | 240 ) |
241 | |
242 def getDiscoInfo(self, requestor, target, nodeIdentifier=""): | |
236 return [disco.DiscoFeature(NS_BOB)] | 243 return [disco.DiscoFeature(NS_BOB)] |
237 | 244 |
238 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | 245 def getDiscoItems(self, requestor, target, nodeIdentifier=""): |
239 return [] | 246 return [] |