summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/api/plugins/xep_0363.rst6
-rw-r--r--slixmpp/plugins/xep_0363/__init__.py9
-rw-r--r--slixmpp/plugins/xep_0363/http_upload.py109
3 files changed, 91 insertions, 33 deletions
diff --git a/docs/api/plugins/xep_0363.rst b/docs/api/plugins/xep_0363.rst
index ebbfdba1..4bbf95fa 100644
--- a/docs/api/plugins/xep_0363.rst
+++ b/docs/api/plugins/xep_0363.rst
@@ -8,6 +8,12 @@ XEP-0363: HTTP File Upload
:members:
:exclude-members: session_bind, plugin_init, plugin_end
+.. autoclass:: UploadServiceNotFound
+
+.. autoclass:: FileTooBig
+
+.. autoclass:: HTTPError
+
Stanza elements
---------------
diff --git a/slixmpp/plugins/xep_0363/__init__.py b/slixmpp/plugins/xep_0363/__init__.py
index 0ed1d7c8..f693eb92 100644
--- a/slixmpp/plugins/xep_0363/__init__.py
+++ b/slixmpp/plugins/xep_0363/__init__.py
@@ -1,4 +1,3 @@
-
# slixmpp: The Slick XMPP Library
# Copyright (C) 2018 Emmanuel Gil Peyrot
# This file is part of slixmpp.
@@ -6,6 +5,12 @@
from slixmpp.plugins.base import register_plugin
from slixmpp.plugins.xep_0363.stanza import Request, Slot, Put, Get, Header
-from slixmpp.plugins.xep_0363.http_upload import XEP_0363
+from slixmpp.plugins.xep_0363.http_upload import (
+ XEP_0363,
+ UploadServiceNotFound,
+ FileTooBig,
+ HTTPError,
+ FileUploadError,
+)
register_plugin(XEP_0363)
diff --git a/slixmpp/plugins/xep_0363/http_upload.py b/slixmpp/plugins/xep_0363/http_upload.py
index 04b066cd..bae3ee7d 100644
--- a/slixmpp/plugins/xep_0363/http_upload.py
+++ b/slixmpp/plugins/xep_0363/http_upload.py
@@ -1,18 +1,21 @@
-"""
- slixmpp: The Slick XMPP Library
- Copyright (C) 2018 Emmanuel Gil Peyrot
- This file is part of slixmpp.
-
- See the file LICENSE for copying permission.
-"""
+# slixmpp: The Slick XMPP Library
+# Copyright (C) 2018 Emmanuel Gil Peyrot
+# This file is part of slixmpp.
+# See the file LICENSE for copying permission.
import logging
import os.path
from aiohttp import ClientSession
+from asyncio import Future
from mimetypes import guess_type
+from typing import (
+ Optional,
+ IO,
+)
-from slixmpp import Iq, __version__
+from slixmpp import JID, __version__
+from slixmpp.stanza import Iq
from slixmpp.plugins import BasePlugin
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.xmlstream.handler import Callback
@@ -25,19 +28,39 @@ class FileUploadError(Exception):
pass
class UploadServiceNotFound(FileUploadError):
- pass
+ """
+ Raised if no upload service can be found.
+ """
class FileTooBig(FileUploadError):
+ """
+ Raised if the file size is above advertised server limits.
+
+ args:
+
+ - size of the file
+ - max file size allowed
+ """
def __str__(self):
return 'File size too large: {} (max: {} bytes)' \
.format(self.args[0], self.args[1])
class HTTPError(FileUploadError):
+ """
+ Raised when we receive an HTTP error response during upload.
+
+ args:
+
+ - HTTP Error code
+ - Content of the HTTP response
+ """
def __str__(self):
return 'Could not upload file: %d (%s)' % (self.args[0], self.args[1])
class XEP_0363(BasePlugin):
- ''' This plugin only supports Python 3.5+ '''
+ """
+ XEP-0363: HTTP File Upload
+ """
name = 'xep_0363'
description = 'XEP-0363: HTTP File Upload'
@@ -62,9 +85,7 @@ class XEP_0363(BasePlugin):
self._handle_request))
def plugin_end(self):
- self._http_session.close()
self.xmpp.remove_handler('HTTP Upload Request')
- self.xmpp.remove_handler('HTTP Upload Slot')
self.xmpp['xep_0030'].del_feature(feature=Request.namespace)
def session_bind(self, jid):
@@ -73,9 +94,14 @@ class XEP_0363(BasePlugin):
def _handle_request(self, iq):
self.xmpp.event('http_upload_request', iq)
- async def find_upload_service(self, domain=None, timeout=None):
+ async def find_upload_service(self, domain: Optional[JID] = None, **iqkwargs) -> Optional[Iq]:
+ """Find an upload service on a domain (our own by default).
+
+ :param domain: Domain to disco to find a service.
+ """
results = await self.xmpp['xep_0030'].get_info_from_domain(
- domain=domain, timeout=timeout)
+ domain=domain, **iqkwargs
+ )
candidates = []
for info in results:
@@ -87,26 +113,49 @@ class XEP_0363(BasePlugin):
if feature == Request.namespace:
return info
- def request_slot(self, jid, filename, size, content_type=None, ifrom=None,
- timeout=None, callback=None, timeout_callback=None):
- iq = self.xmpp.Iq()
- iq['to'] = jid
- iq['from'] = ifrom
- iq['type'] = 'get'
+ def request_slot(self, jid: JID, filename: str, size: int,
+ content_type: Optional[str] = None, *,
+ ifrom: Optional[JID] = None, **iqkwargs) -> Future:
+ """Request an HTTP upload slot from a service.
+
+ :param jid: Service to request the slot from.
+ :param filename: Name of the file that will be uploaded.
+ :param size: size of the file in bytes.
+ :param content_type: Type of the file that will be uploaded.
+ """
+ iq = self.xmpp.make_iq_get(ito=jid, ifrom=ifrom)
request = iq['http_upload_request']
request['filename'] = filename
request['size'] = str(size)
request['content-type'] = content_type or self.default_content_type
- return iq.send(timeout=timeout, callback=callback,
- timeout_callback=timeout_callback)
-
- async def upload_file(self, filename, size=None, content_type=None, *,
- input_file=None, ifrom=None, domain=None, timeout=None,
- callback=None, timeout_callback=None):
- ''' Helper function which does all of the uploading process. '''
+ return iq.send(**iqkwargs)
+
+ async def upload_file(self, filename: str, size: Optional[int] = None,
+ content_type: Optional[str] = None, *,
+ input_file: Optional[IO[bytes]]=None,
+ domain: Optional[JID] = None,
+ **iqkwargs) -> str:
+ '''Helper function which does all of the uploading discovery and
+ process.
+
+ :param filename: Path to the file to upload (or only the name if
+ ``input_file`` is provided.
+ :param size: size of the file in bytes.
+ :param content_type: Type of the file that will be uploaded.
+ :param input_file: Binary file stream on the file.
+ :param domain: Domain to query to find an HTTP upload service.
+ :raises .UploadServiceNotFound: If slixmpp is unable to find an
+ an available upload service.
+ :raises .FileTooBig: If the filesize is above what is accepted by
+ the service.
+ :raises .HTTPError: If there is an error in the HTTP operation.
+ :returns: The URL of the uploaded file.
+ '''
+ timeout = iqkwargs.get('timeout', None)
if self.upload_service is None:
info_iq = await self.find_upload_service(
- domain=domain, timeout=timeout)
+ domain=domain, **iqkwargs
+ )
if info_iq is None:
raise UploadServiceNotFound()
self.upload_service = info_iq['from']
@@ -137,9 +186,7 @@ class XEP_0363(BasePlugin):
basename = os.path.basename(filename)
slot_iq = await self.request_slot(self.upload_service, basename, size,
- content_type, ifrom, timeout,
- callback=callback,
- timeout_callback=timeout_callback)
+ content_type, **iqkwargs)
slot = slot_iq['http_upload_slot']
headers = {