share folders as qr-codes

This commit is contained in:
ed 2024-10-03 23:14:06 +00:00
parent 21be82ef8b
commit e45420646f
8 changed files with 98 additions and 20 deletions

View file

@ -37,6 +37,7 @@ from .__version__ import S_VERSION
from .authsrv import VFS # typechk from .authsrv import VFS # typechk
from .bos import bos from .bos import bos
from .star import StreamTar from .star import StreamTar
from .stolen.qrcodegen import QrCode, qr2svg
from .sutil import StreamArc, gfilter from .sutil import StreamArc, gfilter
from .szip import StreamZip from .szip import StreamZip
from .up2k import up2k_chunksize from .up2k import up2k_chunksize
@ -495,6 +496,9 @@ class HttpCli(object):
self.vpath + "/" if self.trailing_slash and self.vpath else self.vpath 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"): if relchk(self.vpath) and (self.vpath != "*" or self.mode != "OPTIONS"):
self.log("invalid relpath [{}]".format(self.vpath)) self.log("invalid relpath [{}]".format(self.vpath))
self.cbonk(self.conn.hsrv.gmal, self.req, "bad_vp", "invalid relpaths") 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}) self.reply(ico, mime=mime, headers={"Last-Modified": lm})
return True 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: def tx_md(self, vn: VFS, fs_path: str) -> bool:
logmsg = " %s @%s " % (self.req, self.uname) logmsg = " %s @%s " % (self.req, self.uname)
@ -4505,9 +4536,6 @@ class HttpCli(object):
if self.uname != self.args.shr_adm: if self.uname != self.args.shr_adm:
rows = [x for x in rows if x[5] == self.uname] 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( html = self.j2s(
"shares", this=self, shr=self.args.shr, rows=rows, now=int(time.time()) "shares", this=self, shr=self.args.shr, rows=rows, now=int(time.time())
) )
@ -4585,7 +4613,7 @@ class HttpCli(object):
else: else:
for zs in vps: for zs in vps:
if zs.endswith("/"): 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) raise Pebkac(400, t)
vp = vps[0].rsplit("/", 1)[0] vp = vps[0].rsplit("/", 1)[0]
for zs in vps: for zs in vps:

View file

@ -594,3 +594,20 @@ def _get_bit(x: int, i: int) -> bool:
class DataTooLongError(ValueError): class DataTooLongError(ValueError):
pass 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 = """\
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 {0} {0}" stroke="none">
<rect width="100%" height="100%" fill="#F7F7F7"/>
<path d="{1}" fill="#111111"/>
</svg>
"""
return t.format(qr.size + border * 2, " ".join(parts))

View file

@ -345,7 +345,7 @@ var Ls = {
"fs_tsrc": "the file or folder to share", "fs_tsrc": "the file or folder to share",
"fs_ppwd": "optional password", "fs_ppwd": "optional password",
"fs_w8": "creating share...", "fs_w8": "creating share...",
"fs_ok": "<h6>share-URL created</h6>\npress <code>Enter/OK</code> to Clipboard\npress <code>ESC/Cancel</code> to Close\n\n", "fs_ok": "press <code>Enter/OK</code> to Clipboard\npress <code>ESC/Cancel</code> to Close",
"frt_dec": "may fix some cases of broken filenames\">url-decode", "frt_dec": "may fix some cases of broken filenames\">url-decode",
"frt_rst": "reset modified filenames back to the original ones\">↺ reset", "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_tsrc": "fil/mappe som skal deles",
"fs_ppwd": "frivillig passord", "fs_ppwd": "frivillig passord",
"fs_w8": "oppretter deling...", "fs_w8": "oppretter deling...",
"fs_ok": "<h6>URL opprettet</h6>\ntrykk <code>Enter/OK</code> for å kopiere linken (for CTRL-V)\ntrykk <code>ESC/Avbryt</code> for å bare bekrefte\n\n", "fs_ok": "trykk <code>Enter/OK</code> for å kopiere linken (for CTRL-V)\ntrykk <code>ESC/Avbryt</code> for å bare bekrefte",
"frt_dec": "kan korrigere visse ødelagte filnavn\">url-decode", "frt_dec": "kan korrigere visse ødelagte filnavn\">url-decode",
"frt_rst": "nullstiller endringer (tilbake til de originale filnavnene)\">↺ reset", "frt_rst": "nullstiller endringer (tilbake til de originale filnavnene)\">↺ reset",
@ -1481,7 +1481,7 @@ var Ls = {
"fs_tsrc": "共享的文件或文件夹", "fs_tsrc": "共享的文件或文件夹",
"fs_ppwd": "密码可选", "fs_ppwd": "密码可选",
"fs_w8": "正在创建文件共享...", "fs_w8": "正在创建文件共享...",
"fs_ok": "<h6>分享链接已创建</h6>\n按 <code>Enter/OK</code> 复制到剪贴板\n按 <code>ESC/Cancel</code> 关闭\n\n", "fs_ok": "按 <code>Enter/OK</code> 复制到剪贴板\n按 <code>ESC/Cancel</code> 关闭",
"frt_dec": "可能修复一些损坏的文件名\">url-decode", "frt_dec": "可能修复一些损坏的文件名\">url-decode",
"frt_rst": "将修改后的文件名重置为原始文件名\">↺ 重置", "frt_rst": "将修改后的文件名重置为原始文件名\">↺ 重置",
@ -1788,7 +1788,7 @@ ebi('widget').innerHTML = (
' <canvas id="barbuf"></canvas>' + ' <canvas id="barbuf"></canvas>' +
'</div>' + '</div>' +
'<div id="np_inf">' + '<div id="np_inf">' +
' <img id="np_img"></span>' + ' <img id="np_img" />' +
' <span id="np_url"></span>' + ' <span id="np_url"></span>' +
' <span id="np_circle"></span>' + ' <span id="np_circle"></span>' +
' <span id="np_album"></span>' + ' <span id="np_album"></span>' +
@ -4596,11 +4596,12 @@ var fileman = (function () {
return; return;
} }
surl = surl.slice(15); surl = surl.slice(15);
modal.confirm(L.fs_ok + esc(surl), function() { var txt = esc(surl) + '<img class="b64" src="' + surl + '?qr" />';
modal.confirm(txt + L.fs_ok, function() {
cliptxt(surl, function () { cliptxt(surl, function () {
toast.ok(2, L.clipped); toast.ok(2, L.clipped);
}); });
}); }, null);
} }
sh_apply.onclick = function () { sh_apply.onclick = function () {

View file

@ -27,7 +27,7 @@ a {
padding: .2em .6em; padding: .2em .6em;
margin: 0 .3em; margin: 0 .3em;
} }
td a { #wrap td a {
margin: 0; margin: 0;
} }
#w { #w {
@ -53,12 +53,13 @@ th {
position: sticky; position: sticky;
background: #f7f7f7; background: #f7f7f7;
} }
td, th { #wrap td,
#wrap th {
padding: .3em .6em; padding: .3em .6em;
text-align: left; text-align: left;
white-space: nowrap; white-space: nowrap;
} }
td+td+td+td+td+td+td+td { #wrap td+td+td+td+td+td+td+td {
font-family: var(--font-mono), monospace, monospace; font-family: var(--font-mono), monospace, monospace;
} }

View file

@ -22,8 +22,8 @@
<span>min/hrs = time left</span> <span>min/hrs = time left</span>
<table id="tab"><thead><tr> <table id="tab"><thead><tr>
<th>delete</th>
<th>sharekey</th> <th>sharekey</th>
<th>delete</th>
<th>pw</th> <th>pw</th>
<th>source</th> <th>source</th>
<th>axs</th> <th>axs</th>
@ -37,10 +37,13 @@
</tr></thead><tbody> </tr></thead><tbody>
{% for k, pw, vp, pr, st, un, t0, t1 in rows %} {% for k, pw, vp, pr, st, un, t0, t1 in rows %}
<tr> <tr>
<td>
<a href="{{ r }}{{ shr }}{{ k }}?qr">qr</a>
<a href="{{ r }}{{ shr }}{{ k }}">{{ k }}</a>
</td>
<td><a href="#" k="{{ k }}">delete</a></td> <td><a href="#" k="{{ k }}">delete</a></td>
<td><a href="{{ r }}{{ shr }}{{ k }}">{{ k }}</a></td> <td>{{ "yes" if pw else "--" }}</td>
<td>{{ pw }}</td> <td><a href="{{ r }}/{{ vp|e }}">/{{ vp|e }}</a></td>
<td><a href="{{ r }}/{{ vp|e }}">{{ vp|e }}</a></td>
<td>{{ pr }}</td> <td>{{ pr }}</td>
<td>{{ st }}</td> <td>{{ st }}</td>
<td>{{ un|e }}</td> <td>{{ un|e }}</td>

View file

@ -12,7 +12,7 @@ function rm() {
} }
function bump() { 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, u = SR + shr + uricom_enc(k) + '?eshare=' + this.value,
xhr = new XHR(); xhr = new XHR();
@ -28,14 +28,36 @@ function cb() {
document.location = '?shares'; 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) + '<img class="b64" src="' + href + '" />');
}
(function() { (function() {
var tab = ebi('tab').tBodies[0], var tab = ebi('tab').tBodies[0],
tr = Array.prototype.slice.call(tab.rows, 0); tr = Array.prototype.slice.call(tab.rows, 0);
var buf = []; var buf = [];
for (var a = 0; a < tr.length; a++) for (var a = 0; a < tr.length; a++) {
tr[a].cells[0].getElementsByTagName('a')[0].onclick = qr;
for (var b = 7; b < 9; b++) for (var b = 7; b < 9; b++)
buf.push(parseInt(tr[a].cells[b].innerHTML)); buf.push(parseInt(tr[a].cells[b].innerHTML));
}
var ibuf = 0; var ibuf = 0;
for (var a = 0; a < tr.length; a++) for (var a = 0; a < tr.length; a++)

View file

@ -293,6 +293,12 @@ html.y #tth {
#modalc a { #modalc a {
color: #07b; color: #07b;
} }
#modalc .b64 {
display: block;
margin: .1em auto;
width: 60%;
height: 60%;
}
#modalb { #modalb {
position: sticky; position: sticky;
text-align: right; text-align: right;

View file

@ -426,7 +426,7 @@ var tl_browser = {
"fs_tsrc": "the file or folder to share", "fs_tsrc": "the file or folder to share",
"fs_ppwd": "optional password", "fs_ppwd": "optional password",
"fs_w8": "creating share...", "fs_w8": "creating share...",
"fs_ok": "<h6>share-URL created</h6>\npress <code>Enter/OK</code> to Clipboard\npress <code>ESC/Cancel</code> to Close\n\n", "fs_ok": "press <code>Enter/OK</code> to Clipboard\npress <code>ESC/Cancel</code> to Close",
"frt_dec": "may fix some cases of broken filenames\">url-decode", "frt_dec": "may fix some cases of broken filenames\">url-decode",
"frt_rst": "reset modified filenames back to the original ones\">↺ reset", "frt_rst": "reset modified filenames back to the original ones\">↺ reset",