|
|
@ -7,10 +7,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import print_function
|
|
|
|
from __future__ import print_function
|
|
|
|
|
|
|
|
|
|
|
|
__version__ = '0.5.4'
|
|
|
|
__version__ = '0.5.3'
|
|
|
|
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import argparse
|
|
|
|
import base64
|
|
|
|
import base64
|
|
|
|
|
|
|
|
import cgi
|
|
|
|
import datetime
|
|
|
|
import datetime
|
|
|
|
import io
|
|
|
|
import io
|
|
|
|
import mimetypes
|
|
|
|
import mimetypes
|
|
|
@ -20,9 +21,7 @@ import select
|
|
|
|
import socket
|
|
|
|
import socket
|
|
|
|
from subprocess import Popen, PIPE
|
|
|
|
from subprocess import Popen, PIPE
|
|
|
|
import sys
|
|
|
|
import sys
|
|
|
|
import tempfile
|
|
|
|
|
|
|
|
import time
|
|
|
|
import time
|
|
|
|
import warnings
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# fix imports for python2/python3
|
|
|
|
# fix imports for python2/python3
|
|
|
|
try:
|
|
|
|
try:
|
|
|
@ -43,18 +42,11 @@ try:
|
|
|
|
except ImportError:
|
|
|
|
except ImportError:
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
with warnings.catch_warnings():
|
|
|
|
|
|
|
|
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
|
|
|
|
|
|
# scheduled for removal in python3.13, used for FieldStorage
|
|
|
|
|
|
|
|
import cgi
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def getDateStrNow():
|
|
|
|
def getDateStrNow():
|
|
|
|
""" Get the current time formatted for HTTP header """
|
|
|
|
""" Get the current time formatted for HTTP header """
|
|
|
|
now = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
|
|
|
|
now = datetime.datetime.fromtimestamp(time.mktime(time.gmtime()))
|
|
|
|
return now.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
|
|
|
return now.strftime("%a, %d %b %Y %H:%M:%S GMT")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FileBaseHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|
|
|
class FileBaseHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|
|
|
fileName = None
|
|
|
|
fileName = None
|
|
|
|
blockSize = 1024 * 1024
|
|
|
|
blockSize = 1024 * 1024
|
|
|
@ -149,7 +141,7 @@ class FileBaseHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|
|
|
# now we can wind the file *brrrrrr*
|
|
|
|
# now we can wind the file *brrrrrr*
|
|
|
|
myfile.seek(fromto[0])
|
|
|
|
myfile.seek(fromto[0])
|
|
|
|
|
|
|
|
|
|
|
|
if fromto is not None:
|
|
|
|
if fromto != None:
|
|
|
|
self.send_response(216)
|
|
|
|
self.send_response(216)
|
|
|
|
self.send_header('Content-Range', 'bytes %d-%d/%d' % (fromto[0], fromto[1], fileLength))
|
|
|
|
self.send_header('Content-Range', 'bytes %d-%d/%d' % (fromto[0], fromto[1], fileLength))
|
|
|
|
fileLength = fromto[1] - fromto[0] + 1
|
|
|
|
fileLength = fromto[1] - fromto[0] + 1
|
|
|
@ -254,7 +246,7 @@ class TarFileHandler(FileBaseHandler):
|
|
|
|
# give the process a short time to find out if it can
|
|
|
|
# give the process a short time to find out if it can
|
|
|
|
# pack/compress the file
|
|
|
|
# pack/compress the file
|
|
|
|
time.sleep(0.05)
|
|
|
|
time.sleep(0.05)
|
|
|
|
if tarCmd.poll() is not None and tarCmd.poll() != 0:
|
|
|
|
if tarCmd.poll() != None and tarCmd.poll() != 0:
|
|
|
|
# something went wrong
|
|
|
|
# something went wrong
|
|
|
|
print("Error while compressing '%s'. Aborting request." % self.target)
|
|
|
|
print("Error while compressing '%s'. Aborting request." % self.target)
|
|
|
|
self.send_response(500)
|
|
|
|
self.send_response(500)
|
|
|
@ -424,7 +416,7 @@ class DirListingHandler(FileBaseHandler):
|
|
|
|
</tr>
|
|
|
|
</tr>
|
|
|
|
</thead>
|
|
|
|
</thead>
|
|
|
|
<tbody>
|
|
|
|
<tbody>
|
|
|
|
""" % {'path': os.path.normpath(unquote(self.path))} # noqa: E501
|
|
|
|
""" % {'path': os.path.normpath(unquote(self.path))}
|
|
|
|
footer = """</tbody></table></div>
|
|
|
|
footer = """</tbody></table></div>
|
|
|
|
<div class="footer"><a href="http://seba-geek.de/stuff/servefile/">servefile %(version)s</a></div>
|
|
|
|
<div class="footer"><a href="http://seba-geek.de/stuff/servefile/">servefile %(version)s</a></div>
|
|
|
|
<script>
|
|
|
|
<script>
|
|
|
@ -532,7 +524,10 @@ class DirListingHandler(FileBaseHandler):
|
|
|
|
target_items.append((item, itemPath, stat))
|
|
|
|
target_items.append((item, itemPath, stat))
|
|
|
|
|
|
|
|
|
|
|
|
# Directories first, then files
|
|
|
|
# Directories first, then files
|
|
|
|
for (tuple_list, is_dir) in ((dir_items, True), (file_items, False)):
|
|
|
|
for (tuple_list, is_dir) in (
|
|
|
|
|
|
|
|
(dir_items, True),
|
|
|
|
|
|
|
|
(file_items, False),
|
|
|
|
|
|
|
|
):
|
|
|
|
for (item, itemPath, stat) in tuple_list:
|
|
|
|
for (item, itemPath, stat) in tuple_list:
|
|
|
|
self._appendToListing(content, item, itemPath, stat, is_dir=is_dir)
|
|
|
|
self._appendToListing(content, item, itemPath, stat, is_dir=is_dir)
|
|
|
|
|
|
|
|
|
|
|
@ -614,25 +609,8 @@ class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
|
|
|
|
# create FieldStorage object for multipart parsing
|
|
|
|
# create FieldStorage object for multipart parsing
|
|
|
|
env = os.environ
|
|
|
|
env = os.environ
|
|
|
|
env['REQUEST_METHOD'] = "POST"
|
|
|
|
env['REQUEST_METHOD'] = "POST"
|
|
|
|
targetDir = self.targetDir
|
|
|
|
fstorage = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ=env)
|
|
|
|
|
|
|
|
if not "file" in fstorage:
|
|
|
|
class CustomFieldStorage(cgi.FieldStorage):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def make_file(self, *args, **kwargs):
|
|
|
|
|
|
|
|
"""Overwritten to use a named file and the upload directory
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Python 2.7 has an unused "binary" argument while Python 3 does
|
|
|
|
|
|
|
|
not have any arguments. Python 2.7 does not have a
|
|
|
|
|
|
|
|
self._binary_file attribute.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
if sys.version_info.major == 2 or self._binary_file:
|
|
|
|
|
|
|
|
return tempfile.NamedTemporaryFile("wb+", dir=targetDir)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
return tempfile.NamedTemporaryFile(
|
|
|
|
|
|
|
|
"w+", encoding=self.encoding, newline='\n', dir=targetDir)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fstorage = CustomFieldStorage(fp=self.rfile, headers=self.headers, environ=env)
|
|
|
|
|
|
|
|
if "file" not in fstorage:
|
|
|
|
|
|
|
|
self.sendResponse(400, "No file found in request.")
|
|
|
|
self.sendResponse(400, "No file found in request.")
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
@ -641,14 +619,7 @@ class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
|
|
|
|
self.sendResponse(400, "Filename was empty or invalid")
|
|
|
|
self.sendResponse(400, "Filename was empty or invalid")
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
# put the file at the right place, send 200 afterwards
|
|
|
|
# write file down to disk, send a 200 afterwards
|
|
|
|
if getattr(fstorage["file"].file, "name", None):
|
|
|
|
|
|
|
|
# the sent file was large, so we can just hard link the temporary
|
|
|
|
|
|
|
|
# file and are done
|
|
|
|
|
|
|
|
os.link(fstorage["file"].file.name, destFileName)
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
# write file to disk. it was small enough so no temporary file was
|
|
|
|
|
|
|
|
# created
|
|
|
|
|
|
|
|
target = open(destFileName, "wb")
|
|
|
|
target = open(destFileName, "wb")
|
|
|
|
bytesLeft = length
|
|
|
|
bytesLeft = length
|
|
|
|
while bytesLeft > 0:
|
|
|
|
while bytesLeft > 0:
|
|
|
@ -706,8 +677,7 @@ class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
|
|
|
|
self.sendResponse(411, "Content-Length was invalid or not set.")
|
|
|
|
self.sendResponse(411, "Content-Length was invalid or not set.")
|
|
|
|
return -1
|
|
|
|
return -1
|
|
|
|
if self.maxUploadSize > 0 and length > self.maxUploadSize:
|
|
|
|
if self.maxUploadSize > 0 and length > self.maxUploadSize:
|
|
|
|
self.sendResponse(413, "Your file was too big! Maximum allowed size is %d byte. <a href=\"/\">back</a>" %
|
|
|
|
self.sendResponse(413, "Your file was too big! Maximum allowed size is %d byte. <a href=\"/\">back</a>" % self.maxUploadSize)
|
|
|
|
self.maxUploadSize)
|
|
|
|
|
|
|
|
return -1
|
|
|
|
return -1
|
|
|
|
return length
|
|
|
|
return length
|
|
|
|
|
|
|
|
|
|
|
@ -744,7 +714,6 @@ class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
|
|
|
|
return extraDestFileName
|
|
|
|
return extraDestFileName
|
|
|
|
# never reached
|
|
|
|
# never reached
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
|
|
|
|
class ThreadedHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
|
|
|
|
def handle_error(self, request, client_address):
|
|
|
|
def handle_error(self, request, client_address):
|
|
|
|
_, exc_value, _ = sys.exc_info()
|
|
|
|
_, exc_value, _ = sys.exc_info()
|
|
|
@ -820,7 +789,6 @@ class SecureHandler():
|
|
|
|
self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
|
|
|
|
self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
|
|
|
|
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
|
|
|
|
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ServeFileException(Exception):
|
|
|
|
class ServeFileException(Exception):
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
@ -845,8 +813,7 @@ class ServeFile():
|
|
|
|
|
|
|
|
|
|
|
|
if self.serveMode not in range(self._NUM_MODES):
|
|
|
|
if self.serveMode not in range(self._NUM_MODES):
|
|
|
|
self.serveMode = None
|
|
|
|
self.serveMode = None
|
|
|
|
raise ValueError("Unknown serve mode, needs to be MODE_SINGLE, "
|
|
|
|
raise ValueError("Unknown serve mode, needs to be MODE_SINGLE, MODE_SINGLETAR, MODE_UPLOAD or MODE_DIRLIST.")
|
|
|
|
"MODE_SINGLETAR, MODE_UPLOAD or MODE_DIRLIST.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def setIPv4(self, ipv4):
|
|
|
|
def setIPv4(self, ipv4):
|
|
|
|
""" En- or disable ipv4 """
|
|
|
|
""" En- or disable ipv4 """
|
|
|
@ -860,23 +827,23 @@ class ServeFile():
|
|
|
|
""" Get IPs from all interfaces via ip or ifconfig. """
|
|
|
|
""" Get IPs from all interfaces via ip or ifconfig. """
|
|
|
|
# ip and ifconfig sometimes are located in /sbin/
|
|
|
|
# ip and ifconfig sometimes are located in /sbin/
|
|
|
|
os.environ['PATH'] += ':/sbin:/usr/sbin'
|
|
|
|
os.environ['PATH'] += ':/sbin:/usr/sbin'
|
|
|
|
proc = Popen(r"ip addr|"
|
|
|
|
proc = Popen(r"ip addr|" + \
|
|
|
|
r"sed -n -e 's/.*inet6\{0,1\} \([0-9.a-fA-F:]\+\).*/\1/ p'|"
|
|
|
|
"sed -n -e 's/.*inet6\{0,1\} \([0-9.a-fA-F:]\+\).*/\\1/ p'|" + \
|
|
|
|
r"grep -v '^fe80\|^127.0.0.1\|^::1'",
|
|
|
|
"grep -v '^fe80\|^127.0.0.1\|^::1'", \
|
|
|
|
shell=True, stdout=PIPE, stderr=PIPE)
|
|
|
|
shell=True, stdout=PIPE, stderr=PIPE)
|
|
|
|
if proc.wait() != 0:
|
|
|
|
if proc.wait() != 0:
|
|
|
|
# ip failed somehow, falling back to ifconfig
|
|
|
|
# ip failed somehow, falling back to ifconfig
|
|
|
|
oldLang = os.environ.get("LC_ALL", None)
|
|
|
|
oldLang = os.environ.get("LC_ALL", None)
|
|
|
|
os.environ['LC_ALL'] = "C"
|
|
|
|
os.environ['LC_ALL'] = "C"
|
|
|
|
proc = Popen(r"ifconfig|"
|
|
|
|
proc = Popen(r"ifconfig|" + \
|
|
|
|
r"sed -n 's/.*inet6\{0,1\}\( addr:\)\{0,1\} \{0,1\}\([0-9a-fA-F.:]*\).*/"
|
|
|
|
"sed -n 's/.*inet6\{0,1\}\( addr:\)\{0,1\} \{0,1\}\([0-9a-fA-F.:]*\).*/" + \
|
|
|
|
r"\2/p'|"
|
|
|
|
"\\2/p'|" + \
|
|
|
|
r"grep -v '^fe80\|^127.0.0.1\|^::1'",
|
|
|
|
"grep -v '^fe80\|^127.0.0.1\|^::1'", \
|
|
|
|
shell=True, stdout=PIPE, stderr=PIPE)
|
|
|
|
shell=True, stdout=PIPE, stderr=PIPE)
|
|
|
|
if oldLang:
|
|
|
|
if oldLang:
|
|
|
|
os.environ['LC_ALL'] = oldLang
|
|
|
|
os.environ['LC_ALL'] = oldLang
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
del os.environ['LC_ALL']
|
|
|
|
del(os.environ['LC_ALL'])
|
|
|
|
if proc.wait() != 0:
|
|
|
|
if proc.wait() != 0:
|
|
|
|
# we couldn't find any ip address
|
|
|
|
# we couldn't find any ip address
|
|
|
|
proc = None
|
|
|
|
proc = None
|
|
|
@ -919,7 +886,7 @@ class ServeFile():
|
|
|
|
req = crypto.X509Req()
|
|
|
|
req = crypto.X509Req()
|
|
|
|
subj = req.get_subject()
|
|
|
|
subj = req.get_subject()
|
|
|
|
subj.CN = "127.0.0.1"
|
|
|
|
subj.CN = "127.0.0.1"
|
|
|
|
subj.O = "servefile laboratories" # noqa: E741
|
|
|
|
subj.O = "servefile laboratories"
|
|
|
|
subj.OU = "servefile"
|
|
|
|
subj.OU = "servefile"
|
|
|
|
|
|
|
|
|
|
|
|
# generate altnames
|
|
|
|
# generate altnames
|
|
|
@ -984,8 +951,7 @@ class ServeFile():
|
|
|
|
server = SecureThreadedHTTPServer(self._getCert(), self._getKey(),
|
|
|
|
server = SecureThreadedHTTPServer(self._getCert(), self._getKey(),
|
|
|
|
(listenIp, self.port), handler, bind_and_activate=False)
|
|
|
|
(listenIp, self.port), handler, bind_and_activate=False)
|
|
|
|
except SSL.Error as e:
|
|
|
|
except SSL.Error as e:
|
|
|
|
raise ServeFileException("SSL error: Could not read SSL public/private key "
|
|
|
|
raise ServeFileException("SSL error: Could not read SSL public/private key from file(s) (error was: \"%s\")" % (e[0][0][2],))
|
|
|
|
"from file(s) (error was: \"%s\")" % (e[0][0][2],))
|
|
|
|
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
server = ThreadedHTTPServer((listenIp, self.port), handler,
|
|
|
|
server = ThreadedHTTPServer((listenIp, self.port), handler,
|
|
|
|
bind_and_activate=False)
|
|
|
|
bind_and_activate=False)
|
|
|
@ -1018,7 +984,7 @@ class ServeFile():
|
|
|
|
print("Serving \"%s\" for uploads at port %d." % (self.target, self.port))
|
|
|
|
print("Serving \"%s\" for uploads at port %d." % (self.target, self.port))
|
|
|
|
|
|
|
|
|
|
|
|
# print urls with local network adresses
|
|
|
|
# print urls with local network adresses
|
|
|
|
print("\nSome addresses %s will be available at:" %
|
|
|
|
print("\nSome addresses %s will be available at:" % \
|
|
|
|
("this file" if (self.serveMode != self.MODE_UPLOAD) else "the uploadform", ))
|
|
|
|
("this file" if (self.serveMode != self.MODE_UPLOAD) else "the uploadform", ))
|
|
|
|
ips = self.getIPs()
|
|
|
|
ips = self.getIPs()
|
|
|
|
if not ips or len(ips) == 0 or ips[0] == '':
|
|
|
|
if not ips or len(ips) == 0 or ips[0] == '':
|
|
|
@ -1075,8 +1041,7 @@ class ServeFile():
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
os.mkdir(self.target)
|
|
|
|
os.mkdir(self.target)
|
|
|
|
except (IOError, OSError) as e:
|
|
|
|
except (IOError, OSError) as e:
|
|
|
|
raise ServeFileException("Error: Could not create directory '%s' for uploads, %r" %
|
|
|
|
raise ServeFileException("Error: Could not create directory '%s' for uploads, %r" % (self.target, str(e)))
|
|
|
|
(self.target, str(e)))
|
|
|
|
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
raise ServeFileException("Error: Upload directory already exists and is a file.")
|
|
|
|
raise ServeFileException("Error: Upload directory already exists and is a file.")
|
|
|
|
FilePutter.targetDir = os.path.abspath(self.target)
|
|
|
|
FilePutter.targetDir = os.path.abspath(self.target)
|
|
|
@ -1095,7 +1060,6 @@ class ServeFile():
|
|
|
|
AuthenticationHandler.authString = self.auth
|
|
|
|
AuthenticationHandler.authString = self.auth
|
|
|
|
if self.authrealm:
|
|
|
|
if self.authrealm:
|
|
|
|
AuthenticationHandler.realm = self.authrealm
|
|
|
|
AuthenticationHandler.realm = self.authrealm
|
|
|
|
|
|
|
|
|
|
|
|
class AuthenticatedHandler(AuthenticationHandler, handler):
|
|
|
|
class AuthenticatedHandler(AuthenticationHandler, handler):
|
|
|
|
pass
|
|
|
|
pass
|
|
|
|
handler = AuthenticatedHandler
|
|
|
|
handler = AuthenticatedHandler
|
|
|
@ -1141,8 +1105,7 @@ class AuthenticationHandler():
|
|
|
|
self.send_response(401)
|
|
|
|
self.send_response(401)
|
|
|
|
self.send_header("WWW-Authenticate", "Basic realm=\"%s\"" % self.realm)
|
|
|
|
self.send_header("WWW-Authenticate", "Basic realm=\"%s\"" % self.realm)
|
|
|
|
self.send_header("Connection", "close")
|
|
|
|
self.send_header("Connection", "close")
|
|
|
|
errorMsg = ("<html><head><title>401 - Unauthorized</title></head>"
|
|
|
|
errorMsg = "<html><head><title>401 - Unauthorized</title></head><body><h1>401 - Unauthorized</h1></body></html>"
|
|
|
|
"<body><h1>401 - Unauthorized</h1></body></html>")
|
|
|
|
|
|
|
|
self.send_header("Content-Length", str(len(errorMsg)))
|
|
|
|
self.send_header("Content-Length", str(len(errorMsg)))
|
|
|
|
self.end_headers()
|
|
|
|
self.end_headers()
|
|
|
|
self.wfile.write(errorMsg.encode())
|
|
|
|
self.wfile.write(errorMsg.encode())
|
|
|
@ -1152,35 +1115,32 @@ def main():
|
|
|
|
parser = argparse.ArgumentParser(prog='servefile', description='Serve a single file via HTTP.')
|
|
|
|
parser = argparse.ArgumentParser(prog='servefile', description='Serve a single file via HTTP.')
|
|
|
|
parser.add_argument('--version', action='version', version='%(prog)s ' + __version__)
|
|
|
|
parser.add_argument('--version', action='version', version='%(prog)s ' + __version__)
|
|
|
|
parser.add_argument('target', metavar='file/directory', type=str)
|
|
|
|
parser.add_argument('target', metavar='file/directory', type=str)
|
|
|
|
parser.add_argument('-p', '--port', type=int, default=8080,
|
|
|
|
parser.add_argument('-p', '--port', type=int, default=8080, \
|
|
|
|
help='Port to listen on')
|
|
|
|
help='Port to listen on')
|
|
|
|
parser.add_argument('-u', '--upload', action="store_true", default=False,
|
|
|
|
parser.add_argument('-u', '--upload', action="store_true", default=False, \
|
|
|
|
help="Enable uploads to a given directory")
|
|
|
|
help="Enable uploads to a given directory")
|
|
|
|
parser.add_argument('-s', '--max-upload-size', type=str,
|
|
|
|
parser.add_argument('-s', '--max-upload-size', type=str, \
|
|
|
|
help="Limit upload size in kB. Size modifiers are allowed, e.g. 2G, 12MB, 1B")
|
|
|
|
help="Limit upload size in kB. Size modifiers are allowed, e.g. 2G, 12MB, 1B")
|
|
|
|
parser.add_argument('-l', '--list-dir', action="store_true", default=False,
|
|
|
|
parser.add_argument('-l', '--list-dir', action="store_true", default=False, \
|
|
|
|
help="Show directory indexes and allow access to all subdirectories")
|
|
|
|
help="Show directory indexes and allow access to all subdirectories")
|
|
|
|
parser.add_argument('--ssl', action="store_true", default=False,
|
|
|
|
parser.add_argument('--ssl', action="store_true", default=False, \
|
|
|
|
help="Enable SSL. If no key/cert is specified one will be generated")
|
|
|
|
help="Enable SSL. If no key/cert is specified one will be generated")
|
|
|
|
parser.add_argument('--key', type=str,
|
|
|
|
parser.add_argument('--key', type=str, \
|
|
|
|
help="Keyfile to use for SSL. If no cert is given with --cert the keyfile "
|
|
|
|
help="Keyfile to use for SSL. If no cert is given with --cert the keyfile will also be searched for a cert")
|
|
|
|
"will also be searched for a cert")
|
|
|
|
parser.add_argument('--cert', type=str, \
|
|
|
|
parser.add_argument('--cert', type=str,
|
|
|
|
|
|
|
|
help="Certfile to use for SSL")
|
|
|
|
help="Certfile to use for SSL")
|
|
|
|
parser.add_argument('-a', '--auth', type=str, metavar='user:password',
|
|
|
|
parser.add_argument('-a', '--auth', type=str, metavar='user:password', \
|
|
|
|
help="Set user and password for HTTP basic authentication")
|
|
|
|
help="Set user and password for HTTP basic authentication")
|
|
|
|
parser.add_argument('--realm', type=str, default=None,
|
|
|
|
parser.add_argument('--realm', type=str, default=None,\
|
|
|
|
help="Set a realm for HTTP basic authentication")
|
|
|
|
help="Set a realm for HTTP basic authentication")
|
|
|
|
parser.add_argument('-t', '--tar', action="store_true", default=False,
|
|
|
|
parser.add_argument('-t', '--tar', action="store_true", default=False, \
|
|
|
|
help="Enable on the fly tar creation for given file or directory. "
|
|
|
|
help="Enable on the fly tar creation for given file or directory. Note: Download continuation will not be available")
|
|
|
|
"Note: Download continuation will not be available")
|
|
|
|
parser.add_argument('-c', '--compression', type=str, metavar='method', \
|
|
|
|
parser.add_argument('-c', '--compression', type=str, metavar='method',
|
|
|
|
default="none", \
|
|
|
|
default="none",
|
|
|
|
help="Set compression method, only in combination with --tar. Can be one of %s" % ", ".join(TarFileHandler.compressionMethods))
|
|
|
|
help="Set compression method, only in combination with --tar. "
|
|
|
|
parser.add_argument('-4', '--ipv4-only', action="store_true", default=False, \
|
|
|
|
"Can be one of %s" % ", ".join(TarFileHandler.compressionMethods))
|
|
|
|
|
|
|
|
parser.add_argument('-4', '--ipv4-only', action="store_true", default=False,
|
|
|
|
|
|
|
|
help="Listen on IPv4 only")
|
|
|
|
help="Listen on IPv4 only")
|
|
|
|
parser.add_argument('-6', '--ipv6-only', action="store_true", default=False,
|
|
|
|
parser.add_argument('-6', '--ipv6-only', action="store_true", default=False, \
|
|
|
|
help="Listen on IPv6 only")
|
|
|
|
help="Listen on IPv6 only")
|
|
|
|
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
args = parser.parse_args()
|
|
|
@ -1196,7 +1156,7 @@ def main():
|
|
|
|
sys.exit(1)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
if args.max_upload_size:
|
|
|
|
if args.max_upload_size:
|
|
|
|
sizeRe = re.match(r"^(\d+(?:[,.]\d+)?)(?:([bkmgtpe])(?:(?<!b)b?)?)?$", args.max_upload_size.lower())
|
|
|
|
sizeRe = re.match("^(\d+(?:[,.]\d+)?)(?:([bkmgtpe])(?:(?<!b)b?)?)?$", args.max_upload_size.lower())
|
|
|
|
if not sizeRe:
|
|
|
|
if not sizeRe:
|
|
|
|
print("Error: Your max upload size param is broken. Try something like 3M or 2.5Gb.")
|
|
|
|
print("Error: Your max upload size param is broken. Try something like 3M or 2.5Gb.")
|
|
|
|
sys.exit(1)
|
|
|
|
sys.exit(1)
|
|
|
@ -1223,8 +1183,7 @@ def main():
|
|
|
|
if args.auth:
|
|
|
|
if args.auth:
|
|
|
|
dpos = args.auth.find(":")
|
|
|
|
dpos = args.auth.find(":")
|
|
|
|
if dpos <= 0 or dpos == (len(args.auth)-1):
|
|
|
|
if dpos <= 0 or dpos == (len(args.auth)-1):
|
|
|
|
print("Error: User and password for HTTP basic authentication need to be both "
|
|
|
|
print("Error: User and password for HTTP basic authentication need to be both at least one character and have to be separated by a \":\".")
|
|
|
|
"at least one character and have to be separated by a \":\".")
|
|
|
|
|
|
|
|
sys.exit(1)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
if args.realm and not args.auth:
|
|
|
|
if args.realm and not args.auth:
|
|
|
@ -1295,3 +1254,4 @@ def main():
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|
|
|
|
main()
|
|
|
|
|
|
|
|
|
|
|
|