From 6dcb1efb7c5efc804e5039cd78a7d3377fc2bbc3 Mon Sep 17 00:00:00 2001 From: ed Date: Wed, 21 Jan 2026 04:55:50 +0000 Subject: [PATCH] add ?smsg --- copyparty/__main__.py | 1 + copyparty/httpcli.py | 37 +++++++++++++++++++++++++++++++++++++ copyparty/svchub.py | 6 ++++++ docs/devnotes.md | 1 + tests/util.py | 2 +- 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index f1635289..1ae4eec8 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1562,6 +1562,7 @@ def add_optouts(ap): ap2.add_argument("-nih", action="store_true", help="no info hostname -- don't show in UI") ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI. This is the same as --du-who no") ap2.add_argument("-nb", action="store_true", help="no powered-by-copyparty branding in UI") + ap2.add_argument("--smsg", metavar="T,T", type=u, default="POST", help="HTTP-methods to allow ?smsg for; will execute xm hooks like urlform / message-to-serverlog; dangerous example: [\033[32mGET,POST\033[0m]. \033[1;31mWARNING:\033[0m The default (POST) is safe, but GET is dangerous; security/CSRF hazard") ap2.add_argument("--zipmaxn", metavar="N", type=u, default="0", help="reject download-as-zip if more than \033[33mN\033[0m files in total; optionally takes a unit suffix: [\033[32m256\033[0m], [\033[32m9K\033[0m], [\033[32m4G\033[0m] (volflag=zipmaxn)") ap2.add_argument("--zipmaxs", metavar="SZ", type=u, default="0", help="reject download-as-zip if total download size exceeds \033[33mSZ\033[0m bytes; optionally takes a unit suffix: [\033[32m256M\033[0m], [\033[32m4G\033[0m], [\033[32m2T\033[0m] (volflag=zipmaxs)") ap2.add_argument("--zipmaxt", metavar="TXT", type=u, default="", help="custom errormessage when download size exceeds max (volflag=zipmaxt)") diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index b63abace..b861aae9 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -1434,6 +1434,9 @@ class HttpCli(object): self.uparam["h"] = "" + if "smsg" in self.uparam: + return self.handle_smsg() + if "tree" in self.uparam: return self.tx_tree() @@ -2246,6 +2249,9 @@ class HttpCli(object): ): return self.handle_post_json() + if "smsg" in self.uparam: + return self.handle_smsg() + if "move" in self.uparam: return self.handle_mv() @@ -2332,6 +2338,37 @@ class HttpCli(object): raise Pebkac(405, "don't know how to handle POST(%r)" % (ctype,)) + def handle_smsg(self) -> bool: + if self.mode not in self.args.smsg_set: + raise Pebkac(403, "smsg is disabled for this http-method in server config") + + msg = self.uparam["smsg"] + self.log("smsg %d @ %r\n %r\n" % (len(msg), "/" + self.vpath, msg)) + + xm = self.vn.flags.get("xm") + if xm: + xm_rsp = runhook( + self.log, + self.conn.hsrv.broker, + None, + "xm", + xm, + self.vn.canonical(self.rem), + self.vpath, + self.host, + self.uname, + self.asrv.vfs.get_perms(self.vpath, self.uname), + time.time(), + len(msg), + self.ip, + time.time(), + [msg, msg], + ) + self.loud_reply(xm_rsp.get("stdout") or "", status=202) + else: + self.loud_reply("k", status=202) + return True + def get_xml_enc(self, txt: str) -> str: ofs = txt[:512].find(' encoding="') enc = "" diff --git a/copyparty/svchub.py b/copyparty/svchub.py index 3fd30790..39c0491a 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -1118,6 +1118,12 @@ class SvcHub(object): vsa = [x.lower() for x in vsa if x] setattr(al, k + "_set", set(vsa)) + for k in "smsg".split(" "): + vs = getattr(al, k) + vsa = [x.strip() for x in vs.split(",")] + vsa = [x.upper() for x in vsa if x] + setattr(al, k + "_set", set(vsa)) + zs = "dav_ua1 sus_urls nonsus_urls ua_nodav ua_nodoc ua_nozip" for k in zs.split(" "): vs = getattr(al, k) diff --git a/docs/devnotes.md b/docs/devnotes.md index 652b0d33..89dc8fb3 100644 --- a/docs/devnotes.md +++ b/docs/devnotes.md @@ -289,6 +289,7 @@ upload modifiers: | GET | `?imgs=0` | ui: show list-view | | GET | `?thumb` | ui, grid-mode: show thumbnails | | GET | `?thumb=0` | ui, grid-mode: show icons | +| POST | `?smsg=foo` | send-msg-to-serverlog / run xm hook | # event hooks diff --git a/tests/util.py b/tests/util.py index 245c6916..a665c090 100644 --- a/tests/util.py +++ b/tests/util.py @@ -164,7 +164,7 @@ class Cfg(Namespace): ex = "ctl_re db_act forget_ip idp_cookie idp_store k304 loris no304 nosubtle qr_pin qr_wait re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo u2ow zipmaxn zipmaxs" ka.update(**{k: 0 for k in ex.split()}) - ex = "ah_alg bname chdir chmod_f chpw_db db_xattr doctitle df epilogues exit favico ipa ipar html_head html_head_d html_head_s idp_login idp_logout lg_sba lg_sbf log_date log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i opds_exts preadmes prologues readmes shr shr_site site tcolor textfiles txt_eol ufavico ufavico_h unlist up_site vname xff_src zipmaxt R RS SR" + ex = "ah_alg bname chdir chmod_f chpw_db db_xattr doctitle df epilogues exit favico ipa ipar html_head html_head_d html_head_s idp_login idp_logout lg_sba lg_sbf log_date log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i opds_exts preadmes prologues readmes shr shr_site site smsg tcolor textfiles txt_eol ufavico ufavico_h unlist up_site vname xff_src zipmaxt R RS SR" ka.update(**{k: "" for k in ex.split()}) ex = "apnd_who ban_403 ban_404 ban_422 ban_pw ban_pwc ban_url dont_ban cachectl http_vary rcm rss_fmt_d rss_fmt_t spinner"