mirror of
https://github.com/9001/copyparty.git
synced 2025-10-03 01:39:26 +02:00
Compare commits
5 commits
6912e86747
...
6f6b70ad04
Author | SHA1 | Date | |
---|---|---|---|
![]() |
6f6b70ad04 | ||
![]() |
e187df28f2 | ||
![]() |
df0fa9d1b7 | ||
![]() |
397ed5653b | ||
![]() |
9f46e4dbd7 |
11 changed files with 273 additions and 116 deletions
60
bin/hooks/reject-and-explain.py
Normal file
60
bin/hooks/reject-and-explain.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
_ = r"""
|
||||
reject file upload (with a nice explanation why)
|
||||
|
||||
example usage as global config:
|
||||
--xbu j,c1,bin/hooks/reject-and-explain.py
|
||||
|
||||
example usage as a volflag (per-volume config):
|
||||
-v srv/inc:inc:r:rw,ed:c,xbu=j,c1,bin/hooks/reject-and-explain.py
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
(share filesystem-path srv/inc as volume /inc,
|
||||
readable by everyone, read-write for user 'ed',
|
||||
running this plugin on all uploads with the params listed below)
|
||||
|
||||
example usage as a volflag in a copyparty config file:
|
||||
[/inc]
|
||||
srv/inc
|
||||
accs:
|
||||
r: *
|
||||
rw: ed
|
||||
flags:
|
||||
xbu: j,c1,bin/hooks/reject-and-explain.py
|
||||
|
||||
parameters explained,
|
||||
xbu = execute-before-upload (can also be xau, execute-after-upload)
|
||||
j = this hook needs upload information as json (not just the filename)
|
||||
c1 = this hook returns json on stdout, so tell copyparty to read that
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
inf = json.loads(sys.argv[1])
|
||||
vdir, fn = os.path.split(inf["vp"])
|
||||
print("inf[vp] = %r" % (inf["vp"],), file=sys.stderr)
|
||||
|
||||
# the following is what decides if we'll accept the upload or reject it:
|
||||
# we check if the upload-folder url matches the following regex-pattern:
|
||||
ok = re.search(r"(^|/)day[0-9]+$", vdir, re.IGNORECASE)
|
||||
|
||||
if ok:
|
||||
# allow the upload
|
||||
print("{}")
|
||||
return
|
||||
|
||||
# the upload was rejected; display the following errortext:
|
||||
errmsg = "Files can only be uploaded into a folder named 'DayN' where N is a number, for example 'Day573'. This file was REJECTED: "
|
||||
errmsg += inf["vp"] # if you want to mention the file's url at the end of the message
|
||||
print(json.dumps({"rejectmsg": errmsg}))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -956,7 +956,7 @@ def get_sects():
|
|||
\033[36m{{vf.thsize}} \033[35mthumbnail size
|
||||
\033[36m{{srv.itime}} \033[35mserver time in seconds
|
||||
\033[36m{{srv.htime}} \033[35mserver time as YY-mm-dd, HH:MM:SS (UTC)
|
||||
\033[36m{{hdr.cf_ipcountry}} \033[35mthe "CF-IPCountry" client header (probably blank)
|
||||
\033[36m{{hdr.cf-ipcountry}} \033[35mthe "CF-IPCountry" client header (probably blank)
|
||||
\033[0m
|
||||
so the following types of placeholders can be added to the lists:
|
||||
* any client header can be accessed through {{hdr.*}}
|
||||
|
|
|
@ -493,24 +493,30 @@ class FtpHandler(FTPHandler):
|
|||
return
|
||||
self.vfs_map[ap] = vp
|
||||
xbu = vfs.flags.get("xbu")
|
||||
if xbu and not runhook(
|
||||
None,
|
||||
None,
|
||||
self.hub.up2k,
|
||||
"xbu.ftpd",
|
||||
xbu,
|
||||
ap,
|
||||
vp,
|
||||
"",
|
||||
self.uname,
|
||||
self.hub.asrv.vfs.get_perms(vp, self.uname),
|
||||
0,
|
||||
0,
|
||||
self.cli_ip,
|
||||
time.time(),
|
||||
"",
|
||||
):
|
||||
raise FSE("Upload blocked by xbu server config")
|
||||
if xbu:
|
||||
hr = runhook(
|
||||
None,
|
||||
None,
|
||||
self.hub.up2k,
|
||||
"xbu.ftpd",
|
||||
xbu,
|
||||
ap,
|
||||
vp,
|
||||
"",
|
||||
self.uname,
|
||||
self.hub.asrv.vfs.get_perms(vp, self.uname),
|
||||
0,
|
||||
0,
|
||||
self.cli_ip,
|
||||
time.time(),
|
||||
"",
|
||||
)
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "Upload blocked by xbu server config: %r" % (vp,)
|
||||
self.respond("550 %s" % (t,), logging.info)
|
||||
return
|
||||
|
||||
# print("ftp_STOR: {} {} => {}".format(vp, mode, ap))
|
||||
ret = FTPHandler.ftp_STOR(self, file, mode)
|
||||
|
|
|
@ -2268,8 +2268,10 @@ class HttpCli(object):
|
|||
at,
|
||||
"",
|
||||
)
|
||||
if not hr:
|
||||
t = "upload blocked by xbu server config"
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "upload blocked by xbu server config: %r" % (vp,)
|
||||
self.log(t, 1)
|
||||
raise Pebkac(403, t)
|
||||
if hr.get("reloc"):
|
||||
|
@ -2301,7 +2303,11 @@ class HttpCli(object):
|
|||
|
||||
if (
|
||||
self.can_delete
|
||||
and (vfs.flags.get("daw") or "x-oc-mtime" in self.headers)
|
||||
and (
|
||||
vfs.flags.get("daw")
|
||||
or "replace" in self.headers
|
||||
or "x-oc-mtime" in self.headers
|
||||
)
|
||||
) or (
|
||||
not bos.path.exists(os.path.join(fdir, tnam))
|
||||
and not bos.path.getsize(path)
|
||||
|
@ -2397,8 +2403,10 @@ class HttpCli(object):
|
|||
at,
|
||||
"",
|
||||
)
|
||||
if not hr:
|
||||
t = "upload blocked by xau server config"
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "upload blocked by xau server config: %r" % (vp,)
|
||||
self.log(t, 1)
|
||||
wunlink(self.log, path, vfs.flags)
|
||||
raise Pebkac(403, t)
|
||||
|
@ -3210,11 +3218,38 @@ class HttpCli(object):
|
|||
new_file += ".md"
|
||||
|
||||
sanitized = sanitize_fn(new_file, "")
|
||||
fdir = vfs.canonical(rem)
|
||||
fn = os.path.join(fdir, sanitized)
|
||||
|
||||
for hn in ("xbu", "xau"):
|
||||
xxu = vfs.flags.get(hn)
|
||||
if xxu:
|
||||
hr = runhook(
|
||||
self.log,
|
||||
self.conn.hsrv.broker,
|
||||
None,
|
||||
"%s.http.new-md" % (hn,),
|
||||
xxu,
|
||||
fn,
|
||||
vjoin(self.vpath, sanitized),
|
||||
self.host,
|
||||
self.uname,
|
||||
self.asrv.vfs.get_perms(self.vpath, self.uname),
|
||||
time.time(),
|
||||
0,
|
||||
self.ip,
|
||||
time.time(),
|
||||
"",
|
||||
)
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "new-md blocked by " + hn + " server config: %r"
|
||||
t = t % (vjoin(vfs.vpath, rem),)
|
||||
self.log(t, 1)
|
||||
raise Pebkac(403, t)
|
||||
|
||||
if not nullwrite:
|
||||
fdir = vfs.canonical(rem)
|
||||
fn = os.path.join(fdir, sanitized)
|
||||
|
||||
if bos.path.exists(fn):
|
||||
raise Pebkac(500, "that file exists already")
|
||||
|
||||
|
@ -3344,7 +3379,7 @@ class HttpCli(object):
|
|||
|
||||
open_args = {"fdir": fdir, "suffix": suffix, "vf": vfs.flags}
|
||||
|
||||
if "replace" in self.uparam:
|
||||
if "replace" in self.uparam or "replace" in self.headers:
|
||||
if not self.can_delete:
|
||||
self.log("user not allowed to overwrite with ?replace")
|
||||
elif bos.path.exists(abspath):
|
||||
|
@ -3378,8 +3413,11 @@ class HttpCli(object):
|
|||
at,
|
||||
"",
|
||||
)
|
||||
if not hr:
|
||||
t = "upload blocked by xbu server config"
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "upload blocked by xbu server config: %r"
|
||||
t = t % (vjoin(upload_vpath, fname),)
|
||||
self.log(t, 1)
|
||||
raise Pebkac(403, t)
|
||||
if hr.get("reloc"):
|
||||
|
@ -3482,8 +3520,11 @@ class HttpCli(object):
|
|||
at,
|
||||
"",
|
||||
)
|
||||
if not hr:
|
||||
t = "upload blocked by xau server config"
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "upload blocked by xau server config: %r"
|
||||
t = t % (vjoin(upload_vpath, fname),)
|
||||
self.log(t, 1)
|
||||
wunlink(self.log, abspath, vfs.flags)
|
||||
raise Pebkac(403, t)
|
||||
|
@ -3775,7 +3816,7 @@ class HttpCli(object):
|
|||
|
||||
xbu = vfs.flags.get("xbu")
|
||||
if xbu:
|
||||
if not runhook(
|
||||
hr = runhook(
|
||||
self.log,
|
||||
self.conn.hsrv.broker,
|
||||
None,
|
||||
|
@ -3791,8 +3832,11 @@ class HttpCli(object):
|
|||
self.ip,
|
||||
time.time(),
|
||||
"",
|
||||
):
|
||||
t = "save blocked by xbu server config"
|
||||
)
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "save blocked by xbu server config"
|
||||
self.log(t, 1)
|
||||
raise Pebkac(403, t)
|
||||
|
||||
|
@ -3819,27 +3863,31 @@ class HttpCli(object):
|
|||
sha512 = sha512[:56]
|
||||
|
||||
xau = vfs.flags.get("xau")
|
||||
if xau and not runhook(
|
||||
self.log,
|
||||
self.conn.hsrv.broker,
|
||||
None,
|
||||
"xau.http.txt",
|
||||
xau,
|
||||
fp,
|
||||
self.vpath,
|
||||
self.host,
|
||||
self.uname,
|
||||
self.asrv.vfs.get_perms(self.vpath, self.uname),
|
||||
new_lastmod,
|
||||
sz,
|
||||
self.ip,
|
||||
new_lastmod,
|
||||
"",
|
||||
):
|
||||
t = "save blocked by xau server config"
|
||||
self.log(t, 1)
|
||||
wunlink(self.log, fp, vfs.flags)
|
||||
raise Pebkac(403, t)
|
||||
if xau:
|
||||
hr = runhook(
|
||||
self.log,
|
||||
self.conn.hsrv.broker,
|
||||
None,
|
||||
"xau.http.txt",
|
||||
xau,
|
||||
fp,
|
||||
self.vpath,
|
||||
self.host,
|
||||
self.uname,
|
||||
self.asrv.vfs.get_perms(self.vpath, self.uname),
|
||||
new_lastmod,
|
||||
sz,
|
||||
self.ip,
|
||||
new_lastmod,
|
||||
"",
|
||||
)
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "save blocked by xau server config"
|
||||
self.log(t, 1)
|
||||
wunlink(self.log, fp, vfs.flags)
|
||||
raise Pebkac(403, t)
|
||||
|
||||
self.conn.hsrv.broker.say(
|
||||
"up2k.hash_file",
|
||||
|
|
|
@ -246,24 +246,29 @@ class SMB(object):
|
|||
|
||||
ap = absreal(ap)
|
||||
xbu = vfs.flags.get("xbu")
|
||||
if xbu and not runhook(
|
||||
self.nlog,
|
||||
None,
|
||||
self.hub.up2k,
|
||||
"xbu.smb",
|
||||
xbu,
|
||||
ap,
|
||||
vpath,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
"1.7.6.2",
|
||||
time.time(),
|
||||
"",
|
||||
):
|
||||
yeet("blocked by xbu server config: %r" % (vpath,))
|
||||
if xbu:
|
||||
hr = runhook(
|
||||
self.nlog,
|
||||
None,
|
||||
self.hub.up2k,
|
||||
"xbu.smb",
|
||||
xbu,
|
||||
ap,
|
||||
vpath,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
"1.7.6.2",
|
||||
time.time(),
|
||||
"",
|
||||
)
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "blocked by xbu server config: %r" % (vpath,)
|
||||
yeet(t)
|
||||
|
||||
ret = bos.open(ap, flags, *a, mode=chmod, **ka)
|
||||
if wr:
|
||||
|
|
|
@ -363,24 +363,29 @@ class Tftpd(object):
|
|||
yeet("blocked write; folder not world-deletable: /%s" % (vpath,))
|
||||
|
||||
xbu = vfs.flags.get("xbu")
|
||||
if xbu and not runhook(
|
||||
self.nlog,
|
||||
None,
|
||||
self.hub.up2k,
|
||||
"xbu.tftpd",
|
||||
xbu,
|
||||
ap,
|
||||
vpath,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
"8.3.8.7",
|
||||
time.time(),
|
||||
"",
|
||||
):
|
||||
yeet("blocked by xbu server config: %r" % (vpath,))
|
||||
if xbu:
|
||||
hr = runhook(
|
||||
self.nlog,
|
||||
None,
|
||||
self.hub.up2k,
|
||||
"xbu.tftpd",
|
||||
xbu,
|
||||
ap,
|
||||
vpath,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
"8.3.8.7",
|
||||
time.time(),
|
||||
"",
|
||||
)
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "upload blocked by xbu server config: %r" % (vpath,)
|
||||
yeet(t)
|
||||
|
||||
if not self.args.tftp_nols and bos.path.isdir(ap):
|
||||
return self._ls(vpath, "", 0, True)
|
||||
|
|
|
@ -3300,8 +3300,11 @@ class Up2k(object):
|
|||
job["at"],
|
||||
"",
|
||||
)
|
||||
if not hr:
|
||||
t = "upload blocked by xbu server config: %r" % (dst,)
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "upload blocked by xbu server config: %r"
|
||||
t = t % (vp,)
|
||||
self.log(t, 1)
|
||||
raise Pebkac(403, t)
|
||||
if hr.get("reloc"):
|
||||
|
@ -3981,8 +3984,11 @@ class Up2k(object):
|
|||
at or time.time(),
|
||||
"",
|
||||
)
|
||||
if not hr:
|
||||
t = "upload blocked by xau server config"
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "upload blocked by xau server config: %r"
|
||||
t = t % (djoin(vtop, rd, fn),)
|
||||
self.log(t, 1)
|
||||
wunlink(self.log, dst, vflags)
|
||||
self.registry[ptop].pop(wark, None)
|
||||
|
@ -5132,8 +5138,10 @@ class Up2k(object):
|
|||
job["t0"],
|
||||
"",
|
||||
)
|
||||
if not hr:
|
||||
t = "upload blocked by xbu server config: %r" % (vp_chk,)
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or not hr:
|
||||
if not t:
|
||||
t = "upload blocked by xbu server config: %r" % (vp_chk,)
|
||||
self.log(t, 1)
|
||||
raise Pebkac(403, t)
|
||||
if hr.get("reloc"):
|
||||
|
|
|
@ -432,7 +432,7 @@ EXTS["vnd.mozilla.apng"] = "png"
|
|||
MAGIC_MAP = {"jpeg": "jpg"}
|
||||
|
||||
|
||||
DEF_EXP = "self.ip self.ua self.uname self.host cfg.name cfg.logout vf.scan vf.thsize hdr.cf_ipcountry srv.itime srv.htime"
|
||||
DEF_EXP = "self.ip self.ua self.uname self.host cfg.name cfg.logout vf.scan vf.thsize hdr.cf-ipcountry srv.itime srv.htime"
|
||||
|
||||
DEF_MTE = ".files,circle,album,.tn,artist,title,.bpm,key,.dur,.q,.vq,.aq,vc,ac,fmt,res,.fps,ahash,vhash"
|
||||
|
||||
|
|
|
@ -118,6 +118,26 @@ table {
|
|||
.btns>a:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
.agr br {
|
||||
display: none;
|
||||
}
|
||||
#lo,
|
||||
.agr a,
|
||||
.agr form {
|
||||
margin: 0 .5em 0 0;
|
||||
line-height: 4em;
|
||||
}
|
||||
.agr form,
|
||||
.agr input {
|
||||
display: inline;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#lo,
|
||||
.agr input {
|
||||
line-height: 1em;
|
||||
font-weight: normal;
|
||||
}
|
||||
#msg {
|
||||
margin: 3em 0;
|
||||
}
|
||||
|
|
|
@ -151,19 +151,31 @@
|
|||
{%- endif %}
|
||||
|
||||
<h1 id="cc">other stuff:</h1>
|
||||
<ul>
|
||||
<div class="agr">
|
||||
{%- if ahttps %}
|
||||
<li><a id="wb" href="{{ ahttps }}">switch to https</a></li>
|
||||
<a id="wb" href="{{ ahttps }}">switch to https</a><br />
|
||||
{%- endif %}
|
||||
|
||||
{%- if this.uname in this.args.idp_adm_set %}
|
||||
<li><a id="ag" href="{{ r }}/?idp">view idp cache</a></li>
|
||||
{%- endif %}
|
||||
<a id="af" href="{{ r }}/?ru">show recent uploads</a><br />
|
||||
|
||||
{%- if this.uname != '*' and this.args.shr %}
|
||||
<li><a id="y" href="{{ r }}/?shares">edit shares</a></li>
|
||||
<a id="y" href="{{ r }}/?shares">edit shares</a><br />
|
||||
{%- endif %}
|
||||
|
||||
{%- if this.uname in this.args.idp_adm_set %}
|
||||
<a id="ag" href="{{ r }}/?idp">view idp cache</a><br />
|
||||
{%- endif %}
|
||||
|
||||
<a id="k" href="{{ r }}/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a><br />
|
||||
|
||||
{%- if this.uname != '*' and not in_shr %}
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="act" value="logout" />
|
||||
<input type="submit" id="lo" value="logout “{{ this.uname|e }}” everywhere" />
|
||||
</form>
|
||||
{%- endif %}
|
||||
</div>
|
||||
<ul>
|
||||
{%- if k304 or k304vis %}
|
||||
{%- if k304 %}
|
||||
<li><a id="h" href="{{ r }}/?cc&setck=k304=n">disable k304</a> (currently enabled)
|
||||
|
@ -181,16 +193,6 @@
|
|||
{%- endif %}
|
||||
<blockquote id="ad">enabling no304 will disable all caching; try this if k304 wasn't enough. This will waste a huge amount of network traffic!</blockquote></li>
|
||||
{%- endif %}
|
||||
|
||||
<li><a id="af" href="{{ r }}/?ru">show recent uploads</a></li>
|
||||
<li><a id="k" href="{{ r }}/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li>
|
||||
|
||||
{%- if this.uname != '*' and not in_shr %}
|
||||
<li><form method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" name="act" value="logout" />
|
||||
<input type="submit" id="lo" value="logout “{{ this.uname|e }}” everywhere" />
|
||||
</form></li>
|
||||
{%- endif %}
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -246,6 +246,7 @@ upload modifiers:
|
|||
| `Accept: json` | `want=json` | return upload info as json; same as `?j` |
|
||||
| `Rand: 4` | `rand=4` | generate random filename with 4 characters |
|
||||
| `Life: 30` | `life=30` | delete file after 30 seconds |
|
||||
| `Replace: 1` | `replace` | overwrite file if exists |
|
||||
| `CK: no` | `ck` | disable serverside checksum (maybe faster) |
|
||||
| `CK: md5` | `ck=md5` | return md5 checksum instead of sha512 |
|
||||
| `CK: sha1` | `ck=sha1` | return sha1 checksum |
|
||||
|
@ -254,7 +255,9 @@ upload modifiers:
|
|||
| `CK: b2s` | `ck=b2s` | return blake2s checksum |
|
||||
|
||||
* `life` only has an effect if the volume has a lifetime, and the volume lifetime must be greater than the file's
|
||||
|
||||
* `replace` upload-modifier:
|
||||
* the header `replace: 1` works for both PUT and multipart-post
|
||||
* the url-param `replace` only works for multipart-post
|
||||
* server behavior of `msg` can be reconfigured with `--urlform`
|
||||
|
||||
## admin
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue