Last active
June 2, 2016 00:40
-
-
Save mckern/57ebaa8b2d649156e8b5e6468ecdd344 to your computer and use it in GitHub Desktop.
Basic upload form, checksums files if they're uploaded, doesn't overwrite
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
''' | |
upload.py, a basic CGI scipt to enable uploading a file | |
onto a web server without direct SSH or filesystem access. | |
Insecure by design, and in no way intended for public usage. | |
Integrate it with your favorite internal chat robot, drop | |
it into a continuous integration pipeline, or just use the | |
best-of-1994 form interface directly! | |
Author: Ryan McKern | |
Last edited: Tue May 31 2016 | |
''' | |
import cgi | |
import cgitb | |
import hashlib | |
import os | |
cgitb.enable() | |
if os.getenv("GROUP_WRITABLE"): | |
os.umask(0002) | |
BLOCKSIZE = os.getenv("BLOCKSIZE", 2**20) | |
UPLOAD_DIR = os.getenv("TARGET_DIRECTORY", "/tmp") | |
DIGEST_ALGORITHM = os.getenv("HASH_DIGEST", "sha256") | |
HTML_TEMPLATE = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | |
<html> | |
<head> | |
<title>File Upload: {ERROR}</title> | |
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> | |
</head> | |
<body> | |
<h1>File Upload</h1> | |
<form action="{SCRIPT_NAME}" method="POST" enctype="multipart/form-data"> | |
File name: <input name="file" type="file"><br> | |
<input name="submit" type="submit"> | |
</form> | |
</body> | |
</html>""" | |
HTML_ERROR = """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> | |
<html> | |
<head> | |
<title>File Upload: {ERROR}</title> | |
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> | |
</head> | |
<body><h1>Error: {ERROR}</h1> | |
<h1>File Upload</h1> | |
<form action="{SCRIPT_NAME}" method="POST" enctype="multipart/form-data"> | |
File name: <input name="file" type="file"><br> | |
<input name="submit" type="submit"> | |
</form> | |
</body> | |
</html>""" | |
def header(value): | |
''' | |
print a formatted header with value; ends in a single newline | |
''' | |
print "{}".format(value) | |
def statuscode(value): | |
''' | |
print a HTTP Status header with value; ends in two newlines | |
''' | |
header("Status: {}\n".format(value)) | |
def content_type(value="text/html"): | |
''' | |
print a HTTP Content-type header with value; defaults to "text/html" | |
''' | |
header("Content-Type: {}".format(value)) | |
def html(html_string, status_msg="200 OK"): | |
''' | |
print html_string as HTML; optionally takes a status_msg, | |
which defaults to "200 OK" | |
''' | |
content_type("text/html") | |
statuscode(status_msg) | |
print html_string | |
def html_values(error_msg=None): | |
''' | |
default values to interpolate into rendered HTML. Will take | |
optional error_msg if something went awry. | |
''' | |
return {"SCRIPT_NAME": os.getenv("SCRIPT_NAME", __file__), | |
"ERROR": error_msg} | |
def print_html_form(status_msg="200 OK"): | |
''' | |
print HTML_TEMPLATE and define a basic form for usage. | |
Takes an optional status_msg, which defaults to "200 OK". | |
''' | |
html(HTML_TEMPLATE.format(**html_values()), status_msg) | |
raise SystemExit(0) | |
def print_error_page(msg, status_msg="418 I'm a teapot (RFC 2324)"): | |
''' | |
print HTML_ERROR with msg; status_msg is optional, and defaults to | |
"418 I'm a teapot (RFC 2324)" | |
''' | |
html(HTML_ERROR.format(**html_values(msg)), status_msg) | |
raise SystemExit(0) | |
def write_file_with_checksum(filename, contents, digest=DIGEST_ALGORITHM): | |
checksum = hashlib.new(digest) | |
with open(filename + ".%s" % digest, "w") as cout: | |
with open(filename, "wb") as fout: | |
chunk = contents.read(BLOCKSIZE) | |
if not chunk: | |
return | |
checksum.update(chunk) | |
cout.write(checksum.hexdigest() + '\n') | |
fout.write(chunk) | |
def save_uploaded_file(form_field, upload_dir): | |
form = cgi.FieldStorage() | |
if form_field not in form: | |
return | |
fileitem = form[form_field] | |
if not fileitem.file: | |
print_error_page("No file uploaded", "400 Bad Request") | |
return | |
fname = os.path.join(upload_dir, fileitem.filename) | |
if os.path.isfile(fname): | |
print_error_page("File already exists", "403 Forbidden") | |
return | |
write_file_with_checksum(fname, fileitem.file) | |
save_uploaded_file("file", UPLOAD_DIR) | |
print_html_form() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment