M live_doc/file_handler.py => live_doc/file_handler.py +6 -5
@@ 23,7 23,7 @@ class FileHandler(dict):
self.touched = set() # Which formats were touched and need closing.
self.implied = dict() # Which formats were implied by which.
- @property
+ @property # TODO particular interpreters depend..
def depends_files(self):
"""Returns files it depends on.
In the `file` config, `depends` sets this. `:`-separated."""
@@ 39,10 39,11 @@ In the `file` config, `depends` sets this. `:`-separated."""
def use_obj(self):
return self.ensure_interpreter(self.use_lang)
- def hard_reset(self, for_name):
- """Harshly deletes `for_name` and where it was implied from."""
- if for_name in self: del self[for_name]
- for name in self.implied.get(for_name, []):
+ def hard_reset(self, interpreter_name):
+ """Harshly deletes interpreter and where it was implied from."""
+ if interpreter_name in self: del self[interpreter_name]
+
+ for name in self.implied.get(interpreter_name, []):
if name in self: del self[name]
def reset_state(self):
M live_doc/http_main.py => live_doc/http_main.py +28 -18
@@ 76,16 76,23 @@ class RH(hs.BaseHTTPRequestHandler):
def do_GET_file(self, file):
"""Access a file. TODO only files this thing made.."""
- while file[0] == '/': file = file[1:] # May not start with slash.
+ # Strip slash and translate special filenames.
+ file = main.mangle_filename(file.lstrip('/'))
+ if not file:
+ self.send_response(403)
+ self.end_headers()
+ self.write("Not in permitted directory")
+ return
+ # Root directory permitted for module directory.
+ if ('/' + file).startswith(main.module_dir):
+ file = '/' + file
- # TODO permanently remember what .d/ directories are ok?
- if (file.find("..")!=-1
- or (file.find(".d/")==-1 and file not in main.file_ok)):
+ if file.find("..")!=-1: # Dont permit escaping.
print("DENIED", main.output_dir, file, main.file_ok)
self.send_response(403)
self.end_headers()
- self.wfile.write(b"access denied")
- else:
+ self.write("stuff that may try to escape directories.")
+ else: # Ok, provide the file.
print("ACCESS", main.output_dir, file)
self.send_response(200)
self.end_headers()
@@ 116,6 123,7 @@ class RH(hs.BaseHTTPRequestHandler):
result_time = update(self.write, float(after_t), 5)
else: # Nothing there to respond, maybe when file changes.
# TODO need to be able to interrupt this?
+# TODO won't do much if remembered state not appropriately invalidated..
subprocess.call(['inotifywait', '-e', 'modify']
+ file.depends_files)
result_time = update(self.write, float(after_t), 0.5)
@@ 129,17 137,16 @@ class RH(hs.BaseHTTPRequestHandler):
def do_GET_close_nb(self, file):
"""Close a notebook."""
- return 'closed' if main.close_file() else 'not found'
+ return ('closed' if main.close_file(main.mangle_filename(file))
+ else 'not found')
def do_POST_reset_nb(self, file):
"""Reset a notebook."""
- if main.reset_file(file):
+ real_file = main.mangle_filename(file)
+ if main.reset_file(real_file):
self.redirect("Resetting notebook, returning to it.",
'/' + file)
- return 'not found'
-
- def do_GET_reset_nb(self, filename):
- return main.reset_file(filename)
+ return f"not found {real_file}"
def report(self, say, *rest):
print(say, self.path, self.client_address,
@@ 154,16 161,19 @@ class RH(hs.BaseHTTPRequestHandler):
self.write(f"invalid {meth} ({rest})")
def do_POST_save_nb(self, file):
- saved = main.get_file(file).save_nb()
- main.file_ok.add(saved)
+ main.file_ok.add(main.get_file(file).save_nb())
+
+ saved = f"{file}.d/index.html"
self.redirect( # TODO report success/not, link to it.
f"""Tried to save. <code>{file}</code><p>Returning to notebook.
<p><a href="/:file:/{saved}">{saved}</a> contains the saved file.</p>""",
f"/{file}?save_attempt=1")
def do_POST_save_nb_zip(self, file):
- zip_file = main_get_file(file).save_nb_zip()
+ zip_file = main.get_file(file).save_nb_zip()
main.file_ok.add(zip_file)
+
+ zip_file = f"{file}.d/" + zip_file.split('/')[-1]
self.redirect( # TODO report success/not, link to it.
f"""Tried to save. <code>{file}</code><p>Returning to notebook.
<p><a href="/:file:/{zip_file}">{zip_file}</a> contains the saved file.</p>""",
@@ 305,12 315,12 @@ Will probably redirect to a picture after.</div>\n"""
filename = file.filename
self.wfile.write(assets.bformat("top.htm",
- file=filename,
+ file=path[1:],
**{ nom : (f"<a href=\"/:file:/{f}\">{say}</a>"
if Path(filename).exists() else "")
for nom, f, say in
- [('if_saved', f"{filename}.d/index.html", "(earlier)"),
- ('if_saved_zip', f"{filename}.d/" + filename.split('/')[-1] + ".zip", "(earlier)")]
+ [('if_saved', f"{path[1:]}.d/index.html", "(earlier)"),
+ ('if_saved_zip', f"{path[1:]}.d/" + path[1:].split('/')[-1] + ".zip", "(earlier)")]
},
request_interval=1000*args.request_interval))
M live_doc/main.py => live_doc/main.py +12 -8
@@ 31,9 31,6 @@ class MainModuleDict(ModuleDict):
from live_doc.file_handler import FileHandlerDict, FileHandler
-import live_doc.module_dict as module_dict
-module_dir = '/'.join(module_dict.__file__.split('/')[:-2])
-
import os, time
from live_doc.util.stream import Stream
@@ 78,7 75,7 @@ TODO option to insert them with base64, tho inefficient."""
saved = self.out_local("index_for_zip.html")
import zipfile # (want dependency to be optional)
zipname = file.split('/')[-1]
- to_file = (self.out_local("{zipname}.zip")
+ to_file = (self.out_local(f"{zipname}.zip")
if to_file is None else to_file)
zf = zipfile.ZipFile(to_file, 'w')
@@ 86,7 83,7 @@ TODO option to insert them with base64, tho inefficient."""
zf.write(self.out_local(file_inside),
f"{zipname}/{file_inside}")
- self.save_nb(file, saved, add_inside_file) # Write the .html
+ self.save_nb(saved, add_inside_file) # Write the .html
zf.write(saved, f"{zipname}/index.html")
zf.close()
return to_file
@@ 145,9 142,13 @@ highlighter=highlighters.bat_aha:highlighters.vimcat_aha:highlighters.script_bat
colorer=highlighters.aha:highlighters.plain
""") # NOTE: wrap is used straight-up right now.
+ # TODO deny list?
def __init__(self, cfg_dirs, args, file_ok=None, permit_dirs=None):
+ import live_doc.module_dict as module_dict
+ self.module_dir = '/'.join(module_dict.__file__.split('/')[:-2])
+
self.assets = AssetsMem([d + "/assets/" for d in cfg_dirs]
- + [f"{module_dir}/assets/"])
+ + [f"{self.module_dir}/assets/"])
self.configs = Assets([d + "/cfg/" for d in cfg_dirs])
# Figure where the files are put.
@@ 175,12 176,15 @@ colorer=highlighters.aha:highlighters.plain
self.permit_dirs = permit_dirs if len(permit_dirs)>0 else ['']
+ def permitted_file(self, filename):
+ return any(map(filename.startswith, self.permit_dirs))
+
def mangle_filename(self, filename):
if filename.startswith(":live_doc:"):
- return module_dir + filename[10:]
+ return self.module_dir + filename[10:]
elif not filename.startswith('/') and filename.find("..") == -1:
# Must be in one of the permitted directories. (defaultly all)
- if any(map(filename.startswith, self.permit_dirs)):
+ if self.permitted_file(filename)
return filename
# else Otherwise not permitted.