comparison src/plugins/plugin_xep_0231.py @ 2110:2d633b3c923d

plugin XEP-0231: Bits of Binary first draft: The current implementation only retrieve data from Bits of Binary, it's not used yet for sending. When an XHTML-IM message is received (XEP-0071), all <img> are checked and if a cid scheme is found in src attribute the file is retrieved and the src is changed with cached file.
author Goffi <goffi@goffi.org>
date Thu, 05 Jan 2017 20:29:05 +0100
parents
children 1d3f73e065e1
comparison
equal deleted inserted replaced
2109:85f3e12e984d 2110:2d633b3c923d
1 #!/usr/bin/env python2
2 # -*- coding: utf-8 -*-
3
4 # SAT plugin for Jingle File Transfer (XEP-0231)
5 # Copyright (C) 2009-2016 Jérôme Poisson (goffi@goffi.org)
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
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/>.
19
20 from sat.core.i18n import _
21 from sat.core.constants import Const as C
22 from sat.core.log import getLogger
23 log = getLogger(__name__)
24 from sat.tools import xml_tools
25 from wokkel import disco, iwokkel
26 from zope.interface import implements
27 from twisted.words.protocols.jabber.xmlstream import XMPPHandler
28 import base64
29
30
31 PLUGIN_INFO = {
32 "name": "Bits of Binary",
33 "import_name": "XEP-0231",
34 "type": "XEP",
35 "protocols": ["XEP-0231"],
36 "dependencies": ["XEP-0071"],
37 "main": "XEP_0231",
38 "handler": "yes",
39 "description": _("""Implementation of bits of binary (used for small images/files)""")
40 }
41
42 NS_BOB = u'urn:xmpp:bob'
43
44
45 class XEP_0231(object):
46
47 def __init__(self, host):
48 log.info(_(u"plugin Bits of Binary initialization"))
49 self.host = host
50 host.trigger.add("xhtml_post_treat", self.XHTMLTrigger)
51
52 def dumpData(self, client, data_elt, cid):
53 """save file encoded in data_elt to cache
54
55 @param data_elt(domish.Element): <data> as in XEP-0231
56 @param cid(unicode): content-id
57 @return(unicode): full path to dumped file
58 """
59 # FIXME: is it needed to use a separate thread?
60 # probably not with the little data expected with BoB
61 mime_type = data_elt.getAttribute('type','')
62
63 try:
64 max_age = int(data_elt['max-age'])
65 except (KeyError, ValueError):
66 log.warning(u'invalid max-age found')
67 max_age = None
68
69 with client.cache.cacheData(
70 PLUGIN_INFO['import_name'],
71 cid,
72 mime_type,
73 max_age) as f:
74
75 file_path = f.name
76 f.write(base64.b64decode(str(data_elt)))
77
78 return file_path
79
80 def getHandler(self, profile):
81 return XEP_0231_handler()
82
83 def _dataCb(self, iq_elt, client, img_elt, cid):
84 for data_elt in iq_elt.elements(NS_BOB, u'data'):
85 if data_elt.getAttribute('cid') == cid:
86 file_path = self.dumpData(client, data_elt, cid)
87 img_elt[u'src'] = u'file://{}'.format(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
95 def _dataEb(self, iq_elt):
96 log.warning(u"Can't get requested data:\n{iq_elt}".format(iq_elt=iq_elt))
97
98 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'):
100 source = img_elt.getAttribute(u'src','')
101 if source.startswith(u'cid:'):
102 cid = source[4:]
103 file_path = client.cache.getFilePath(cid)
104 if file_path is not None:
105 # image is in cache, we change change the url
106 img_elt[u'src'] = u'file://{}'.format(file_path)
107 continue
108 else:
109 # image is not in cache, is it given locally?
110 for data_elt in message_elt.elements(NS_BOB, u'data'):
111 if data_elt.getAttribute('cid') == cid:
112 file_path = self.dumpData(data_elt, cid)
113 img_elt[u'src'] = u'file://{}'.format(file_path)
114 break
115 else:
116 # cid not found locally, we need to request it
117 # so we use the deferred
118 iq_elt = client.IQ('get')
119 iq_elt['to'] = message_elt['from']
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)
126
127
128 class XEP_0231_handler(XMPPHandler):
129 implements(iwokkel.IDisco)
130
131 def getDiscoInfo(self, requestor, target, nodeIdentifier=''):
132 return [disco.DiscoFeature(NS_BOB)]
133
134 def getDiscoItems(self, requestor, target, nodeIdentifier=''):
135 return []