From 677fd8eef1df0ad36cf2731b2afffae460d69742 Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 14 Oct 2025 20:23:38 +0000 Subject: [PATCH] config for `?stack` verbosity / access --- copyparty/__main__.py | 4 +++- copyparty/httpcli.py | 17 ++++++++++++----- copyparty/svchub.py | 3 +++ copyparty/util.py | 10 +++++++--- 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 3f0f6624..11dbd498 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1611,12 +1611,14 @@ def add_admin(ap): ap2 = ap.add_argument_group("admin panel options") ap2.add_argument("--no-reload", action="store_true", help="disable ?reload=cfg (reload users/volumes/volflags from config file)") ap2.add_argument("--no-rescan", action="store_true", help="disable ?scan (volume reindexing)") - ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks)") + ap2.add_argument("--no-stack", action="store_true", help="disable ?stack (list all stacks); same as --stack-who=no") ap2.add_argument("--no-ups-page", action="store_true", help="disable ?ru (list of recent uploads)") ap2.add_argument("--no-up-list", action="store_true", help="don't show list of incoming files in controlpanel") ap2.add_argument("--dl-list", metavar="LVL", type=int, default=2, help="who can see active downloads in the controlpanel? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=everyone") ap2.add_argument("--ups-who", metavar="LVL", type=int, default=2, help="who can see recent uploads on the ?ru page? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=everyone (volflag=ups_who)") ap2.add_argument("--ups-when", action="store_true", help="let everyone see upload timestamps on the ?ru page, not just admins") + ap2.add_argument("--stack-who", metavar="LVL", type=u, default="a", help="who can see the ?stack page (list of threads)? [\033[32mno\033[0m]=nobody, [\033[32ma\033[0m]=admins, [\033[32mrw\033[0m]=read+write, [\033[32mall\033[0m]=everyone") + ap2.add_argument("--stack-v", action="store_true", help="verbose ?stack") def add_thumbnail(ap): diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 3f5b1c31..b0e14b76 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -5463,13 +5463,20 @@ class HttpCli(object): return self.redirect("", "?h", x.get(), "return to", False) def tx_stack(self) -> bool: - if not self.avol and not [x for x in self.wvol if x in self.rvol]: + zs = self.args.stack_who + if zs == "all" or ( + (zs == "a" and self.avol) + or (zs == "rw" and [x for x in self.wvol if x in self.rvol]) + ): + pass + else: raise Pebkac(403, "'stack' not allowed for user " + self.uname) - if self.args.no_stack: - raise Pebkac(403, "the stackdump feature is disabled in server config") - - ret = "
{}\n{}".format(time.time(), html_escape(alltrace()))
+        ret = html_escape(alltrace(self.args.stack_v))
+        if self.args.stack_v:
+            ret = "
%s\n%s" % (time.time(), ret)
+        else:
+            ret = "
%s" % (ret,)
         self.reply(ret.encode("utf-8"))
         return True
 
diff --git a/copyparty/svchub.py b/copyparty/svchub.py
index 3efe5d24..65420924 100644
--- a/copyparty/svchub.py
+++ b/copyparty/svchub.py
@@ -291,6 +291,9 @@ class SvcHub(object):
         ch = "abcdefghijklmnopqrstuvwx"[int(args.theme / 2)]
         args.theme = "{0}{1} {0} {1}".format(ch, bri)
 
+        if args.no_stack:
+            args.stack_who = "no"
+
         if args.nid:
             args.du_who = "no"
         args.du_iwho = n_du_who(args.du_who)
diff --git a/copyparty/util.py b/copyparty/util.py
index 0b7cbd07..1837233b 100644
--- a/copyparty/util.py
+++ b/copyparty/util.py
@@ -1500,20 +1500,24 @@ def trace(*args: Any, **kwargs: Any) -> None:
     nuprint(msg)
 
 
-def alltrace() -> str:
+def alltrace(verbose: bool = True) -> str:
     threads: dict[str, types.FrameType] = {}
     names = dict([(t.ident, t.name) for t in threading.enumerate()])
     for tid, stack in sys._current_frames().items():
-        name = "%s (%x)" % (names.get(tid), tid)
+        if verbose:
+            name = "%s (%x)" % (names.get(tid), tid)
+        else:
+            name = str(names.get(tid))
         threads[name] = stack
 
     rret: list[str] = []
     bret: list[str] = []
+    np = -3 if verbose else -2
     for name, stack in sorted(threads.items()):
         ret = ["\n\n# %s" % (name,)]
         pad = None
         for fn, lno, name, line in traceback.extract_stack(stack):
-            fn = os.sep.join(fn.split(os.sep)[-3:])
+            fn = os.sep.join(fn.split(os.sep)[np:])
             ret.append('File: "%s", line %d, in %s' % (fn, lno, name))
             if line:
                 ret.append("  " + str(line.strip()))