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[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