~jasper/live_doc

6f09426b6dacf501d675c49e672675259cb17836 — Jasper den Ouden 1 year, 4 months ago 3975e6e
Filename mangling fix for when confusion between directory names and those in urls
3 files changed, 46 insertions(+), 31 deletions(-)

M live_doc/file_handler.py
M live_doc/http_main.py
M live_doc/main.py
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.