mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
shares: allow upload, unpost
* files can be uploaded into writeable shares * add "write-only" button to the create-share ui * unpost is possible while viewing the relevant share
This commit is contained in:
parent
833c6cf2ec
commit
4bdcbc1cb5
|
@ -552,15 +552,14 @@ class VFS(object):
|
||||||
return self._get_dbv(vrem)
|
return self._get_dbv(vrem)
|
||||||
|
|
||||||
shv, srem = src
|
shv, srem = src
|
||||||
return shv, vjoin(srem, vrem)
|
return shv._get_dbv(vjoin(srem, vrem))
|
||||||
|
|
||||||
def _get_dbv(self, vrem: str) -> tuple["VFS", str]:
|
def _get_dbv(self, vrem: str) -> tuple["VFS", str]:
|
||||||
dbv = self.dbv
|
dbv = self.dbv
|
||||||
if not dbv:
|
if not dbv:
|
||||||
return self, vrem
|
return self, vrem
|
||||||
|
|
||||||
tv = [self.vpath[len(dbv.vpath) :].lstrip("/"), vrem]
|
vrem = vjoin(self.vpath[len(dbv.vpath) :].lstrip("/"), vrem)
|
||||||
vrem = "/".join([x for x in tv if x])
|
|
||||||
return dbv, vrem
|
return dbv, vrem
|
||||||
|
|
||||||
def canonical(self, rem: str, resolve: bool = True) -> str:
|
def canonical(self, rem: str, resolve: bool = True) -> str:
|
||||||
|
|
|
@ -105,6 +105,7 @@ from .util import (
|
||||||
unquotep,
|
unquotep,
|
||||||
vjoin,
|
vjoin,
|
||||||
vol_san,
|
vol_san,
|
||||||
|
vroots,
|
||||||
vsplit,
|
vsplit,
|
||||||
wrename,
|
wrename,
|
||||||
wunlink,
|
wunlink,
|
||||||
|
@ -1202,9 +1203,6 @@ class HttpCli(object):
|
||||||
if "stack" in self.uparam:
|
if "stack" in self.uparam:
|
||||||
return self.tx_stack()
|
return self.tx_stack()
|
||||||
|
|
||||||
if "ups" in self.uparam:
|
|
||||||
return self.tx_ups()
|
|
||||||
|
|
||||||
if "setck" in self.uparam:
|
if "setck" in self.uparam:
|
||||||
return self.setck()
|
return self.setck()
|
||||||
|
|
||||||
|
@ -1220,6 +1218,10 @@ class HttpCli(object):
|
||||||
if "h" in self.uparam:
|
if "h" in self.uparam:
|
||||||
return self.tx_mounts()
|
return self.tx_mounts()
|
||||||
|
|
||||||
|
if "ups" in self.uparam:
|
||||||
|
# vpath is used for share translation
|
||||||
|
return self.tx_ups()
|
||||||
|
|
||||||
if "rss" in self.uparam:
|
if "rss" in self.uparam:
|
||||||
return self.tx_rss()
|
return self.tx_rss()
|
||||||
|
|
||||||
|
@ -2408,6 +2410,15 @@ class HttpCli(object):
|
||||||
if "purl" in ret:
|
if "purl" in ret:
|
||||||
ret["purl"] = self.args.SR + ret["purl"]
|
ret["purl"] = self.args.SR + ret["purl"]
|
||||||
|
|
||||||
|
if self.args.shr and self.vpath.startswith(self.args.shr1):
|
||||||
|
# strip common suffix (uploader's folder structure)
|
||||||
|
vp_req, vp_vfs = vroots(self.vpath, vjoin(dbv.vpath, vrem))
|
||||||
|
if not ret["purl"].startswith(vp_vfs):
|
||||||
|
t = "share-mapping failed; req=[%s] dbv=[%s] vrem=[%s] n1=[%s] n2=[%s] purl=[%s]"
|
||||||
|
zt = (self.vpath, dbv.vpath, vrem, vp_req, vp_vfs, ret["purl"])
|
||||||
|
raise Pebkac(500, t % zt)
|
||||||
|
ret["purl"] = vp_req + ret["purl"][len(vp_vfs) :]
|
||||||
|
|
||||||
ret = json.dumps(ret)
|
ret = json.dumps(ret)
|
||||||
self.log(ret)
|
self.log(ret)
|
||||||
self.reply(ret.encode("utf-8"), mime="application/json")
|
self.reply(ret.encode("utf-8"), mime="application/json")
|
||||||
|
@ -2500,7 +2511,11 @@ class HttpCli(object):
|
||||||
chashes.append(siblings[n : n + clen])
|
chashes.append(siblings[n : n + clen])
|
||||||
|
|
||||||
vfs, _ = self.asrv.vfs.get(self.vpath, self.uname, False, True)
|
vfs, _ = self.asrv.vfs.get(self.vpath, self.uname, False, True)
|
||||||
ptop = (vfs.dbv or vfs).realpath
|
ptop = vfs.get_dbv("")[0].realpath
|
||||||
|
# if this is a share, then get_dbv has been overridden to return
|
||||||
|
# the dbv (which does not exist as a property). And its realpath
|
||||||
|
# could point into the middle of its origin vfs node, meaning it
|
||||||
|
# is not necessarily registered with up2k, so get_dbv is crucial
|
||||||
|
|
||||||
broker = self.conn.hsrv.broker
|
broker = self.conn.hsrv.broker
|
||||||
x = broker.ask("up2k.handle_chunks", ptop, wark, chashes)
|
x = broker.ask("up2k.handle_chunks", ptop, wark, chashes)
|
||||||
|
@ -4456,7 +4471,7 @@ class HttpCli(object):
|
||||||
rvol=rvol,
|
rvol=rvol,
|
||||||
wvol=wvol,
|
wvol=wvol,
|
||||||
avol=avol,
|
avol=avol,
|
||||||
in_shr=self.args.shr and self.vpath.startswith(self.args.shr[1:]),
|
in_shr=self.args.shr and self.vpath.startswith(self.args.shr1),
|
||||||
vstate=vstate,
|
vstate=vstate,
|
||||||
ups=ups,
|
ups=ups,
|
||||||
scanning=vs["scanning"],
|
scanning=vs["scanning"],
|
||||||
|
@ -4520,7 +4535,7 @@ class HttpCli(object):
|
||||||
|
|
||||||
t = t.format(self.args.SR)
|
t = t.format(self.args.SR)
|
||||||
qv = quotep(self.vpaths) + self.ourlq()
|
qv = quotep(self.vpaths) + self.ourlq()
|
||||||
in_shr = self.args.shr and self.vpath.startswith(self.args.shr[1:])
|
in_shr = self.args.shr and self.vpath.startswith(self.args.shr1)
|
||||||
html = self.j2s("splash", this=self, qvpath=qv, in_shr=in_shr, msg=t)
|
html = self.j2s("splash", this=self, qvpath=qv, in_shr=in_shr, msg=t)
|
||||||
self.reply(html.encode("utf-8"), status=rc)
|
self.reply(html.encode("utf-8"), status=rc)
|
||||||
return True
|
return True
|
||||||
|
@ -4683,6 +4698,11 @@ class HttpCli(object):
|
||||||
lm = "ups [{}]".format(filt)
|
lm = "ups [{}]".format(filt)
|
||||||
self.log(lm)
|
self.log(lm)
|
||||||
|
|
||||||
|
if self.args.shr and self.vpath.startswith(self.args.shr1):
|
||||||
|
shr_dbv, shr_vrem = self.vn.get_dbv(self.rem)
|
||||||
|
else:
|
||||||
|
shr_dbv = None
|
||||||
|
|
||||||
ret: list[dict[str, Any]] = []
|
ret: list[dict[str, Any]] = []
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
lim = time.time() - self.args.unpost
|
lim = time.time() - self.args.unpost
|
||||||
|
@ -4703,7 +4723,12 @@ class HttpCli(object):
|
||||||
else:
|
else:
|
||||||
allvols = list(self.asrv.vfs.all_vols.values())
|
allvols = list(self.asrv.vfs.all_vols.values())
|
||||||
|
|
||||||
allvols = [x for x in allvols if "e2d" in x.flags]
|
allvols = [
|
||||||
|
x
|
||||||
|
for x in allvols
|
||||||
|
if "e2d" in x.flags
|
||||||
|
and ("*" in x.axs.uwrite or self.uname in x.axs.uwrite or x == shr_dbv)
|
||||||
|
]
|
||||||
|
|
||||||
for vol in allvols:
|
for vol in allvols:
|
||||||
cur = idx.get_cur(vol)
|
cur = idx.get_cur(vol)
|
||||||
|
@ -4753,6 +4778,16 @@ class HttpCli(object):
|
||||||
|
|
||||||
ret = ret[:2000]
|
ret = ret[:2000]
|
||||||
|
|
||||||
|
if shr_dbv:
|
||||||
|
# translate vpaths from share-target to share-url
|
||||||
|
# to satisfy access checks
|
||||||
|
assert shr_vrem.split # type: ignore # !rm
|
||||||
|
vp_shr, vp_vfs = vroots(self.vpath, vjoin(shr_dbv.vpath, shr_vrem))
|
||||||
|
for v in ret:
|
||||||
|
vp = v["vp"]
|
||||||
|
if vp.startswith(vp_vfs):
|
||||||
|
v["vp"] = vp_shr + vp[len(vp_vfs) :]
|
||||||
|
|
||||||
if self.is_vproxied:
|
if self.is_vproxied:
|
||||||
for v in ret:
|
for v in ret:
|
||||||
v["vp"] = self.args.SR + v["vp"]
|
v["vp"] = self.args.SR + v["vp"]
|
||||||
|
@ -4882,7 +4917,7 @@ class HttpCli(object):
|
||||||
if m:
|
if m:
|
||||||
raise Pebkac(400, "sharekey has illegal character [%s]" % (m[1],))
|
raise Pebkac(400, "sharekey has illegal character [%s]" % (m[1],))
|
||||||
|
|
||||||
if vp.startswith(self.args.shr[1:]):
|
if vp.startswith(self.args.shr1):
|
||||||
raise Pebkac(400, "yo dawg...")
|
raise Pebkac(400, "yo dawg...")
|
||||||
|
|
||||||
cur = idx.get_shr()
|
cur = idx.get_shr()
|
||||||
|
|
|
@ -230,6 +230,7 @@ class SvcHub(object):
|
||||||
if not self.args.no_ses:
|
if not self.args.no_ses:
|
||||||
self.setup_session_db()
|
self.setup_session_db()
|
||||||
|
|
||||||
|
args.shr1 = ""
|
||||||
if args.shr:
|
if args.shr:
|
||||||
self.setup_share_db()
|
self.setup_share_db()
|
||||||
|
|
||||||
|
@ -460,6 +461,7 @@ class SvcHub(object):
|
||||||
raise Exception(t)
|
raise Exception(t)
|
||||||
|
|
||||||
al.shr = "/%s/" % (al.shr,)
|
al.shr = "/%s/" % (al.shr,)
|
||||||
|
al.shr1 = al.shr[1:]
|
||||||
|
|
||||||
create = True
|
create = True
|
||||||
modified = False
|
modified = False
|
||||||
|
|
|
@ -3907,11 +3907,9 @@ class Up2k(object):
|
||||||
if unpost:
|
if unpost:
|
||||||
raise Pebkac(400, "cannot unpost folders")
|
raise Pebkac(400, "cannot unpost folders")
|
||||||
elif stat.S_ISLNK(st.st_mode) or stat.S_ISREG(st.st_mode):
|
elif stat.S_ISLNK(st.st_mode) or stat.S_ISREG(st.st_mode):
|
||||||
dbv, vrem = self.asrv.vfs.get(vpath, uname, *permsets[0])
|
voldir = vsplit(rem)[0]
|
||||||
dbv, vrem = dbv.get_dbv(vrem)
|
|
||||||
voldir = vsplit(vrem)[0]
|
|
||||||
vpath_dir = vsplit(vpath)[0]
|
vpath_dir = vsplit(vpath)[0]
|
||||||
g = [(dbv, voldir, vpath_dir, adir, [(fn, 0)], [], {})] # type: ignore
|
g = [(vn, voldir, vpath_dir, adir, [(fn, 0)], [], {})] # type: ignore
|
||||||
else:
|
else:
|
||||||
self.log("rm: skip type-{:x} file [{}]".format(st.st_mode, atop))
|
self.log("rm: skip type-{:x} file [{}]".format(st.st_mode, atop))
|
||||||
return 0, [], []
|
return 0, [], []
|
||||||
|
@ -3938,7 +3936,10 @@ class Up2k(object):
|
||||||
volpath = ("%s/%s" % (vrem, fn)).strip("/")
|
volpath = ("%s/%s" % (vrem, fn)).strip("/")
|
||||||
vpath = ("%s/%s" % (dbv.vpath, volpath)).strip("/")
|
vpath = ("%s/%s" % (dbv.vpath, volpath)).strip("/")
|
||||||
self.log("rm %s\n %s" % (vpath, abspath))
|
self.log("rm %s\n %s" % (vpath, abspath))
|
||||||
_ = dbv.get(volpath, uname, *permsets[0])
|
if not unpost:
|
||||||
|
# recursion-only sanchk
|
||||||
|
_ = dbv.get(volpath, uname, *permsets[0])
|
||||||
|
|
||||||
if xbd:
|
if xbd:
|
||||||
if not runhook(
|
if not runhook(
|
||||||
self.log,
|
self.log,
|
||||||
|
|
|
@ -2204,6 +2204,23 @@ def unquotep(txt: str) -> str:
|
||||||
return w8dec(unq2)
|
return w8dec(unq2)
|
||||||
|
|
||||||
|
|
||||||
|
def vroots(vp1: str, vp2: str) -> tuple[str, str]:
|
||||||
|
"""
|
||||||
|
input("q/w/e/r","a/s/d/e/r") output("/q/w/","/a/s/d/")
|
||||||
|
"""
|
||||||
|
while vp1 and vp2:
|
||||||
|
zt1 = vp1.rsplit("/", 1) if "/" in vp1 else ("", vp1)
|
||||||
|
zt2 = vp2.rsplit("/", 1) if "/" in vp2 else ("", vp2)
|
||||||
|
if zt1[1] != zt2[1]:
|
||||||
|
break
|
||||||
|
vp1 = zt1[0]
|
||||||
|
vp2 = zt2[0]
|
||||||
|
return (
|
||||||
|
"/%s/" % (vp1,) if vp1 else "/",
|
||||||
|
"/%s/" % (vp2,) if vp2 else "/",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def vsplit(vpath: str) -> tuple[str, str]:
|
def vsplit(vpath: str) -> tuple[str, str]:
|
||||||
if "/" not in vpath:
|
if "/" not in vpath:
|
||||||
return "", vpath
|
return "", vpath
|
||||||
|
|
|
@ -4510,9 +4510,12 @@ var fileman = (function () {
|
||||||
'<tr><td>perms</td><td class="sh_axs">',
|
'<tr><td>perms</td><td class="sh_axs">',
|
||||||
];
|
];
|
||||||
for (var a = 0; a < perms.length; a++)
|
for (var a = 0; a < perms.length; a++)
|
||||||
if (perms[a] != 'admin')
|
if (!has(['admin', 'move'], perms[a]))
|
||||||
html.push('<a href="#" class="tgl btn">' + perms[a] + '</a>');
|
html.push('<a href="#" class="tgl btn">' + perms[a] + '</a>');
|
||||||
|
|
||||||
|
if (has(perms, 'write'))
|
||||||
|
html.push('<a href="#" class="btn">write-only</a>');
|
||||||
|
|
||||||
html.push('</td></tr></div');
|
html.push('</td></tr></div');
|
||||||
shui.innerHTML = html.join('\n');
|
shui.innerHTML = html.join('\n');
|
||||||
|
|
||||||
|
@ -4576,6 +4579,9 @@ var fileman = (function () {
|
||||||
|
|
||||||
function shspf() {
|
function shspf() {
|
||||||
clmod(this, 'on', 't');
|
clmod(this, 'on', 't');
|
||||||
|
if (this.textContent == 'write-only')
|
||||||
|
for (var a = 0; a < pbtns.length; a++)
|
||||||
|
clmod(pbtns[a], 'on', pbtns[a].textContent == 'write');
|
||||||
}
|
}
|
||||||
clmod(pbtns[0], 'on', 1);
|
clmod(pbtns[0], 'on', 1);
|
||||||
|
|
||||||
|
@ -7380,6 +7386,9 @@ var treectl = (function () {
|
||||||
r.ls_cb = null;
|
r.ls_cb = null;
|
||||||
fun();
|
fun();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (window.have_shr && QS('#op_unpost.act') && (cdir.startsWith(SR + have_shr) || get_evpath().startsWith(SR + have_shr)))
|
||||||
|
goto('unpost');
|
||||||
}
|
}
|
||||||
|
|
||||||
r.chk_index_html = function (top, res) {
|
r.chk_index_html = function (top, res) {
|
||||||
|
@ -9115,7 +9124,7 @@ var unpost = (function () {
|
||||||
r.me = me;
|
r.me = me;
|
||||||
}
|
}
|
||||||
|
|
||||||
var q = SR + '/?ups';
|
var q = get_evpath() + '?ups';
|
||||||
if (filt.value)
|
if (filt.value)
|
||||||
q += '&filter=' + uricom_enc(filt.value, true);
|
q += '&filter=' + uricom_enc(filt.value, true);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue