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)
|
||||
|
||||
shv, srem = src
|
||||
return shv, vjoin(srem, vrem)
|
||||
return shv._get_dbv(vjoin(srem, vrem))
|
||||
|
||||
def _get_dbv(self, vrem: str) -> tuple["VFS", str]:
|
||||
dbv = self.dbv
|
||||
if not dbv:
|
||||
return self, vrem
|
||||
|
||||
tv = [self.vpath[len(dbv.vpath) :].lstrip("/"), vrem]
|
||||
vrem = "/".join([x for x in tv if x])
|
||||
vrem = vjoin(self.vpath[len(dbv.vpath) :].lstrip("/"), vrem)
|
||||
return dbv, vrem
|
||||
|
||||
def canonical(self, rem: str, resolve: bool = True) -> str:
|
||||
|
|
|
@ -105,6 +105,7 @@ from .util import (
|
|||
unquotep,
|
||||
vjoin,
|
||||
vol_san,
|
||||
vroots,
|
||||
vsplit,
|
||||
wrename,
|
||||
wunlink,
|
||||
|
@ -1202,9 +1203,6 @@ class HttpCli(object):
|
|||
if "stack" in self.uparam:
|
||||
return self.tx_stack()
|
||||
|
||||
if "ups" in self.uparam:
|
||||
return self.tx_ups()
|
||||
|
||||
if "setck" in self.uparam:
|
||||
return self.setck()
|
||||
|
||||
|
@ -1220,6 +1218,10 @@ class HttpCli(object):
|
|||
if "h" in self.uparam:
|
||||
return self.tx_mounts()
|
||||
|
||||
if "ups" in self.uparam:
|
||||
# vpath is used for share translation
|
||||
return self.tx_ups()
|
||||
|
||||
if "rss" in self.uparam:
|
||||
return self.tx_rss()
|
||||
|
||||
|
@ -2408,6 +2410,15 @@ class HttpCli(object):
|
|||
if "purl" in ret:
|
||||
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)
|
||||
self.log(ret)
|
||||
self.reply(ret.encode("utf-8"), mime="application/json")
|
||||
|
@ -2500,7 +2511,11 @@ class HttpCli(object):
|
|||
chashes.append(siblings[n : n + clen])
|
||||
|
||||
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
|
||||
x = broker.ask("up2k.handle_chunks", ptop, wark, chashes)
|
||||
|
@ -4456,7 +4471,7 @@ class HttpCli(object):
|
|||
rvol=rvol,
|
||||
wvol=wvol,
|
||||
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,
|
||||
ups=ups,
|
||||
scanning=vs["scanning"],
|
||||
|
@ -4520,7 +4535,7 @@ class HttpCli(object):
|
|||
|
||||
t = t.format(self.args.SR)
|
||||
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)
|
||||
self.reply(html.encode("utf-8"), status=rc)
|
||||
return True
|
||||
|
@ -4683,6 +4698,11 @@ class HttpCli(object):
|
|||
lm = "ups [{}]".format(filt)
|
||||
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]] = []
|
||||
t0 = time.time()
|
||||
lim = time.time() - self.args.unpost
|
||||
|
@ -4703,7 +4723,12 @@ class HttpCli(object):
|
|||
else:
|
||||
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:
|
||||
cur = idx.get_cur(vol)
|
||||
|
@ -4753,6 +4778,16 @@ class HttpCli(object):
|
|||
|
||||
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:
|
||||
for v in ret:
|
||||
v["vp"] = self.args.SR + v["vp"]
|
||||
|
@ -4882,7 +4917,7 @@ class HttpCli(object):
|
|||
if m:
|
||||
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...")
|
||||
|
||||
cur = idx.get_shr()
|
||||
|
|
|
@ -230,6 +230,7 @@ class SvcHub(object):
|
|||
if not self.args.no_ses:
|
||||
self.setup_session_db()
|
||||
|
||||
args.shr1 = ""
|
||||
if args.shr:
|
||||
self.setup_share_db()
|
||||
|
||||
|
@ -460,6 +461,7 @@ class SvcHub(object):
|
|||
raise Exception(t)
|
||||
|
||||
al.shr = "/%s/" % (al.shr,)
|
||||
al.shr1 = al.shr[1:]
|
||||
|
||||
create = True
|
||||
modified = False
|
||||
|
|
|
@ -3907,11 +3907,9 @@ class Up2k(object):
|
|||
if unpost:
|
||||
raise Pebkac(400, "cannot unpost folders")
|
||||
elif stat.S_ISLNK(st.st_mode) or stat.S_ISREG(st.st_mode):
|
||||
dbv, vrem = self.asrv.vfs.get(vpath, uname, *permsets[0])
|
||||
dbv, vrem = dbv.get_dbv(vrem)
|
||||
voldir = vsplit(vrem)[0]
|
||||
voldir = vsplit(rem)[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:
|
||||
self.log("rm: skip type-{:x} file [{}]".format(st.st_mode, atop))
|
||||
return 0, [], []
|
||||
|
@ -3938,7 +3936,10 @@ class Up2k(object):
|
|||
volpath = ("%s/%s" % (vrem, fn)).strip("/")
|
||||
vpath = ("%s/%s" % (dbv.vpath, volpath)).strip("/")
|
||||
self.log("rm %s\n %s" % (vpath, abspath))
|
||||
if not unpost:
|
||||
# recursion-only sanchk
|
||||
_ = dbv.get(volpath, uname, *permsets[0])
|
||||
|
||||
if xbd:
|
||||
if not runhook(
|
||||
self.log,
|
||||
|
|
|
@ -2204,6 +2204,23 @@ def unquotep(txt: str) -> str:
|
|||
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]:
|
||||
if "/" not in vpath:
|
||||
return "", vpath
|
||||
|
|
|
@ -4510,9 +4510,12 @@ var fileman = (function () {
|
|||
'<tr><td>perms</td><td class="sh_axs">',
|
||||
];
|
||||
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>');
|
||||
|
||||
if (has(perms, 'write'))
|
||||
html.push('<a href="#" class="btn">write-only</a>');
|
||||
|
||||
html.push('</td></tr></div');
|
||||
shui.innerHTML = html.join('\n');
|
||||
|
||||
|
@ -4576,6 +4579,9 @@ var fileman = (function () {
|
|||
|
||||
function shspf() {
|
||||
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);
|
||||
|
||||
|
@ -7380,6 +7386,9 @@ var treectl = (function () {
|
|||
r.ls_cb = null;
|
||||
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) {
|
||||
|
@ -9115,7 +9124,7 @@ var unpost = (function () {
|
|||
r.me = me;
|
||||
}
|
||||
|
||||
var q = SR + '/?ups';
|
||||
var q = get_evpath() + '?ups';
|
||||
if (filt.value)
|
||||
q += '&filter=' + uricom_enc(filt.value, true);
|
||||
|
||||
|
|
Loading…
Reference in a new issue