Merge branch 'hovudstraum' of https://github.com/9001/copyparty into hovudstraum

This commit is contained in:
stackxp 2025-12-23 20:34:57 +01:00
commit 9b0ca03cef
11 changed files with 83 additions and 50 deletions

View file

@ -93,6 +93,13 @@ LEELOO_DALLAS = "leeloo_dallas"
## thanks for coming to my ted talk ## thanks for coming to my ted talk
UP_MTE_MAP = { # db-order
"w": "substr(w,1,16)",
"up_ip": "ip",
".up_at": "at",
"up_by": "un",
}
SEE_LOG = "see log for details" SEE_LOG = "see log for details"
SEESLOG = " (see serverlog for details)" SEESLOG = " (see serverlog for details)"
SSEELOG = " ({})".format(SEE_LOG) SSEELOG = " ({})".format(SEE_LOG)
@ -1058,6 +1065,7 @@ class AuthSrv(object):
"tcolor": self.args.tcolor, "tcolor": self.args.tcolor,
"du_iwho": self.args.du_iwho, "du_iwho": self.args.du_iwho,
"shr_who": self.args.shr_who if self.args.shr else "no", "shr_who": self.args.shr_who if self.args.shr else "no",
"ls_q_m": ("", ""),
} }
self._vf0 = self._vf0b.copy() self._vf0 = self._vf0b.copy()
self._vf0["d2d"] = True self._vf0["d2d"] = True
@ -2672,6 +2680,12 @@ class AuthSrv(object):
self.log(t.format(vol.vpath, mtp), 1) self.log(t.format(vol.vpath, mtp), 1)
errors = True errors = True
mte = vol.flags.get("mte") or {}
up_m = [x for x in UP_MTE_MAP if x in mte]
up_q = [UP_MTE_MAP[x] for x in up_m]
zs = "select %s from up where rd=? and fn=?" % (", ".join(up_q),)
vol.flags["ls_q_m"] = (zs if up_m else "", up_m)
for vol in vfs.all_nodes.values(): for vol in vfs.all_nodes.values():
if not vol.flags.get("is_file"): if not vol.flags.get("is_file"):
continue continue

View file

@ -174,7 +174,7 @@ class FtpFs(AbstractedFS):
t = "Unsupported characters in [{}]" t = "Unsupported characters in [{}]"
raise FSE(t.format(vpath), 1) raise FSE(t.format(vpath), 1)
fn = sanitize_fn(fn or "", "") fn = sanitize_fn(fn or "")
vpath = vjoin(rd, fn) vpath = vjoin(rd, fn)
vfs, rem = self.hub.asrv.vfs.get(vpath, self.uname, r, w, m, d) vfs, rem = self.hub.asrv.vfs.get(vpath, self.uname, r, w, m, d)
if ( if (

View file

@ -2419,7 +2419,7 @@ class HttpCli(object):
if rnd: if rnd:
fn = rand_name(fdir, fn, rnd) fn = rand_name(fdir, fn, rnd)
fn = sanitize_fn(fn or "", "") fn = sanitize_fn(fn or "")
path = os.path.join(fdir, fn) path = os.path.join(fdir, fn)
@ -2857,7 +2857,7 @@ class HttpCli(object):
name = name.translate(tl) name = name.translate(tl)
dbv, vrem = vfs.get_dbv(rem) dbv, vrem = vfs.get_dbv(rem)
name = sanitize_fn(name, "") name = sanitize_fn(name)
if ( if (
not self.can_read not self.can_read
and self.can_write and self.can_write
@ -2875,7 +2875,7 @@ class HttpCli(object):
body["addr"] = self.ip body["addr"] = self.ip
body["vcfg"] = dbv.flags body["vcfg"] = dbv.flags
if not self.can_delete: if not self.can_delete and not body.get("replace") == "skip":
body.pop("replace", None) body.pop("replace", None)
if rem: if rem:
@ -3353,7 +3353,7 @@ class HttpCli(object):
if "nosub" in vfs.flags: if "nosub" in vfs.flags:
raise Pebkac(403, "mkdir is forbidden below this folder") raise Pebkac(403, "mkdir is forbidden below this folder")
rem = sanitize_vpath(rem, "/") rem = sanitize_vpath(rem)
fn = vfs.canonical(rem) fn = vfs.canonical(rem)
if not nullwrite: if not nullwrite:
@ -3397,7 +3397,7 @@ class HttpCli(object):
t = "you can only create .md files because you don't have the delete-permission" t = "you can only create .md files because you don't have the delete-permission"
raise Pebkac(400, t) raise Pebkac(400, t)
sanitized = sanitize_fn(new_file, "") sanitized = sanitize_fn(new_file)
fdir = vfs.canonical(rem) fdir = vfs.canonical(rem)
fn = os.path.join(fdir, sanitized) fn = os.path.join(fdir, sanitized)
@ -3549,7 +3549,7 @@ class HttpCli(object):
# fallthrough # fallthrough
fdir = fdir_base fdir = fdir_base
fname = sanitize_fn(p_file or "", "") fname = sanitize_fn(p_file or "")
abspath = os.path.join(fdir, fname) abspath = os.path.join(fdir, fname)
suffix = "-%.6f-%s" % (time.time(), dip) suffix = "-%.6f-%s" % (time.time(), dip)
if p_file and not nullwrite: if p_file and not nullwrite:
@ -7032,18 +7032,8 @@ class HttpCli(object):
if self.can_admin: if self.can_admin:
up_q = "select substr(w,1,16), ip, at, un from up where rd=? and fn=?" up_q = "select substr(w,1,16), ip, at, un from up where rd=? and fn=?"
up_m = ["w", "up_ip", ".up_at", "up_by"] up_m = ["w", "up_ip", ".up_at", "up_by"]
elif ".up_at" in mte:
if "w" in mte:
up_q = "select substr(w,1,16), at from up where rd=? and fn=?"
up_m = ["w", ".up_at"]
else: else:
up_q = "select at from up where rd=? and fn=?" up_q, up_m = vn.flags["ls_q_m"]
up_m = [".up_at"]
elif "w" in mte:
up_q = "select substr(w,1,16) from up where rd=? and fn=?"
up_m = ["w"]
else:
up_q = ""
mt_q = "select mt.k, mt.v from up inner join mt on mt.w = substr(up.w,1,16) where up.rd = ? and up.fn = ? and +mt.k != 'x'" mt_q = "select mt.k, mt.v from up inner join mt on mt.w = substr(up.w,1,16) where up.rd = ? and up.fn = ? and +mt.k != 'x'"
for fe in files: for fe in files:

View file

@ -8,7 +8,7 @@ import time
from .authsrv import AuthSrv from .authsrv import AuthSrv
from .bos import bos from .bos import bos
from .sutil import StreamArc, errdesc from .sutil import StreamArc, errdesc
from .util import min_ex, sanitize_fn, spack, sunpack, yieldfile, zlib from .util import VPTL_WIN, min_ex, sanitize_to, spack, sunpack, yieldfile, zlib
if True: # pylint: disable=using-constant-test if True: # pylint: disable=using-constant-test
from typing import Any, Generator, Optional from typing import Any, Generator, Optional
@ -104,7 +104,7 @@ def gen_hdr(
ret += spack(b"<LL", vsz, vsz) ret += spack(b"<LL", vsz, vsz)
# windows support (the "?" replace below too) # windows support (the "?" replace below too)
fn = sanitize_fn(fn, "/") fn = sanitize_to(fn, VPTL_WIN)
bfn = fn.encode("utf-8" if utf8 else "cp437", "replace").replace(b"?", b"_") bfn = fn.encode("utf-8" if utf8 else "cp437", "replace").replace(b"?", b"_")
# add ntfs (0x24) and/or unix (0x10) extrafields for utc, add z64 if requested # add ntfs (0x24) and/or unix (0x10) extrafields for utc, add z64 if requested

View file

@ -3323,7 +3323,7 @@ class Up2k(object):
job["ptop"] = vfs.realpath job["ptop"] = vfs.realpath
job["vtop"] = vfs.vpath job["vtop"] = vfs.vpath
job["prel"] = rem job["prel"] = rem
job["name"] = sanitize_fn(job["name"], "") job["name"] = sanitize_fn(job["name"])
ud2 = (vfs.vpath, job["prel"], job["name"]) ud2 = (vfs.vpath, job["prel"], job["name"])
if ud1 != ud2: if ud1 != ud2:
# print(json.dumps(job, sort_keys=True, indent=4)) # print(json.dumps(job, sort_keys=True, indent=4))
@ -3468,7 +3468,15 @@ class Up2k(object):
fp = djoin(fdir, fname) fp = djoin(fdir, fname)
ow = job.get("replace") and bos.path.exists(fp) ow = job.get("replace") and bos.path.exists(fp)
if ow and "mt" in str(job["replace"]).lower(): if ow:
replace_arg = str(job["replace"]).lower()
if ow and "skip" in replace_arg:
self.log("skipping upload, filename already exists: %r" % fp)
err = "upload rejected, a file with that name already exists"
raise Pebkac(409, err)
if ow and "mt" in replace_arg:
mts = bos.stat(fp).st_mtime mts = bos.stat(fp).st_mtime
mtc = job["lmod"] mtc = job["lmod"]
if mtc < mts: if mtc < mts:
@ -5186,7 +5194,7 @@ class Up2k(object):
job["ptop"] = vfs.realpath job["ptop"] = vfs.realpath
job["vtop"] = vfs.vpath job["vtop"] = vfs.vpath
job["prel"] = rem job["prel"] = rem
job["name"] = sanitize_fn(job["name"], "") job["name"] = sanitize_fn(job["name"])
ud2 = (vfs.vpath, job["prel"], job["name"]) ud2 = (vfs.vpath, job["prel"], job["name"])
if ud1 != ud2: if ud1 != ud2:
self.log("xbu reloc2:%d..." % (depth,), 6) self.log("xbu reloc2:%d..." % (depth,), 6)

View file

@ -2245,12 +2245,15 @@ def undot(path: str) -> str:
return "/".join(ret) return "/".join(ret)
def sanitize_fn(fn: str, ok: str) -> str: def sanitize_fn(fn: str) -> str:
if "/" not in ok:
fn = fn.replace("\\", "/").split("/")[-1] fn = fn.replace("\\", "/").split("/")[-1]
if APTL_OS: if APTL_OS:
fn = fn.translate(APTL_OS) fn = sanitize_to(fn, APTL_OS)
return fn.strip()
def sanitize_to(fn: str, tl: dict[int, int]) -> str:
fn = fn.translate(tl)
if ANYWIN: if ANYWIN:
bad = ["con", "prn", "aux", "nul"] bad = ["con", "prn", "aux", "nul"]
for n in range(1, 10): for n in range(1, 10):
@ -2258,15 +2261,14 @@ def sanitize_fn(fn: str, ok: str) -> str:
if fn.lower().split(".")[0] in bad: if fn.lower().split(".")[0] in bad:
fn = "_" + fn fn = "_" + fn
return fn
return fn.strip()
def sanitize_vpath(vp: str, ok: str) -> str: def sanitize_vpath(vp: str) -> str:
if not FNTL_OS: if not FNTL_OS:
return vp return vp
parts = vp.replace(os.sep, "/").split("/") parts = vp.replace(os.sep, "/").split("/")
ret = [sanitize_fn(x, ok) for x in parts] ret = [sanitize_to(x, APTL_OS) for x in parts]
return "/".join(ret) return "/".join(ret)

View file

@ -1014,7 +1014,9 @@ window.baguetteBox = (function () {
if (index >= imagesElements.length) if (index >= imagesElements.length)
return bounceAnimation(options.readDirRtl ? 'left' : 'right'); return bounceAnimation(options.readDirRtl ? 'left' : 'right');
var orot;
try { try {
orot = vidimg().getAttribute('rot');
vid().pause(); vid().pause();
} }
catch (ex) { } catch (ex) { }
@ -1032,6 +1034,9 @@ window.baguetteBox = (function () {
else if (toast.tag == 'bb-ded') else if (toast.tag == 'bb-ded')
toast.hide(); toast.hide();
if (orot && im.getAttribute('rot') === null)
rotn(orot / 90, 1);
if (options.animation == 'none') if (options.animation == 'none')
unvid(vid()); unvid(vid());
else else
@ -1048,7 +1053,7 @@ window.baguetteBox = (function () {
} }
var prev_cw = 0, prev_ch = 0, unrot_timer = null; var prev_cw = 0, prev_ch = 0, unrot_timer = null;
function rotn(n) { function rotn(n, asap) {
var el = vidimg(), var el = vidimg(),
orot = parseInt(el.getAttribute('rot') || 0), orot = parseInt(el.getAttribute('rot') || 0),
frot = orot + (n || 0) * 90; frot = orot + (n || 0) * 90;
@ -1063,6 +1068,8 @@ window.baguetteBox = (function () {
if (!n && prev_cw === cw && prev_ch === ch) if (!n && prev_cw === cw && prev_ch === ch)
return; // reflow noop return; // reflow noop
clmod(el, 'asap', asap);
prev_cw = cw; prev_cw = cw;
prev_ch = ch; prev_ch = ch;
var rot = frot, var rot = frot,
@ -1072,8 +1079,13 @@ window.baguetteBox = (function () {
dl = el.closest('div').querySelector('figcaption a'), dl = el.closest('div').querySelector('figcaption a'),
vw = cw, vw = cw,
vh = ch - dl.offsetHeight + magic, vh = ch - dl.offsetHeight + magic,
pmag = Math.min(1, Math.min(vw / ih, vh / iw)), pmag = Math.min(vw / ih, vh / iw),
wmag = Math.min(1, Math.min(vw / iw, vh / ih)); wmag = Math.min(vw / iw, vh / ih);
if (!options.bbzoom) {
pmag = Math.min(1, pmag);
wmag = Math.min(1, wmag);
}
while (rot < 0) rot += 360; while (rot < 0) rot += 360;
while (rot >= 360) rot -= 360; while (rot >= 360) rot -= 360;
@ -1117,7 +1129,7 @@ window.baguetteBox = (function () {
return; return;
clmod(el, 'nt', 1); clmod(el, 'nt', 1);
el.removeAttribute('rot'); el.setAttribute('rot', 0);
el.removeAttribute("style"); el.removeAttribute("style");
rot = el.offsetHeight; rot = el.offsetHeight;
clmod(el, 'nt'); clmod(el, 'nt');

View file

@ -2166,6 +2166,10 @@ html.noscroll .sbar::-webkit-scrollbar {
vertical-align: middle; vertical-align: middle;
transition: transform .23s, left .23s, top .23s, width .23s, height .23s; transition: transform .23s, left .23s, top .23s, width .23s, height .23s;
} }
.full-image img.asap,
.full-image video.asap {
transition: none;
}
#bbox-overlay.fill .full-image img, #bbox-overlay.fill .full-image img,
#bbox-overlay.fill .full-image video { #bbox-overlay.fill .full-image video {
width: 100%; width: 100%;

View file

@ -158,7 +158,7 @@ if (1)
"ul_par": "parallel uploads:", "ul_par": "parallel uploads:",
"ut_rand": "randomize filenames", "ut_rand": "randomize filenames",
"ut_u2ts": "copy the last-modified timestamp$Nfrom your filesystem to the server\">📅", "ut_u2ts": "copy the last-modified timestamp$Nfrom your filesystem to the server\">📅",
"ut_ow": "overwrite existing files on the server?$N🛡: never (will generate a new filename instead)$N🕒: overwrite if server-file is older than yours$N♻: always overwrite if the files are different", "ut_ow": "overwrite existing files on the server?$N🛡: never (will generate a new filename instead)$N🕒: overwrite if server-file is older than yours$N♻: always overwrite if the files are different$N⏭: skip uploading if the server-file exists, regardless of contents",
"ut_mt": "continue hashing other files while uploading$N$Nmaybe disable if your CPU or HDD is a bottleneck", "ut_mt": "continue hashing other files while uploading$N$Nmaybe disable if your CPU or HDD is a bottleneck",
"ut_ask": 'ask for confirmation before upload starts">💭', "ut_ask": 'ask for confirmation before upload starts">💭',
"ut_pot": "improve upload speed on slow devices$Nby making the UI less complex", "ut_pot": "improve upload speed on slow devices$Nby making the UI less complex",

View file

@ -155,7 +155,7 @@ Ls.deu = {
"ul_par": "Parallele Uploads:", "ul_par": "Parallele Uploads:",
"ut_rand": "Zufällige Dateinamen", "ut_rand": "Zufällige Dateinamen",
"ut_u2ts": "Zuletzt geändert-Zeitstempel von$Ndeinem Dateisystem auf den Server übertragen\">📅", "ut_u2ts": "Zuletzt geändert-Zeitstempel von$Ndeinem Dateisystem auf den Server übertragen\">📅",
"ut_ow": "Existierende Dateien auf dem Server überschreiben?$N🛡: Nie (generiert einen neuen Dateinamen)$N🕒: Überschreiben, wenn Server-Datei älter ist als meine$N♻: Überschreiben, wenn der Dateiinhalt anders ist", "ut_ow": "Existierende Dateien auf dem Server überschreiben?$N🛡: Nie (generiert einen neuen Dateinamen)$N🕒: Überschreiben, wenn Server-Datei älter ist als meine$N♻: Überschreiben, wenn der Dateiinhalt anders ist$N⏭: Nur hochladen, wenn keine Datei mit dem gleichen Namen existiert",
"ut_mt": "Andere Dateien während des Uploads hashen$N$Nsolltest du deaktivieren, falls deine CPU oder Festplatte zum Flaschenhals werden könnte", "ut_mt": "Andere Dateien während des Uploads hashen$N$Nsolltest du deaktivieren, falls deine CPU oder Festplatte zum Flaschenhals werden könnte",
"ut_ask": 'Vor dem Upload nach Bestätigung fragen">💭', "ut_ask": 'Vor dem Upload nach Bestätigung fragen">💭',
"ut_pot": "Verbessert Upload-Geschwindigkeit$Nindem das UI weniger komplex gemacht wird", "ut_pot": "Verbessert Upload-Geschwindigkeit$Nindem das UI weniger komplex gemacht wird",

View file

@ -900,19 +900,19 @@ function up2k_init(subtle) {
bcfg_bind(uc, 'upnag', 'upnag', false, set_upnag); bcfg_bind(uc, 'upnag', 'upnag', false, set_upnag);
bcfg_bind(uc, 'upsfx', 'upsfx', false, set_upsfx); bcfg_bind(uc, 'upsfx', 'upsfx', false, set_upsfx);
uc.ow = parseInt(sread('u2ow', ['0', '1', '2']) || u2ow); uc.ow = parseInt(sread('u2ow', ['0', '1', '2', '3']) || u2ow);
uc.owt = ['🛡️', '🕒', '♻️']; uc.owt = ['🛡️', '🕒', '♻️', '⏭️'];
function set_ow() { function set_ow() {
QS('label[for="u2ow"]').innerHTML = uc.owt[uc.ow]; QS('label[for="u2ow"]').innerHTML = uc.owt[uc.ow];
ebi('u2ow').checked = true; //cosmetic ebi('u2ow').checked = true; //cosmetic
} }
ebi('u2ow').onclick = function (e) { ebi('u2ow').onclick = function (e) {
ev(e); ev(e);
if (++uc.ow > 2) if (++uc.ow > 3)
uc.ow = 0; uc.ow = 0;
swrite('u2ow', uc.ow); swrite('u2ow', uc.ow);
set_ow(); set_ow();
if (uc.ow && !has(perms, 'delete')) if (uc.ow && uc.ow !== 3 && !has(perms, 'delete'))
toast.warn(10, L.u_enoow, 'noow'); toast.warn(10, L.u_enoow, 'noow');
else if (toast.tag == 'noow') else if (toast.tag == 'noow')
toast.hide(); toast.hide();
@ -2727,9 +2727,10 @@ function up2k_init(subtle) {
var err_pend = rsp.indexOf('partial upload exists at a different') + 1, var err_pend = rsp.indexOf('partial upload exists at a different') + 1,
err_srcb = rsp.indexOf('source file busy; please try again') + 1, err_srcb = rsp.indexOf('source file busy; please try again') + 1,
err_plug = rsp.indexOf('upload blocked by x') + 1, err_plug = rsp.indexOf('upload blocked by x') + 1,
err_dupe = rsp.indexOf('upload rejected, file already exists') + 1; err_dupe = rsp.indexOf('upload rejected, file already exists') + 1,
err_exists = rsp.indexOf('upload rejected, a file with that name already exists') + 1;
if (err_pend || err_srcb || err_plug || err_dupe) { if (err_pend || err_srcb || err_plug || err_dupe || err_exists) {
err = rsp; err = rsp;
ofs = err.indexOf('\n/'); ofs = err.indexOf('\n/');
if (ofs !== -1) { if (ofs !== -1) {
@ -2793,6 +2794,8 @@ function up2k_init(subtle) {
req.replace = 'mt'; req.replace = 'mt';
if (uc.ow == 2) if (uc.ow == 2)
req.replace = true; req.replace = true;
if (uc.ow == 3)
req.replace = 'skip';
} }
xhr.open('POST', t.purl, true); xhr.open('POST', t.purl, true);