Compare commits
No commits in common. "6e27ecfe8ce1a417af65a7f328f8ea2cd85c9c8d" and "93e0c3dd7e96c7603475f776d7085e60ffd9758a" have entirely different histories.
6e27ecfe8c
...
93e0c3dd7e
52
servefile
52
servefile
|
@ -13,8 +13,8 @@ import argparse
|
||||||
import base64
|
import base64
|
||||||
import cgi
|
import cgi
|
||||||
import datetime
|
import datetime
|
||||||
import io
|
|
||||||
import mimetypes
|
import mimetypes
|
||||||
|
import urllib
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import select
|
import select
|
||||||
|
@ -27,12 +27,10 @@ import time
|
||||||
try:
|
try:
|
||||||
import BaseHTTPServer
|
import BaseHTTPServer
|
||||||
import SocketServer
|
import SocketServer
|
||||||
from urllib import quote, unquote
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# both have different names in python3
|
# both have different names in python3
|
||||||
import http.server as BaseHTTPServer
|
import http.server as BaseHTTPServer
|
||||||
import socketserver as SocketServer
|
import socketserver as SocketServer
|
||||||
from urllib.parse import quote, unquote
|
|
||||||
|
|
||||||
# only activate SSL if available
|
# only activate SSL if available
|
||||||
HAVE_SSL = False
|
HAVE_SSL = False
|
||||||
|
@ -58,7 +56,7 @@ class FileBaseHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
Returns True if a redirect was issued. """
|
Returns True if a redirect was issued. """
|
||||||
if not fileName:
|
if not fileName:
|
||||||
fileName = self.fileName
|
fileName = self.fileName
|
||||||
if unquote(self.path) != "/" + fileName:
|
if urllib.unquote(self.path) != "/" + fileName:
|
||||||
self.send_response(302)
|
self.send_response(302)
|
||||||
self.send_header('Location', '/' + fileName)
|
self.send_header('Location', '/' + fileName)
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
|
@ -348,12 +346,12 @@ class DirListingHandler(FileBaseHandler):
|
||||||
<p>The requestet URL %s was not found on this server</p>
|
<p>The requestet URL %s was not found on this server</p>
|
||||||
<p><a href="/">Back to /</a>
|
<p><a href="/">Back to /</a>
|
||||||
</body>
|
</body>
|
||||||
</html>""" % self.escapeHTML(unquote(self.path))
|
</html>""" % self.escapeHTML(urllib.unquote(self.path))
|
||||||
self.send_header("Content-Length", str(len(errorMsg)))
|
self.send_header("Content-Length", str(len(errorMsg)))
|
||||||
self.send_header('Connection', 'close')
|
self.send_header('Connection', 'close')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
if not head:
|
if not head:
|
||||||
self.wfile.write(errorMsg.encode())
|
self.wfile.write(errorMsg)
|
||||||
|
|
||||||
def escapeHTML(self, htmlstr):
|
def escapeHTML(self, htmlstr):
|
||||||
entities = [("<", "<"), (">", ">")]
|
entities = [("<", "<"), (">", ">")]
|
||||||
|
@ -380,7 +378,7 @@ class DirListingHandler(FileBaseHandler):
|
||||||
<td class="size">%s</td>
|
<td class="size">%s</td>
|
||||||
<td class="type">%s</td>
|
<td class="type">%s</td>
|
||||||
</tr>
|
</tr>
|
||||||
""" % (quote(item), item, lastModified, fileSize, fileType))
|
""" % (urllib.quote(item), item, lastModified, fileSize, fileType))
|
||||||
|
|
||||||
def sendDirectoryListing(self, path, head):
|
def sendDirectoryListing(self, path, head):
|
||||||
""" Generate a directorylisting for path and send it """
|
""" Generate a directorylisting for path and send it """
|
||||||
|
@ -416,7 +414,7 @@ class DirListingHandler(FileBaseHandler):
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
""" % {'path': os.path.normpath(unquote(self.path))}
|
""" % {'path': os.path.normpath(urllib.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>
|
||||||
|
@ -542,7 +540,7 @@ class DirListingHandler(FileBaseHandler):
|
||||||
self.send_header("Content-Length", str(len(listing)))
|
self.send_header("Content-Length", str(len(listing)))
|
||||||
self.send_header('Connection', 'close')
|
self.send_header('Connection', 'close')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(listing.encode())
|
self.wfile.write(listing)
|
||||||
|
|
||||||
def convertSize(self, size):
|
def convertSize(self, size):
|
||||||
for ext in "KMGT":
|
for ext in "KMGT":
|
||||||
|
@ -554,7 +552,7 @@ class DirListingHandler(FileBaseHandler):
|
||||||
return (size, ext.strip())
|
return (size, ext.strip())
|
||||||
|
|
||||||
def getCleanPath(self):
|
def getCleanPath(self):
|
||||||
urlPath = os.path.normpath(unquote(self.path)).strip("/")
|
urlPath = os.path.normpath(urllib.unquote(self.path)).strip("/")
|
||||||
path = os.path.join(self.targetDir, urlPath)
|
path = os.path.join(self.targetDir, urlPath)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
@ -596,8 +594,7 @@ class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
length = self.getContentLength()
|
length = self.getContentLength()
|
||||||
if length < 0:
|
if length < 0:
|
||||||
return
|
return
|
||||||
print(self.headers)
|
ctype = self.headers.getheader('Content-Type')
|
||||||
ctype = self.headers.get('Content-Type')
|
|
||||||
|
|
||||||
# check for multipart/form-data.
|
# check for multipart/form-data.
|
||||||
if not (ctype and ctype.lower().startswith("multipart/form-data")):
|
if not (ctype and ctype.lower().startswith("multipart/form-data")):
|
||||||
|
@ -618,7 +615,7 @@ class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
return
|
return
|
||||||
|
|
||||||
# write file down to disk, send a 200 afterwards
|
# write file down to disk, send a 200 afterwards
|
||||||
target = open(destFileName, "wb")
|
target = open(destFileName, "w")
|
||||||
bytesLeft = length
|
bytesLeft = length
|
||||||
while bytesLeft > 0:
|
while bytesLeft > 0:
|
||||||
bytesToRead = min(self.blockSize, bytesLeft)
|
bytesToRead = min(self.blockSize, bytesLeft)
|
||||||
|
@ -641,7 +638,7 @@ class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
if length < 0:
|
if length < 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
fileName = unquote(self.path)
|
fileName = urllib.unquote(self.path)
|
||||||
if fileName == "/":
|
if fileName == "/":
|
||||||
# if no filename was given we have to generate one
|
# if no filename was given we have to generate one
|
||||||
fileName = str(time.time())
|
fileName = str(time.time())
|
||||||
|
@ -688,7 +685,7 @@ class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||||
self.send_header('Content-Length', str(len(msg)))
|
self.send_header('Content-Length', str(len(msg)))
|
||||||
self.send_header('Connection', 'close')
|
self.send_header('Connection', 'close')
|
||||||
self.end_headers()
|
self.end_headers()
|
||||||
self.wfile.write(msg.encode())
|
self.wfile.write(msg)
|
||||||
|
|
||||||
def getTargetName(self, fname):
|
def getTargetName(self, fname):
|
||||||
""" Generate a clean and secure filename.
|
""" Generate a clean and secure filename.
|
||||||
|
@ -772,17 +769,6 @@ class SecureThreadedHTTPServer(ThreadedHTTPServer):
|
||||||
class SecureHandler():
|
class SecureHandler():
|
||||||
def setup(self):
|
def setup(self):
|
||||||
self.connection = self.request
|
self.connection = self.request
|
||||||
|
|
||||||
if sys.version_info[0] > 2:
|
|
||||||
# python3 SocketIO (replacement for socket._fileobject)
|
|
||||||
raw_read_sock = socket.SocketIO(self.request, 'rb')
|
|
||||||
raw_write_sock = socket.SocketIO(self.request, 'wb')
|
|
||||||
rbufsize = self.rbufsize > 0 and self.rbufsize or io.DEFAULT_BUFFER_SIZE
|
|
||||||
wbufsize = self.wbufsize > 0 and self.wbufsize or io.DEFAULT_BUFFER_SIZE
|
|
||||||
self.rfile = io.BufferedReader(raw_read_sock, rbufsize)
|
|
||||||
self.wfile = io.BufferedWriter(raw_write_sock, wbufsize)
|
|
||||||
else:
|
|
||||||
# python2 does not have SocketIO
|
|
||||||
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)
|
||||||
|
|
||||||
|
@ -845,7 +831,7 @@ class ServeFile():
|
||||||
# we couldn't find any ip address
|
# we couldn't find any ip address
|
||||||
proc = None
|
proc = None
|
||||||
if proc:
|
if proc:
|
||||||
ips = proc.stdout.read().decode().strip().split("\n")
|
ips = proc.stdout.read().strip().split("\n")
|
||||||
|
|
||||||
# filter out ips we are not listening on
|
# filter out ips we are not listening on
|
||||||
if not self.listenIPv6:
|
if not self.listenIPv6:
|
||||||
|
@ -891,7 +877,7 @@ class ServeFile():
|
||||||
for ip in self.getIPs() + ["127.0.0.1", "::1"]:
|
for ip in self.getIPs() + ["127.0.0.1", "::1"]:
|
||||||
altNames.append("IP:%s" % ip)
|
altNames.append("IP:%s" % ip)
|
||||||
altNames.append("DNS:localhost")
|
altNames.append("DNS:localhost")
|
||||||
ext = crypto.X509Extension(b"subjectAltName", False, (",".join(altNames)).encode())
|
ext = crypto.X509Extension("subjectAltName", False, ",".join(altNames))
|
||||||
req.add_extensions([ext])
|
req.add_extensions([ext])
|
||||||
|
|
||||||
req.set_pubkey(pkey)
|
req.set_pubkey(pkey)
|
||||||
|
@ -915,8 +901,8 @@ class ServeFile():
|
||||||
self.key = pkey
|
self.key = pkey
|
||||||
|
|
||||||
print("done.")
|
print("done.")
|
||||||
print("SHA1 fingerprint:", cert.digest("sha1").decode())
|
print("SHA1 fingerprint:", cert.digest("sha1"))
|
||||||
print("MD5 fingerprint:", cert.digest("md5").decode())
|
print("MD5 fingerprint:", cert.digest("md5"))
|
||||||
|
|
||||||
def _getCert(self):
|
def _getCert(self):
|
||||||
return self.cert
|
return self.cert
|
||||||
|
@ -927,7 +913,7 @@ class ServeFile():
|
||||||
def setAuth(self, user, password, realm=None):
|
def setAuth(self, user, password, realm=None):
|
||||||
if not user or not password:
|
if not user or not password:
|
||||||
raise ServeFileException("User and password both need to be at least one character.")
|
raise ServeFileException("User and password both need to be at least one character.")
|
||||||
self.auth = base64.b64encode(("%s:%s" % (user, password)).encode()).decode()
|
self.auth = base64.b64encode("%s:%s" % (user, password))
|
||||||
self.authrealm = realm
|
self.authrealm = realm
|
||||||
|
|
||||||
def _createServer(self, handler, withv6=False):
|
def _createServer(self, handler, withv6=False):
|
||||||
|
@ -989,7 +975,7 @@ class ServeFile():
|
||||||
else:
|
else:
|
||||||
pwPart = ""
|
pwPart = ""
|
||||||
if self.auth:
|
if self.auth:
|
||||||
pwPart = base64.b64decode(self.auth).decode() + "@"
|
pwPart = base64.b64decode(self.auth) + "@"
|
||||||
for ip in ips:
|
for ip in ips:
|
||||||
if ":" in ip:
|
if ":" in ip:
|
||||||
ip = "[%s]" % ip
|
ip = "[%s]" % ip
|
||||||
|
@ -1105,7 +1091,7 @@ class AuthenticationHandler():
|
||||||
errorMsg = "<html><head><title>401 - Unauthorized</title></head><body><h1>401 - Unauthorized</h1></body></html>"
|
errorMsg = "<html><head><title>401 - Unauthorized</title></head><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)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|
|
@ -274,13 +274,3 @@ def test_https(run_servefile, datadir):
|
||||||
# assert fingerprint
|
# assert fingerprint
|
||||||
urllib3.disable_warnings()
|
urllib3.disable_warnings()
|
||||||
check_download(data, protocol='https', verify=False)
|
check_download(data, protocol='https', verify=False)
|
||||||
|
|
||||||
def test_https_big_download(run_servefile, datadir):
|
|
||||||
# test with about 10 mb of data
|
|
||||||
data = "x" * (10 * 1024 ** 2)
|
|
||||||
p = datadir({'testfile': data}) / 'testfile'
|
|
||||||
run_servefile(['--ssl', str(p)])
|
|
||||||
time.sleep(0.2) # time for generating ssl certificates
|
|
||||||
|
|
||||||
urllib3.disable_warnings()
|
|
||||||
check_download(data, protocol='https', verify=False)
|
|
||||||
|
|
Loading…
Reference in New Issue