mirror of
https://github.com/9001/copyparty.git
synced 2025-10-10 02:22:21 -06:00
hooks: import-flag
This commit is contained in:
parent
a0f8f794a8
commit
41ed559faa
54
bin/hooks/import-me.py
Normal file
54
bin/hooks/import-me.py
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
_ = r"""
|
||||||
|
the fastest hook in the west
|
||||||
|
(runs directly inside copyparty, not as a subprocess)
|
||||||
|
|
||||||
|
example usage as global config:
|
||||||
|
--xbu I,bin/hooks/import-me.py
|
||||||
|
|
||||||
|
example usage as a volflag (per-volume config):
|
||||||
|
-v srv/inc:inc:r:rw,ed:c,xbu=I,bin/hooks/import-me.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: I,bin/hooks/import-me.py
|
||||||
|
|
||||||
|
parameters explained,
|
||||||
|
I = import; do not fork / subprocess
|
||||||
|
|
||||||
|
IMPORTANT NOTE:
|
||||||
|
because this hook is running inside copyparty, you need to
|
||||||
|
be EXCEPTIONALLY CAREFUL to avoid side-effects, for example
|
||||||
|
DO NOT os.chdir() or anything like that, and also make sure
|
||||||
|
that the name of this file is unique (cannot be the same as
|
||||||
|
an existing python module/library)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def main(ka: dict[str, Any]) -> dict[str, str]:
|
||||||
|
# "ka" is a dictionary with info from copyparty...
|
||||||
|
|
||||||
|
# but because we are running inside copyparty, we don't need such courtesies;
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
cf = inspect.currentframe().f_back.f_back.f_back
|
||||||
|
t = "hello from hook; I am able to peek into copyparty's memory like so:\n function name: %s\n variables:\n %s\n"
|
||||||
|
t2 = "\n ".join([("%r: %r" % (k, v))[:99] for k, v in cf.f_locals.items()][:9])
|
||||||
|
print(t % (cf.f_code, t2))
|
||||||
|
|
||||||
|
# must return a dictionary with:
|
||||||
|
# "rc": the retcode; 0 is ok
|
||||||
|
return {"rc": 0}
|
|
@ -816,6 +816,7 @@ def get_sects():
|
||||||
\033[36mwN\033[35m waits N sec after command has been started before continuing
|
\033[36mwN\033[35m waits N sec after command has been started before continuing
|
||||||
\033[36mtN\033[35m sets an N sec timeout before the command is abandoned
|
\033[36mtN\033[35m sets an N sec timeout before the command is abandoned
|
||||||
\033[36miN\033[35m xiu only: volume must be idle for N sec (default = 5)
|
\033[36miN\033[35m xiu only: volume must be idle for N sec (default = 5)
|
||||||
|
\033[36mI\033[35m import and run as module, not as subprocess
|
||||||
|
|
||||||
\033[36mar\033[35m only run hook if user has read-access
|
\033[36mar\033[35m only run hook if user has read-access
|
||||||
\033[36marw\033[35m only run hook if user has read-write-access
|
\033[36marw\033[35m only run hook if user has read-write-access
|
||||||
|
@ -865,6 +866,12 @@ def get_sects():
|
||||||
on new uploads, but with certain limitations. See
|
on new uploads, but with certain limitations. See
|
||||||
bin/hooks/reloc* and docs/devnotes.md#hook-effects
|
bin/hooks/reloc* and docs/devnotes.md#hook-effects
|
||||||
|
|
||||||
|
the \033[36mI\033[0m option will override most other options, because
|
||||||
|
it entirely hands over control to the hook, which is
|
||||||
|
then able to tamper with copyparty's internal memory
|
||||||
|
and wreck havoc if it wants to -- but this is worh it
|
||||||
|
because it makes the hook 140x faster
|
||||||
|
|
||||||
except for \033[36mxm\033[0m, only one hook / one action can run at a time,
|
except for \033[36mxm\033[0m, only one hook / one action can run at a time,
|
||||||
so it's recommended to use the \033[36mf\033[0m flag unless you really need
|
so it's recommended to use the \033[36mf\033[0m flag unless you really need
|
||||||
to wait for the hook to finish before continuing (without \033[36mf\033[0m
|
to wait for the hook to finish before continuing (without \033[36mf\033[0m
|
||||||
|
|
|
@ -3611,6 +3611,7 @@ def _parsehook(
|
||||||
chk = False
|
chk = False
|
||||||
fork = False
|
fork = False
|
||||||
jtxt = False
|
jtxt = False
|
||||||
|
imp = False
|
||||||
wait = 0.0
|
wait = 0.0
|
||||||
tout = 0.0
|
tout = 0.0
|
||||||
kill = "t"
|
kill = "t"
|
||||||
|
@ -3624,6 +3625,8 @@ def _parsehook(
|
||||||
fork = True
|
fork = True
|
||||||
elif arg == "j":
|
elif arg == "j":
|
||||||
jtxt = True
|
jtxt = True
|
||||||
|
elif arg == "I":
|
||||||
|
imp = True
|
||||||
elif arg.startswith("w"):
|
elif arg.startswith("w"):
|
||||||
wait = float(arg[1:])
|
wait = float(arg[1:])
|
||||||
elif arg.startswith("t"):
|
elif arg.startswith("t"):
|
||||||
|
@ -3668,7 +3671,7 @@ def _parsehook(
|
||||||
|
|
||||||
argv[0] = os.path.expandvars(os.path.expanduser(argv[0]))
|
argv[0] = os.path.expandvars(os.path.expanduser(argv[0]))
|
||||||
|
|
||||||
return areq, chk, fork, jtxt, wait, sp_ka, argv
|
return areq, chk, imp, fork, jtxt, wait, sp_ka, argv
|
||||||
|
|
||||||
|
|
||||||
def runihook(
|
def runihook(
|
||||||
|
@ -3678,7 +3681,7 @@ def runihook(
|
||||||
vol: "VFS",
|
vol: "VFS",
|
||||||
ups: list[tuple[str, int, int, str, str, str, int, str]],
|
ups: list[tuple[str, int, int, str, str, str, int, str]],
|
||||||
) -> bool:
|
) -> bool:
|
||||||
_, chk, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
|
_, chk, imp, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
|
||||||
bcmd = [sfsenc(x) for x in acmd]
|
bcmd = [sfsenc(x) for x in acmd]
|
||||||
if acmd[0].endswith(".py"):
|
if acmd[0].endswith(".py"):
|
||||||
bcmd = [sfsenc(pybin)] + bcmd
|
bcmd = [sfsenc(pybin)] + bcmd
|
||||||
|
@ -3857,7 +3860,7 @@ def _runhook(
|
||||||
txt: str,
|
txt: str,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
ret = {"rc": 0}
|
ret = {"rc": 0}
|
||||||
areq, chk, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
|
areq, chk, imp, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd)
|
||||||
if areq:
|
if areq:
|
||||||
for ch in areq:
|
for ch in areq:
|
||||||
if ch not in perms:
|
if ch not in perms:
|
||||||
|
@ -3865,7 +3868,7 @@ def _runhook(
|
||||||
if log:
|
if log:
|
||||||
log(t % (uname, cmd, areq, perms))
|
log(t % (uname, cmd, areq, perms))
|
||||||
return ret # fallthrough to next hook
|
return ret # fallthrough to next hook
|
||||||
if jtxt:
|
if imp or jtxt:
|
||||||
ja = {
|
ja = {
|
||||||
"ap": ap,
|
"ap": ap,
|
||||||
"vp": vp,
|
"vp": vp,
|
||||||
|
@ -3879,6 +3882,9 @@ def _runhook(
|
||||||
"src": src,
|
"src": src,
|
||||||
"txt": txt,
|
"txt": txt,
|
||||||
}
|
}
|
||||||
|
if imp:
|
||||||
|
mod = loadpy(acmd[0], False)
|
||||||
|
return mod.main(ja)
|
||||||
arg = json.dumps(ja)
|
arg = json.dumps(ja)
|
||||||
else:
|
else:
|
||||||
arg = txt or ap
|
arg = txt or ap
|
||||||
|
|
|
@ -310,6 +310,14 @@ a subset of effect types are available for a subset of hook types,
|
||||||
to trigger indexing of files `/foo/1.txt` and `/foo/bar/2.txt`, a hook can `print(json.dumps({"idx":{"vp":["/foo/1.txt","/foo/bar/2.txt"]}}))` (and replace "idx" with "del" to delete instead)
|
to trigger indexing of files `/foo/1.txt` and `/foo/bar/2.txt`, a hook can `print(json.dumps({"idx":{"vp":["/foo/1.txt","/foo/bar/2.txt"]}}))` (and replace "idx" with "del" to delete instead)
|
||||||
* note: paths starting with `/` are absolute URLs, but you can also do `../3.txt` relative to the destination folder of each uploaded file
|
* note: paths starting with `/` are absolute URLs, but you can also do `../3.txt` relative to the destination folder of each uploaded file
|
||||||
|
|
||||||
|
### hook import
|
||||||
|
|
||||||
|
the `I` flag runs the hook inside copyparty, which can be very useful and dangerous:
|
||||||
|
|
||||||
|
* around 140x faster because it doesn't need to launch a new subprocess
|
||||||
|
* the hook can intentionally (or accidentally) mess with copyparty's internals
|
||||||
|
* very easy to crash things if not careful
|
||||||
|
|
||||||
|
|
||||||
# assumptions
|
# assumptions
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue