comparison src/plugins/plugin_xep_0231.py @ 2511:20a5e7db0609

plugin XEP-0231: separated the requestData code in a method which can called independantly + some minor improvments
author Goffi <goffi@goffi.org>
date Fri, 02 Mar 2018 17:45:23 +0100
parents 0046283a285d
children 95c31756944c
comparison
equal deleted inserted replaced
2510:4001aa395a04 2511:20a5e7db0609
17 # You should have received a copy of the GNU Affero General Public License 17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
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.log import getLogger 23 from sat.core.log import getLogger
23 log = getLogger(__name__) 24 log = getLogger(__name__)
24 from sat.tools import xml_tools 25 from sat.tools import xml_tools
25 from wokkel import disco, iwokkel 26 from wokkel import disco, iwokkel
26 from zope.interface import implements 27 from zope.interface import implements
27 from twisted.words.protocols.jabber.xmlstream import XMPPHandler 28 from twisted.python import failure
29 from twisted.words.protocols.jabber import xmlstream
30 from twisted.words.protocols.jabber import jid
31 from functools import partial
28 import base64 32 import base64
29 33
30 34
31 PLUGIN_INFO = { 35 PLUGIN_INFO = {
32 C.PI_NAME: "Bits of Binary", 36 C.PI_NAME: "Bits of Binary",
47 def __init__(self, host): 51 def __init__(self, host):
48 log.info(_(u"plugin Bits of Binary initialization")) 52 log.info(_(u"plugin Bits of Binary initialization"))
49 self.host = host 53 self.host = host
50 host.trigger.add("xhtml_post_treat", self.XHTMLTrigger) 54 host.trigger.add("xhtml_post_treat", self.XHTMLTrigger)
51 55
52 def dumpData(self, client, data_elt, cid): 56 def dumpData(self, cache, data_elt, cid):
53 """save file encoded in data_elt to cache 57 """save file encoded in data_elt to cache
54 58
59 @param cache(memory.cache.Cache): cache to use to store the data
55 @param data_elt(domish.Element): <data> as in XEP-0231 60 @param data_elt(domish.Element): <data> as in XEP-0231
56 @param cid(unicode): content-id 61 @param cid(unicode): content-id
57 @return(unicode): full path to dumped file 62 @return(unicode): full path to dumped file
58 """ 63 """
59 # FIXME: is it needed to use a separate thread? 64 # FIXME: is it needed to use a separate thread?
60 # probably not with the little data expected with BoB 65 # probably not with the little data expected with BoB
61 mime_type = data_elt.getAttribute('type','')
62
63 try: 66 try:
64 max_age = int(data_elt['max-age']) 67 max_age = int(data_elt['max-age'])
68 if max_age < 0:
69 raise ValueError
65 except (KeyError, ValueError): 70 except (KeyError, ValueError):
66 log.warning(u'invalid max-age found') 71 log.warning(u'invalid max-age found')
67 max_age = None 72 max_age = None
68 73
69 with client.cache.cacheData( 74 with cache.cacheData(
70 PLUGIN_INFO['import_name'], 75 PLUGIN_INFO[C.PI_IMPORT_NAME],
71 cid, 76 cid,
72 mime_type, 77 data_elt.getAttribute('type'),
73 max_age) as f: 78 max_age) as f:
74 79
75 file_path = f.name 80 file_path = f.name
76 f.write(base64.b64decode(str(data_elt))) 81 f.write(base64.b64decode(str(data_elt)))
77 82
78 return file_path 83 return file_path
79 84
80 def getHandler(self, client): 85 def getHandler(self, client):
81 return XEP_0231_handler() 86 return XEP_0231_handler()
82 87
83 def _dataCb(self, iq_elt, client, img_elt, cid): 88 def _requestCb(self, iq_elt, cache, cid):
84 for data_elt in iq_elt.elements(NS_BOB, u'data'): 89 for data_elt in iq_elt.elements(NS_BOB, u'data'):
85 if data_elt.getAttribute('cid') == cid: 90 if data_elt.getAttribute('cid') == cid:
86 file_path = self.dumpData(client, data_elt, cid) 91 file_path = self.dumpData(cache, data_elt, cid)
87 img_elt[u'src'] = u'file://{}'.format(file_path) 92 return file_path
88 break
89 else:
90 log.warning(u"invalid data stanza received, requested cid was not found:\n{iq_elt}\nrequested cid: {cid}".format(
91 iq_elt = iq_elt,
92 cid = cid
93 ))
94 93
95 def _dataEb(self, iq_elt): 94 log.warning(u"invalid data stanza received, requested cid was not found:\n{iq_elt}\nrequested cid: {cid}".format(
96 log.warning(u"Can't get requested data:\n{iq_elt}".format(iq_elt=iq_elt)) 95 iq_elt = iq_elt,
96 cid = cid
97 ))
98 raise failure.Failure(exceptions.DataError("missing data"))
99
100 def _requestEb(self, failure_):
101 """Log the error and continue errback chain"""
102 log.warning(u"Can't get requested data:\n{reason}".format(reason=failure_))
103 return failure_
104
105 def requestData(self, client, to_jid, cid, cache=None):
106 """Request data if we don't have it in cache
107
108 @param to_jid(jid.JID): jid to request the data to
109 @param cid(unicode): content id
110 @param cache(memory.cache.Cache, None): cache to use
111 client.cache will be used if None
112 """
113 if cache is None:
114 cache = client.cache
115 iq_elt = client.IQ('get')
116 iq_elt['to'] = to_jid.full()
117 data_elt = iq_elt.addElement((NS_BOB, 'data'))
118 data_elt['cid'] = cid
119 d = iq_elt.send()
120 d.addCallback(self._requestCb, cache, cid)
121 d.addErrback(self._requestEb)
122
123 def _setImgEltSrc(self, path, img_elt):
124 img_elt[u'src'] = u'file://{}'.format(path)
97 125
98 def XHTMLTrigger(self, client, message_elt, body_elt, lang, treat_d): 126 def XHTMLTrigger(self, client, message_elt, body_elt, lang, treat_d):
99 for img_elt in xml_tools.findAll(body_elt, C.NS_XHTML, u'img'): 127 for img_elt in xml_tools.findAll(body_elt, C.NS_XHTML, u'img'):
100 source = img_elt.getAttribute(u'src','') 128 source = img_elt.getAttribute(u'src','')
101 if source.startswith(u'cid:'): 129 if source.startswith(u'cid:'):
107 continue 135 continue
108 else: 136 else:
109 # image is not in cache, is it given locally? 137 # image is not in cache, is it given locally?
110 for data_elt in message_elt.elements(NS_BOB, u'data'): 138 for data_elt in message_elt.elements(NS_BOB, u'data'):
111 if data_elt.getAttribute('cid') == cid: 139 if data_elt.getAttribute('cid') == cid:
112 file_path = self.dumpData(data_elt, cid) 140 file_path = self.dumpData(client.cache, data_elt, cid)
113 img_elt[u'src'] = u'file://{}'.format(file_path) 141 img_elt[u'src'] = u'file://{}'.format(file_path)
114 break 142 break
115 else: 143 else:
116 # cid not found locally, we need to request it 144 # cid not found locally, we need to request it
117 # so we use the deferred 145 # so we use the deferred
118 iq_elt = client.IQ('get') 146 d = self.requestData(client, jid.JID(message_elt['from']), cid)
119 iq_elt['to'] = message_elt['from'] 147 d.addCallback(partial(self._setImgEltSrc, img_elt=img_elt))
120 data_elt = iq_elt.addElement((NS_BOB, 'data'))
121 data_elt['cid'] = cid
122 d = iq_elt.send()
123 d.addCallback(self._dataCb, client, img_elt, cid)
124 d.addErrback(self._dataEb)
125 treat_d.addCallback(lambda dummy: d) 148 treat_d.addCallback(lambda dummy: d)
126 149
127 150
128 class XEP_0231_handler(XMPPHandler): 151 class XEP_0231_handler(xmlstream.XMPPHandler):
129 implements(iwokkel.IDisco) 152 implements(iwokkel.IDisco)
130 153
131 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): 154 def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
132 return [disco.DiscoFeature(NS_BOB)] 155 return [disco.DiscoFeature(NS_BOB)]
133 156