mirror of
				https://github.com/9001/copyparty.git
				synced 2025-10-31 04:32:20 -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