Code cleanup

This commit is contained in:
Sebastian Lohff 2012-04-12 13:17:55 +02:00
parent a0e97c7de4
commit d9ca9b128c
1 changed files with 49 additions and 10 deletions

View File

@ -108,6 +108,10 @@ class FileHandler(BaseHTTPServer.BaseHTTPRequestHandler):
return myfile.read(readsize) return myfile.read(readsize)
class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler): class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
""" Simple HTTP Server which allows uploading to a specified directory
either via multipart/form-data or POST/PUT requests containing the file.
"""
targetDir = "unknown" targetDir = "unknown"
uploadPage = """ uploadPage = """
<!docype html> <!docype html>
@ -122,33 +126,54 @@ class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
""" """
def do_GET(self): def do_GET(self):
""" Answer every GET request with the upload form """
self.sendResponse(200, self.uploadPage) self.sendResponse(200, self.uploadPage)
def do_POST(self): def do_POST(self):
env = os.environ """ Upload a file via POST
env['REQUEST_METHOD'] = "POST"
If the content-type is multipart/form-data it checks for the file
field and saves the data to disk. For other content-types it just
calls do_PUT and is handled as such except for the http response code.
Files can be uploaded with wget --post-file=path/to/file <url> or
curl -X POST -d @file <url> .
"""
ctype = self.headers.getheader('content-type') ctype = self.headers.getheader('content-type')
# 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")):
# not a normal multipart request ==> handle as PUT request # not a normal multipart request ==> handle as PUT request
self.do_PUT() return self.do_PUT(fromPost=True)
return
self.sendResponse(400, "Request was not a multipart request. If you want to upload data with curl, do a PUT request, e.g. curl -X PUT -d @file http://ip:port/your-filename")
return
# create FieldStorage object for multipart parsing
env = os.environ
env['REQUEST_METHOD'] = "POST"
fstorage = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ=env)
if not "file" in fstorage: if not "file" in fstorage:
self.sendResponse(400, "No file found in request.") self.sendResponse(400, "No file found in request.")
return return
fstorage = cgi.FieldStorage(fp=self.rfile, headers=self.headers, environ=env)
destFileName = self.getTargetName(fstorage["file"].filename) destFileName = self.getTargetName(fstorage["file"].filename)
if destFileName == "": if destFileName == "":
self.sendResponse(400, "Filename was empty") self.sendResponse(400, "Filename was empty or invalid")
return return
# write file down to disk, send an
target = open(destFileName, "w") target = open(destFileName, "w")
target.write(fstorage["file"].file.read()) target.write(fstorage["file"].file.read())
target.close() target.close()
self.sendResponse(200, "OK!") self.sendResponse(200, "OK!")
def do_PUT(self): def do_PUT(self, fromPost=False):
""" Upload a file via PUT
The request path is used as filename, so uploading a file to the url
http://host:8080/testfile will cause the file to be named testfile. If
no filename is given, a random name will be generated.
Files can be uploaded with e.g. curl -X POST -d @file <url> .
"""
length = 0 length = 0
try: try:
length = int(self.headers['Content-Length']) length = int(self.headers['Content-Length'])
@ -160,21 +185,29 @@ class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
fileName = urllib.unquote(self.path) fileName = urllib.unquote(self.path)
if fileName == "/": if fileName == "/":
# if no filename was given we have to generate one
fileName = str(time.time()) fileName = str(time.time())
cleanFileName = self.getTargetName(fileName) cleanFileName = self.getTargetName(fileName)
if cleanFileName == "": if cleanFileName == "":
self.sendResponse(400, "Filename was invalid") self.sendResponse(400, "Filename was invalid")
return return
# Sometimes clients want to be told to continue with their transfer
if self.headers.getheader("Expect") == "100-continue": if self.headers.getheader("Expect") == "100-continue":
self.send_response(100) self.send_response(100)
self.end_headers() self.end_headers()
print "Saving uploaded file to %s" % cleanFileName print "Saving uploaded file to %s" % cleanFileName
target = open(cleanFileName, "w") target = open(cleanFileName, "w")
target.write(self.rfile.read(int(self.headers['Content-Length']))) target.write(self.rfile.read(int(self.headers['Content-Length'])))
target.close() target.close()
self.sendResponse(201, "OK!") self.sendResponse(fromPost and 200 or 201, "OK!")
def sendResponse(self, code, msg): def sendResponse(self, code, msg):
""" Send a HTTP response with code and msg, providing the correct
content-length.
"""
self.send_response(code) self.send_response(code)
self.send_header('Content-Type', 'text/html') self.send_header('Content-Type', 'text/html')
self.send_header('content-Length', str(len(msg))) self.send_header('content-Length', str(len(msg)))
@ -182,6 +215,12 @@ class FilePutter(BaseHTTPServer.BaseHTTPRequestHandler):
self.wfile.write(msg) self.wfile.write(msg)
def getTargetName(self, fname): def getTargetName(self, fname):
""" Generate a clean and secure filename.
This function takes a filename and strips all the slashes out of it.
If the file already exists in the target directory, a (NUM) will be
appended, so no file will be overwritten.
"""
cleanFileName = fname.replace("/", "") cleanFileName = fname.replace("/", "")
if cleanFileName == "": if cleanFileName == "":
return "" return ""