view .cbz in browser (#916)

adds functionality to allow browsing .cbz directly in the browser, without downloading them and using a separate program. meant for quickly inspecting the contents, less so for reading.

adds two new api calls, ?zls and ?zget, which return a file listing of a zip file and a specific file in the archive, respectively.

uses the zipfile module, so no support for .cbr etc
This commit is contained in:
AppleTheGolden
2025-10-12 01:17:24 +02:00
committed by GitHub
parent 46c205dd60
commit 8ef6dda74b
7 changed files with 166 additions and 18 deletions

View File

@@ -1521,6 +1521,63 @@ class HttpCli(object):
self.log("rss: %d hits, %d bytes" % (len(hits), len(bret)))
return True
def tx_zls(self, abspath) -> bool:
if self.do_log:
self.log("zls %s @%s" % (self.req, self.uname))
if self.args.no_zls:
raise Pebkac(405, "zip browsing is disabled in server config")
import zipfile
try:
with zipfile.ZipFile(abspath, "r") as zf:
filelist = [{"fn": f.filename} for f in zf.infolist()]
ret = json.dumps(filelist).encode("utf-8", "replace")
self.reply(ret, mime="application/json")
return True
except (zipfile.BadZipfile, RuntimeError):
raise Pebkac(404, "requested file is not a valid zip file")
def tx_zget(self, abspath) -> bool:
maxsz = 1024 * 1024 * 64
inner_path = self.uparam.get("zget")
if not inner_path:
raise Pebkac(405, "inner path is required")
if self.do_log:
self.log(
"zget %s \033[35m%s\033[0m @%s" % (self.req, inner_path, self.uname)
)
if self.args.no_zls:
raise Pebkac(405, "zip browsing is disabled in server config")
import zipfile
try:
with zipfile.ZipFile(abspath, "r") as zf:
zi = zf.getinfo(inner_path)
if zi.file_size >= maxsz:
raise Pebkac(404, "zip bomb defused")
with zf.open(zi, "r") as fi:
self.send_headers(length=zi.file_size, mime=guess_mime(inner_path))
sendfile_py(
self.log,
0,
zi.file_size,
fi,
self.s,
self.args.s_wr_sz,
self.args.s_wr_slp,
not self.args.no_poll,
{},
"",
)
except KeyError:
raise Pebkac(404, "no such file in archive")
except (zipfile.BadZipfile, RuntimeError):
raise Pebkac(404, "requested file is not a valid zip file")
def handle_propfind(self) -> bool:
if self.do_log:
self.log("PFIND %s @%s" % (self.req, self.uname))
@@ -6480,6 +6537,11 @@ class HttpCli(object):
):
return self.tx_md(vn, abspath)
if "zls" in self.uparam:
return self.tx_zls(abspath)
if "zget" in self.uparam:
return self.tx_zget(abspath)
if not add_og or not og_fn:
return self.tx_file(
abspath, None if st.st_size or "nopipe" in vn.flags else vn.realpath
@@ -6569,6 +6631,7 @@ class HttpCli(object):
"acct": self.uname,
"perms": perms,
}
# also see `js_htm` in authsrv.py
j2a = {
"cgv1": vn.js_htm,
"cgv": cgv,