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 []