Skip to content

Commit 6c94a63

Browse files
committed
add hook side-effects; closes #86
hooks can now interrupt or redirect actions, and initiate related actions, by printing json on stdout with commands mainly to mitigate limitations such as ShareX/ShareX#3992 xbr/xau can redirect uploads to other destinations with `reloc` and most hooks can initiate indexing or deletion of additional files by giving a list of vpaths in json-keys `idx` or `del` there are limitations; * xbu/xau effects don't apply to ftp, tftp, smb * xau will intentionally fail if a reloc destination exists * xau effects do not apply to up2k also provides more details for hooks: * xbu/xau: basic-uploader vpath with filename * xbr/xar: add client ip
1 parent 20669c7 commit 6c94a63

File tree

15 files changed

+793
-175
lines changed

15 files changed

+793
-175
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,8 @@ you can set hooks before and/or after an event happens, and currently you can ho
13131313
13141314
there's a bunch of flags and stuff, see `--help-hooks`
13151315
1316+
if you want to write your own hooks, see [devnotes](./docs/devnotes.md#event-hooks)
1317+
13161318
13171319
### upload events
13181320

bin/hooks/into-the-cache-it-goes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@
4141
t10 = abort download and continue if it takes longer than 10sec
4242
4343
example usage as a volflag (per-volume config):
44-
-v srv/inc:inc:r:rw,ed:xau=j,t10,bin/hooks/into-the-cache-it-goes.py
45-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44+
-v srv/inc:inc:r:rw,ed:c,xau=j,t10,bin/hooks/into-the-cache-it-goes.py
45+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4646
4747
(share filesystem-path srv/inc as volume /inc,
4848
readable by everyone, read-write for user 'ed',

bin/hooks/reloc-by-ext.py

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#!/usr/bin/env python3
2+
3+
import json
4+
import os
5+
import sys
6+
7+
8+
_ = r"""
9+
relocate/redirect incoming uploads according to file extension
10+
11+
example usage as global config:
12+
--xbu j,c1,bin/hooks/reloc-by-ext.py
13+
14+
parameters explained,
15+
xbu = execute before upload
16+
j = this hook needs upload information as json (not just the filename)
17+
c1 = this hook returns json on stdout, so tell copyparty to read that
18+
19+
example usage as a volflag (per-volume config):
20+
-v srv/inc:inc:r:rw,ed:c,xbu=j,c1,bin/hooks/reloc-by-ext.py
21+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
22+
23+
(share filesystem-path srv/inc as volume /inc,
24+
readable by everyone, read-write for user 'ed',
25+
running this plugin on all uploads with the params explained above)
26+
27+
example usage as a volflag in a copyparty config file:
28+
[/inc]
29+
srv/inc
30+
accs:
31+
r: *
32+
rw: ed
33+
flags:
34+
xbu: j,c1,bin/hooks/reloc-by-ext.py
35+
36+
note: this only works with the basic uploader (sharex and such),
37+
does not work with up2k / dragdrop into browser
38+
39+
note: this could also work as an xau hook (after-upload), but
40+
because it doesn't need to read the file contents its better
41+
as xbu (before-upload) since that's safer / less buggy
42+
"""
43+
44+
45+
PICS = "avif bmp gif heic heif jpeg jpg jxl png psd qoi tga tif tiff webp"
46+
VIDS = "3gp asf avi flv mkv mov mp4 mpeg mpeg2 mpegts mpg mpg2 nut ogm ogv rm ts vob webm wmv"
47+
MUSIC = "aac aif aiff alac amr ape dfpwm flac m4a mp3 ogg opus ra tak tta wav wma wv"
48+
49+
50+
def main():
51+
inf = json.loads(sys.argv[1])
52+
vdir, fn = os.path.split(inf["vp"])
53+
54+
try:
55+
fn, ext = fn.rsplit(".", 1)
56+
except:
57+
# no file extension; abort
58+
return
59+
60+
ext = ext.lower()
61+
62+
##
63+
## some example actions to take; pick one by
64+
## selecting it inside the print at the end:
65+
##
66+
67+
# create a subfolder named after the filetype and move it into there
68+
into_subfolder = {"vp": ext}
69+
70+
# move it into a toplevel folder named after the filetype
71+
into_toplevel = {"vp": "/" + ext}
72+
73+
# move it into a filetype-named folder next to the target folder
74+
into_sibling = {"vp": "../" + ext}
75+
76+
# move images into "/just/pics", vids into "/just/vids",
77+
# music into "/just/tunes", and anything else as-is
78+
if ext in PICS.split():
79+
by_category = {"vp": "/just/pics"}
80+
elif ext in VIDS.split():
81+
by_category = {"vp": "/just/vids"}
82+
elif ext in MUSIC.split():
83+
by_category = {"vp": "/just/tunes"}
84+
else:
85+
by_category = {}
86+
87+
# now choose the effect to apply; can be any of these:
88+
# into_subfolder into_toplevel into_sibling by_category
89+
effect = into_subfolder
90+
print(json.dumps({"reloc": effect}))
91+
92+
93+
if __name__ == "__main__":
94+
main()

copyparty/__main__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,11 @@ def get_sects():
704704
\033[36mxban\033[0m can be used to overrule / cancel a user ban event;
705705
if the program returns 0 (true/OK) then the ban will NOT happen
706706
707+
effects can be used to redirect uploads into other
708+
locations, and to delete or index other files based
709+
on new uploads, but with certain limitations. See
710+
bin/hooks/reloc* and docs/devnotes.md#hook-effects
711+
707712
except for \033[36mxm\033[0m, only one hook / one action can run at a time,
708713
so it's recommended to use the \033[36mf\033[0m flag unless you really need
709714
to wait for the hook to finish before continuing (without \033[36mf\033[0m
@@ -1132,6 +1137,7 @@ def add_hooks(ap):
11321137
ap2.add_argument("--xad", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m after a file delete")
11331138
ap2.add_argument("--xm", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m on message")
11341139
ap2.add_argument("--xban", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m if someone gets banned (pw/404/403/url)")
1140+
ap2.add_argument("--hook-v", action="store_true", help="verbose hooks")
11351141

11361142

11371143
def add_stats(ap):

copyparty/authsrv.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,8 +521,8 @@ def get(
521521
t = "{} has no {} in [{}] => [{}] => [{}]"
522522
self.log("vfs", t.format(uname, msg, vpath, cvpath, ap), 6)
523523

524-
t = 'you don\'t have %s-access in "/%s"'
525-
raise Pebkac(err, t % (msg, cvpath))
524+
t = 'you don\'t have %s-access in "/%s" or below "/%s"'
525+
raise Pebkac(err, t % (msg, cvpath, vn.vpath))
526526

527527
return vn, rem
528528

@@ -1898,7 +1898,7 @@ def _reload(self) -> None:
18981898
self.log(t.format(vol.vpath), 1)
18991899
del vol.flags["lifetime"]
19001900

1901-
needs_e2d = [x for x in hooks if x != "xm"]
1901+
needs_e2d = [x for x in hooks if x in ("xau", "xiu")]
19021902
drop = [x for x in needs_e2d if vol.flags.get(x)]
19031903
if drop:
19041904
t = 'removing [{}] from volume "/{}" because e2d is disabled'

copyparty/ftpd.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ def rename(self, src: str, dst: str) -> None:
353353
svp = join(self.cwd, src).lstrip("/")
354354
dvp = join(self.cwd, dst).lstrip("/")
355355
try:
356-
self.hub.up2k.handle_mv(self.uname, svp, dvp)
356+
self.hub.up2k.handle_mv(self.uname, self.h.cli_ip, svp, dvp)
357357
except Exception as ex:
358358
raise FSE(str(ex))
359359

@@ -471,6 +471,9 @@ def ftp_STOR(self, file: str, mode: str = "w") -> Any:
471471
xbu = vfs.flags.get("xbu")
472472
if xbu and not runhook(
473473
None,
474+
None,
475+
self.hub.up2k,
476+
"xbu.ftpd",
474477
xbu,
475478
ap,
476479
vp,
@@ -480,7 +483,7 @@ def ftp_STOR(self, file: str, mode: str = "w") -> Any:
480483
0,
481484
0,
482485
self.cli_ip,
483-
0,
486+
time.time(),
484487
"",
485488
):
486489
raise FSE("Upload blocked by xbu server config")

0 commit comments

Comments
 (0)