diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index bc21e036..75503abc 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -37,6 +37,7 @@ from .__version__ import S_VERSION from .authsrv import VFS # typechk from .bos import bos from .star import StreamTar +from .stolen.qrcodegen import QrCode, qr2svg from .sutil import StreamArc, gfilter from .szip import StreamZip from .up2k import up2k_chunksize @@ -495,6 +496,9 @@ class HttpCli(object): self.vpath + "/" if self.trailing_slash and self.vpath else self.vpath ) + if "qr" in uparam: + return self.tx_qr() + if relchk(self.vpath) and (self.vpath != "*" or self.mode != "OPTIONS"): self.log("invalid relpath [{}]".format(self.vpath)) self.cbonk(self.conn.hsrv.gmal, self.req, "bad_vp", "invalid relpaths") @@ -3934,6 +3938,33 @@ class HttpCli(object): self.reply(ico, mime=mime, headers={"Last-Modified": lm}) return True + def tx_qr(self): + url = "%s://%s%s%s" % ( + "https" if self.is_https else "http", + self.host, + self.args.SRS, + self.vpaths, + ) + uhash = "" + uparams = [] + if self.ouparam: + for k, v in self.ouparam.items(): + if k == "qr": + continue + if k == "uhash": + uhash = v + continue + uparams.append(k if v is "" else "%s=%s" % (k, v)) + if uparams: + url += "?" + "&".join(uparams) + if uhash: + url += "#" + uhash + + self.log("qrcode(%r)" % (url,)) + ret = qr2svg(QrCode.encode_binary(url.encode("utf-8")), 2) + self.reply(ret.encode("utf-8"), mime="image/svg+xml") + return True + def tx_md(self, vn: VFS, fs_path: str) -> bool: logmsg = " %s @%s " % (self.req, self.uname) @@ -4505,9 +4536,6 @@ class HttpCli(object): if self.uname != self.args.shr_adm: rows = [x for x in rows if x[5] == self.uname] - for x in rows: - x[1] = "yes" if x[1] else "" - html = self.j2s( "shares", this=self, shr=self.args.shr, rows=rows, now=int(time.time()) ) @@ -4585,7 +4613,7 @@ class HttpCli(object): else: for zs in vps: if zs.endswith("/"): - t = "you cannot select more than one folder, or mix flies and folders in one selection" + t = "you cannot select more than one folder, or mix files and folders in one selection" raise Pebkac(400, t) vp = vps[0].rsplit("/", 1)[0] for zs in vps: diff --git a/copyparty/stolen/qrcodegen.py b/copyparty/stolen/qrcodegen.py index 3bee4dc9..38296359 100644 --- a/copyparty/stolen/qrcodegen.py +++ b/copyparty/stolen/qrcodegen.py @@ -594,3 +594,20 @@ def _get_bit(x: int, i: int) -> bool: class DataTooLongError(ValueError): pass + + +def qr2svg(qr: QrCode, border: int) -> str: + parts: list[str] = [] + for y in range(qr.size): + sy = border + y + for x in range(qr.size): + if qr.modules[y][x]: + parts.append("M%d,%dh1v1h-1z" % (border + x, sy)) + t = """\ + + +""" + return t.format(qr.size + border * 2, " ".join(parts)) diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index c35f750d..7cde5c63 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -345,7 +345,7 @@ var Ls = { "fs_tsrc": "the file or folder to share", "fs_ppwd": "optional password", "fs_w8": "creating share...", - "fs_ok": "
Enter/OK
to Clipboard\npress ESC/Cancel
to Close\n\n",
+ "fs_ok": "press Enter/OK
to Clipboard\npress ESC/Cancel
to Close",
"frt_dec": "may fix some cases of broken filenames\">url-decode",
"frt_rst": "reset modified filenames back to the original ones\">↺ reset",
@@ -913,7 +913,7 @@ var Ls = {
"fs_tsrc": "fil/mappe som skal deles",
"fs_ppwd": "frivillig passord",
"fs_w8": "oppretter deling...",
- "fs_ok": "Enter/OK
for å kopiere linken (for CTRL-V)\ntrykk ESC/Avbryt
for å bare bekrefte\n\n",
+ "fs_ok": "trykk Enter/OK
for å kopiere linken (for CTRL-V)\ntrykk ESC/Avbryt
for å bare bekrefte",
"frt_dec": "kan korrigere visse ødelagte filnavn\">url-decode",
"frt_rst": "nullstiller endringer (tilbake til de originale filnavnene)\">↺ reset",
@@ -1481,7 +1481,7 @@ var Ls = {
"fs_tsrc": "共享的文件或文件夹",
"fs_ppwd": "密码可选",
"fs_w8": "正在创建文件共享...",
- "fs_ok": "Enter/OK
复制到剪贴板\n按 ESC/Cancel
关闭\n\n",
+ "fs_ok": "按 Enter/OK
复制到剪贴板\n按 ESC/Cancel
关闭",
"frt_dec": "可能修复一些损坏的文件名\">url-decode",
"frt_rst": "将修改后的文件名重置为原始文件名\">↺ 重置",
@@ -1788,7 +1788,7 @@ ebi('widget').innerHTML = (
' ' +
'' +
'delete | sharekey | +delete | pw | source | axs | @@ -37,10 +37,13 @@||||
---|---|---|---|---|---|---|---|---|---|
+ qr + {{ k }} + | delete | -{{ k }} | -{{ pw }} | -{{ vp|e }} | +{{ "yes" if pw else "--" }} | +/{{ vp|e }} | {{ pr }} | {{ st }} | {{ un|e }} | diff --git a/copyparty/web/shares.js b/copyparty/web/shares.js index 10e401f7..203042bd 100644 --- a/copyparty/web/shares.js +++ b/copyparty/web/shares.js @@ -12,7 +12,7 @@ function rm() { } function bump() { - var k = this.closest('tr').getElementsByTagName('a')[0].getAttribute('k'), + var k = this.closest('tr').getElementsByTagName('a')[2].getAttribute('k'), u = SR + shr + uricom_enc(k) + '?eshare=' + this.value, xhr = new XHR(); @@ -28,14 +28,36 @@ function cb() { document.location = '?shares'; } +function qr(e) { + ev(e); + var href = this.href, + pw = this.closest('tr').cells[2].textContent; + + if (pw.indexOf('yes') < 0) + return showqr(href); + + modal.prompt("if you want to bypass the password protection by\nembedding the password into the qr-code, then\ntype the password now, otherwise leave this empty", "", function (v) { + if (v) + href += "&pw=" + v; + showqr(href); + }); +} + +function showqr(href) { + var vhref = href.replace('?qr&', '?').replace('?qr', ''); + modal.alert(esc(vhref) + '