Last active
January 23, 2021 09:23
-
-
Save FND/e765a0da6be27fb8b9e1779ae7e3ef4b to your computer and use it in GitHub Desktop.
minimal WebDAV implementation for TiddlyWiki
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 python3 | |
import os | |
from hashlib import md5 | |
ROOT_DIR = os.path.dirname(__file__) | |
STATUSES = { | |
200: "OK", | |
204: "No Content", | |
404: "Not Found", | |
405: "Method Not Allowed", | |
412: "Precondition Failed", | |
415: "Unsupported Media Type" | |
} | |
def handler(environ, start_response): | |
method = environ["REQUEST_METHOD"] | |
respond = lambda *args, **kwargs: make_response(start_response, *args, **kwargs) | |
if method == "OPTIONS": | |
return respond(200, [("DAV", "1")]) # XXX: TiddlyWiki expects 200 rather than 204 | |
if method == "HEAD": | |
filepath = _determine_filepath(environ) | |
return respond(204, headers=[ | |
("ETag", _determine_etag(filepath)) | |
]) | |
elif method == "GET": | |
uri = environ.get("PATH_INFO", "") | |
if uri == "/favicon.ico": # TODO: read from disk | |
return respond(404) | |
else: | |
return serve_file(environ, start_response) | |
if method == "PUT": | |
return store_file(environ, start_response) | |
else: | |
return respond(405, body="invalid request") | |
def serve_file(environ, start_response): | |
filepath = _determine_filepath(environ) | |
with open(filepath) as fh: | |
return make_response(start_response, 200, | |
headers=[("Content-Type", "text/html")], # NB: TiddlyWiki-specific | |
body=fh.read()) # XXX: inefficient | |
def store_file(environ, start_response): | |
respond = lambda *args, **kwargs: make_response(start_response, *args, **kwargs) | |
if environ["CONTENT_TYPE"] != "text/html;charset=UTF-8": # NB: TiddlyWiki-specific heuristic | |
return respond(415) | |
filepath = _determine_filepath(environ) | |
etag = environ.get("HTTP_IF_MATCH") | |
if etag is not None: | |
_hash = _determine_etag(filepath) | |
if etag != _hash: | |
return respond(412, body=("expected ETag %s, received %s" % (_hash, etag))) | |
try: | |
size = int(environ["CONTENT_LENGTH"]) | |
content = environ["wsgi.input"].read(size) | |
except (KeyError, ValueError): | |
return make_response(400, body="missing `Content-Length` request header") | |
with open(filepath, "w") as fh: | |
fh.write(content.decode("utf-8")) | |
return respond(204) | |
def make_response(start_response, status, headers=[], body=[]): | |
status_line = "%s %s" % (status, STATUSES[status]) | |
start_response(status_line, headers) | |
if isinstance(body, str): # XXX: simplistic | |
return [body.encode("utf-8")] | |
return body | |
def _determine_etag(filepath): | |
with open(filepath) as fh: | |
content = fh.read() | |
return md5(content.encode("utf-8")).hexdigest() | |
def _determine_filepath(environ): | |
uri = environ.get("PATH_INFO", "") | |
filename = uri[1:] if uri.startswith("/") else uri | |
if len(filename) == 0: | |
filename = "index.html" # NB: TiddlyWki-specific | |
return os.path.abspath(os.path.join(ROOT_DIR, filename)) | |
if __name__ == "__main__": | |
from wsgiref.simple_server import make_server | |
host = "localhost" | |
port = 8080 | |
srv = make_server(host, port, handler) | |
print("→ http://%s:%s" % (host, port)) | |
srv.serve_forever() |
Hey @Dialga, I didn't expect anyone but myself to use this, but you could change line 8 to make that work:
-ROOT_DIR = os.path.dirname(__file__)
+ROOT_DIR = os.getcwd()
How did you come across this script in the first place?
Note that there might be security risks if others can access it via the web because modifying the URL might allow targeting arbitrary files on your disk (see line 94).
I searched for it on gist.
I was looking for a tiddlywiki server that behaves like the nodejs version, which saves individual tiddlers, rather than saving the whole file each time.
If this script is in the cwd then it would be accessible at localhost:8080/webdav.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This script will not work if placed in /local/bin, it will search for index.html there rather than the cwd.