mirror of
https://github.com/9001/copyparty.git
synced 2025-10-10 10:32:19 -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[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[36mI\033[35m import and run as module, not as subprocess
|
||||
|
||||
\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
|
||||
|
@ -865,6 +866,12 @@ def get_sects():
|
|||
on new uploads, but with certain limitations. See
|
||||
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,
|
||||
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
|
||||
|
|
|
@ -3611,6 +3611,7 @@ def _parsehook(
|
|||
chk = False
|
||||
fork = False
|
||||
jtxt = False
|
||||
imp = False
|
||||
wait = 0.0
|
||||
tout = 0.0
|
||||
kill = "t"
|
||||
|
@ -3624,6 +3625,8 @@ def _parsehook(
|
|||
fork = True
|
||||
elif arg == "j":
|
||||
jtxt = True
|
||||
elif arg == "I":
|
||||
imp = True
|
||||
elif arg.startswith("w"):
|
||||
wait = float(arg[1:])
|
||||
elif arg.startswith("t"):
|
||||
|
@ -3668,7 +3671,7 @@ def _parsehook(
|
|||
|
||||
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(
|
||||
|
@ -3678,7 +3681,7 @@ def runihook(
|
|||
vol: "VFS",
|
||||
ups: list[tuple[str, int, int, str, str, str, int, str]],
|
||||
) -> 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]
|
||||
if acmd[0].endswith(".py"):
|
||||
bcmd = [sfsenc(pybin)] + bcmd
|
||||
|
@ -3857,7 +3860,7 @@ def _runhook(
|
|||
txt: str,
|
||||
) -> dict[str, Any]:
|
||||
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:
|
||||
for ch in areq:
|
||||
if ch not in perms:
|
||||
|
@ -3865,7 +3868,7 @@ def _runhook(
|
|||
if log:
|
||||
log(t % (uname, cmd, areq, perms))
|
||||
return ret # fallthrough to next hook
|
||||
if jtxt:
|
||||
if imp or jtxt:
|
||||
ja = {
|
||||
"ap": ap,
|
||||
"vp": vp,
|
||||
|
@ -3879,6 +3882,9 @@ def _runhook(
|
|||
"src": src,
|
||||
"txt": txt,
|
||||
}
|
||||
if imp:
|
||||
mod = loadpy(acmd[0], False)
|
||||
return mod.main(ja)
|
||||
arg = json.dumps(ja)
|
||||
else:
|
||||
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)
|
||||
* 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
|
||||
|
||||
|
|
Loading…
Reference in a new issue