mirror of
https://github.com/moparisthebest/HttpUploadComponent
synced 2024-11-27 11:22:18 -05:00
Merge branch 'master' of github.com:siacs/HttpUploadComponent
This commit is contained in:
commit
6d8993145b
54
README.md
54
README.md
@ -1,30 +1,52 @@
|
|||||||
# HttpUploadComponent
|
# HttpUploadComponent
|
||||||
The HttpUploadComponent is a plugin extension to your XMPP server that allows users to upload files to a HTTP host and eventually share the link to those files.
|
|
||||||
It runs as a stand alone process on the same host as your XMPP server and connects to that server using the [Jabber Component Protocol](http://xmpp.org/extensions/xep-0114.html).
|
|
||||||
|
|
||||||
A detailed introduction into the necessity of such a component and the simple protocol can be found on the [XMPP Standards email list](http://mail.jabber.org/pipermail/standards/2015-June/029969.html).
|
The HttpUploadComponent is a plugin extension to your XMPP server that allows
|
||||||
|
users to upload files to a HTTP host and eventually share the link to those
|
||||||
|
files.
|
||||||
|
|
||||||
###Configuration
|
It runs as a stand alone process on the same host as your XMPP server and
|
||||||
Configuration happens in config.yml and is pretty straight forward.
|
connects to that server using the [Jabber Component
|
||||||
|
Protocol](http://xmpp.org/extensions/xep-0114.html).
|
||||||
|
|
||||||
```jid``` and ```secret``` have to match the corresponding entries in your XMPP server config (Refer to the documentation of your server).
|
A detailed introduction into the necessity of such a component and the simple
|
||||||
|
protocol can be found on the [XMPP Standards email
|
||||||
|
list](http://mail.jabber.org/pipermail/standards/2015-June/029969.html).
|
||||||
|
|
||||||
```whitelist``` should contain a list of domains whos JIDs are allowed to upload files. Remove the entry if you want to allow everyone (not really recommended).
|
### Configuration
|
||||||
|
|
||||||
```get_url``` and ```put_url``` are prefixes to the URLs. The put_url is usually a combination of your hostname and the port (Port can be ommited if HTTP default ports are being used) The GET URL can use a different host if you want to serve files using a standard nginx or another HTTP server that might be more suitable for serving files than a python script.
|
Configuration happens in `config.yml` and is pretty straight forward.
|
||||||
|
|
||||||
For security purposes you should put the python script behind an HTTPS proxy or stunnel (remember to adapt the URLs).
|
```jid``` and ```secret``` have to match the corresponding entries in your XMPP
|
||||||
For quicker results you can also use the build in TLS encryption by setting the ```keyfile``` and ```certfile``` in the config file.
|
server config (Refer to the documentation of your server).
|
||||||
|
|
||||||
|
```whitelist``` should contain a list of domains whos JIDs are allowed to
|
||||||
|
upload files. Remove the entry if you want to allow everyone (not really
|
||||||
|
recommended).
|
||||||
|
|
||||||
For the configuration on the XMPP server side have a look into the contrib directory or check the server documentation.
|
```get_url``` and ```put_url``` are prefixes to the URLs. The put_url is
|
||||||
|
usually a combination of your hostname and the port (Port can be ommited if
|
||||||
|
HTTP default ports are being used) The GET URL can use a different host if you
|
||||||
|
want to serve files using a standard nginx or another HTTP server that might be
|
||||||
|
more suitable for serving files than a python script.
|
||||||
|
|
||||||
|
For security purposes you should put the python script behind an HTTPS proxy or
|
||||||
|
stunnel (remember to adapt the URLs). For quicker results you can also use the
|
||||||
|
built in TLS encryption by setting ```keyfile``` and ```certfile``` in the
|
||||||
|
config file.
|
||||||
|
|
||||||
|
For the configuration of the XMPP server side have a look into the contrib
|
||||||
|
directory or check the server documentation.
|
||||||
|
|
||||||
|
### Run
|
||||||
|
|
||||||
###Run
|
|
||||||
Running the component is as easy as invoking ```python server.py```
|
Running the component is as easy as invoking ```python server.py```
|
||||||
|
|
||||||
Some (unoffical) init scripts can be found in the contrib directory.
|
Some (unoffical) init scripts can be found in the contrib directory. Feel free
|
||||||
|
to write your own init scripts if necessary and contribute them back by
|
||||||
|
creating a pull request.
|
||||||
|
|
||||||
Feel free to write your own init scripts if necessary and contribute them back by creating a pull request.
|
### Clients
|
||||||
|
|
||||||
###Clients
|
Currently the only client with build in support is
|
||||||
Currently the only client with build in support is [Conversations](http://conversations.im) where it is being used to send files to Multi User Conferences and to multiple resources in 1 on 1 chats.
|
[Conversations](http://conversations.im) where it is being used to send files
|
||||||
|
to Multi User Conferences and to multiple resources in 1 on 1 chats.
|
||||||
|
80
server.py
80
server.py
@ -1,18 +1,24 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import yaml
|
import argparse
|
||||||
import sys
|
import errno
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
import mimetypes
|
||||||
|
import os
|
||||||
|
import random
|
||||||
import shutil
|
import shutil
|
||||||
import signal
|
import signal
|
||||||
import logging
|
import sleekxmpp
|
||||||
import string
|
|
||||||
import hashlib
|
|
||||||
import random
|
|
||||||
import os
|
|
||||||
import ssl
|
import ssl
|
||||||
import argparse
|
import string
|
||||||
from threading import Thread
|
import sys
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from sleekxmpp.componentxmpp import ComponentXMPP
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
|
from threading import Thread
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Python 3
|
# Python 3
|
||||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||||
@ -21,9 +27,20 @@ except ImportError:
|
|||||||
# Python 2
|
# Python 2
|
||||||
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
|
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
|
||||||
from SocketServer import ThreadingMixIn
|
from SocketServer import ThreadingMixIn
|
||||||
FileNotFoundError = IOError
|
|
||||||
import sleekxmpp
|
try:
|
||||||
from sleekxmpp.componentxmpp import ComponentXMPP
|
FileNotFoundError
|
||||||
|
except NameError:
|
||||||
|
# Python 2
|
||||||
|
class FileNotFoundError(IOError):
|
||||||
|
def __init__(self, message=None, *args):
|
||||||
|
super(FileNotFoundError, self).__init__(args)
|
||||||
|
self.message = message
|
||||||
|
self.errno = errno.ENOENT
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.message or os.strerror(self.errno)
|
||||||
|
|
||||||
|
|
||||||
LOGLEVEL=logging.DEBUG
|
LOGLEVEL=logging.DEBUG
|
||||||
|
|
||||||
@ -103,13 +120,12 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||||||
filename = os.path.join(config['storage_path'], path)
|
filename = os.path.join(config['storage_path'], path)
|
||||||
os.makedirs(os.path.dirname(filename))
|
os.makedirs(os.path.dirname(filename))
|
||||||
remaining = length
|
remaining = length
|
||||||
f = open(filename,'wb')
|
with open(filename,'wb') as f:
|
||||||
data = self.rfile.read(min(4096,remaining))
|
|
||||||
while data and remaining >= 0:
|
|
||||||
remaining -= len(data)
|
|
||||||
f.write(data)
|
|
||||||
data = self.rfile.read(min(4096,remaining))
|
data = self.rfile.read(min(4096,remaining))
|
||||||
f.close()
|
while data and remaining >= 0:
|
||||||
|
remaining -= len(data)
|
||||||
|
f.write(data)
|
||||||
|
data = self.rfile.read(min(4096,remaining))
|
||||||
self.send_response(200,'ok')
|
self.send_response(200,'ok')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
else:
|
else:
|
||||||
@ -117,7 +133,7 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||||||
self.send_response(403,'invalid slot')
|
self.send_response(403,'invalid slot')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
def do_GET(self):
|
def do_GET(self, body=True):
|
||||||
global config
|
global config
|
||||||
path = normalize_path(self.path[1:])
|
path = normalize_path(self.path[1:])
|
||||||
slashcount = path.count('/')
|
slashcount = path.count('/')
|
||||||
@ -130,34 +146,22 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||||||
try:
|
try:
|
||||||
with open(filename,'rb') as f:
|
with open(filename,'rb') as f:
|
||||||
self.send_response(200)
|
self.send_response(200)
|
||||||
self.send_header("Content-Type", 'application/octet-stream')
|
mime, _ = mimetypes.guess_type(filename)
|
||||||
|
if mime is None:
|
||||||
|
mime = 'application/octet-stream'
|
||||||
|
self.send_header("Content-Type", mime)
|
||||||
self.send_header("Content-Disposition", 'attachment; filename="{}"'.format(os.path.basename(filename)))
|
self.send_header("Content-Disposition", 'attachment; filename="{}"'.format(os.path.basename(filename)))
|
||||||
fs = os.fstat(f.fileno())
|
fs = os.fstat(f.fileno())
|
||||||
self.send_header("Content-Length", str(fs.st_size))
|
self.send_header("Content-Length", str(fs.st_size))
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
shutil.copyfileobj(f, self.wfile)
|
if body:
|
||||||
|
shutil.copyfileobj(f, self.wfile)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
self.send_response(404,'file not found')
|
self.send_response(404,'file not found')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
|
||||||
def do_HEAD(self):
|
def do_HEAD(self):
|
||||||
global config
|
self.do_GET(body=False)
|
||||||
path = normalize_path(self.path[1:])
|
|
||||||
slashcount = path.count('/')
|
|
||||||
if path[0] in ('/', '\\') or slashcount < 1 or slashcount > 2:
|
|
||||||
self.send_response(404,'file not found')
|
|
||||||
self.end_headers()
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
filename = os.path.join(config['storage_path'], path)
|
|
||||||
self.send_response(200,'OK')
|
|
||||||
self.send_header("Content-Type", 'application/octet-stream')
|
|
||||||
self.send_header("Content-Disposition", 'attachment; filename="{}"'.format(os.path.basename(filename)))
|
|
||||||
self.send_header("Content-Length",str(os.path.getsize(filename)))
|
|
||||||
self.end_headers()
|
|
||||||
except FileNotFoundError:
|
|
||||||
self.send_response(404,'file not found')
|
|
||||||
self.end_headers()
|
|
||||||
|
|
||||||
|
|
||||||
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
|
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
|
||||||
|
Loading…
Reference in New Issue
Block a user