summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorian Baumann <derflob@derflob.de>2016-07-13 23:03:11 +1200
committerFlorian Baumann <derflob@derflob.de>2016-07-13 23:03:11 +1200
commit60ac83b68723c8ebe7de07402f410171f6d7e086 (patch)
treea53563cf6f34eccdfd5857a4625d51f4e4e68575
downloadsleek-0363-60ac83b68723c8ebe7de07402f410171f6d7e086.tar.gz
sleek-0363-60ac83b68723c8ebe7de07402f410171f6d7e086.tar.bz2
XEP-0363: HTTP File Upload
-rw-r--r--__init__.py7
-rw-r--r--stanza.py17
-rw-r--r--xep_0363.py89
3 files changed, 113 insertions, 0 deletions
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..e0dac18
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,7 @@
+from sleekxmpp.plugins.base import register_plugin
+from .xep_0363 import XEP_0363
+from .stanza import SlotRequest, UploadSlot
+
+register_plugin(XEP_0363)
+
+xep_0363 = XEP_0363
diff --git a/stanza.py b/stanza.py
new file mode 100644
index 0000000..367b463
--- /dev/null
+++ b/stanza.py
@@ -0,0 +1,17 @@
+from sleekxmpp.xmlstream.stanzabase import ElementBase
+from sleekxmpp.stanza import Iq
+
+class SlotRequest(ElementBase):
+ namespace = 'urn:xmpp:http:upload'
+ name = 'request'
+ plugin_attrib = 'slot_request'
+ interfaces = set(['filename', 'size', 'content-type'])
+ sub_interfaces = interfaces
+
+
+class UploadSlot(ElementBase):
+ namespace = 'urn:xmpp:http:upload'
+ name = 'slot'
+ plugin_attrib = 'slot'
+ interfaces = set(['put', 'get'])
+ sub_interfaces = interfaces
diff --git a/xep_0363.py b/xep_0363.py
new file mode 100644
index 0000000..0be9ce4
--- /dev/null
+++ b/xep_0363.py
@@ -0,0 +1,89 @@
+import logging
+import requests
+
+from sleekxmpp import register_stanza_plugin
+from sleekxmpp.stanza import Iq
+from sleekxmpp.plugins import BasePlugin, register_plugin
+from sleekxmpp.xmlstream.handler import Callback
+from sleekxmpp.xmlstream.matcher import StanzaPath
+from sleekxmpp.plugins.xep_0363 import stanza
+
+log = logging.getLogger(__name__)
+
+class HTTPUploadServiceError(Exception):
+ pass
+
+class XEP_0363(BasePlugin):
+
+ name = 'xep_0363'
+ description = 'XEP-0363: HTTP File Upload'
+ stanza = stanza
+
+ def plugin_init(self):
+ register_stanza_plugin(Iq, stanza.SlotRequest)
+ register_stanza_plugin(Iq, stanza.UploadSlot)
+
+ self.xmpp.register_handler(
+ Callback('HTTP Upload Slot',
+ StanzaPath('iq/slot'), self._handle_upload_slot))
+
+ self._waiting_files = {}
+
+ def plugin_end(self):
+ self.xmpp.unregister_handler('HTTP Upload Slot')
+
+ def _handle_upload_slot(self, iq):
+ fh = self._waiting_files.pop(iq['id'], None)
+ if (iq['type'] == 'result'):
+ log.debug("Got UploadSlot for {} ({}) - get: {} put: {}".format(
+ self._waiting_files[iq['id']], iq['id'], iq['slot']['get'], iq['slot']['put']))
+
+ if (fh):
+ p = requests.put(url=iq['slot']['put'], data=fh)
+ if (p.ok):
+ self.xmpp.event('http_upload::{}::finished'.format(iq['id']), iq['slot']['get'])
+
+ elif (iq['type'] == 'error'):
+ self.xmpp.event('http_upload::{}::failed'.format(iq['id']), iq)
+
+ def _discover_upload_service(self):
+ disco = self.xmpp['xep_0030']
+
+ iq = disco.get_items(jid=self.xmpp.boundjid.host)
+ items = iq['disco_items'].get_items()
+
+ for (item, _, _) in items:
+ if disco.supports(jid=item, feature=stanza.UploadSlot.namespace):
+ return item
+
+ return None
+
+ def _check_file_size(self, upload_service, file_size):
+ disco = self.xmpp['xep_0030']
+
+ return True
+
+ def start_upload(self, filename, handle, size, mime=None, upload_service=None):
+ if (not upload_service):
+ upload_service = self._discover_upload_service()
+
+ if (not upload_service):
+ raise HTTPUploadServiceError("Server ({}) does not support HTTP Upload".format(self.xmpp.boundjid.host))
+
+ if (not self._check_file_size(upload_service, size)):
+ raise HTTPUploadServiceError("HTTP Upload Service ({}) not suitable, file size to large".format(upload_service))
+
+ req = self.xmpp.make_iq_get(ito=upload_service)
+
+ req['slot_request']['filename'] = filename
+ req['slot_request']['size'] = str(size)
+ req['slot_request']['content-type'] = mime
+
+ log.debug("Requesting UploadSlot for {} ({} Bytes, {})".format(
+ req['slot_request']['filename'], req['slot_request']['size'], req['slot_request']['content-type']))
+
+ self._waiting_files[req['id']] = handle
+
+ req.send(block=False)
+
+ return req['id']