copyparty 1.16.8__py3-none-any.whl → 1.16.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
copyparty/__main__.py CHANGED
@@ -54,6 +54,8 @@ from .util import (
54
54
  RAM_TOTAL,
55
55
  SQLITE_VER,
56
56
  UNPLICATIONS,
57
+ URL_BUG,
58
+ URL_PRJ,
57
59
  Daemon,
58
60
  align_tab,
59
61
  ansi_re,
@@ -326,17 +328,16 @@ def ensure_webdeps() :
326
328
  if has_resource(E, "web/deps/mini-fa.woff"):
327
329
  return
328
330
 
329
- warn(
330
- """could not find webdeps;
331
+ t = """could not find webdeps;
331
332
  if you are running the sfx, or exe, or pypi package, or docker image,
332
333
  then this is a bug! Please let me know so I can fix it, thanks :-)
333
- https://github.com/9001/copyparty/issues/new?labels=bug&template=bug_report.md
334
+ %s
334
335
 
335
336
  however, if you are a dev, or running copyparty from source, and you want
336
337
  full client functionality, you will need to build or obtain the webdeps:
337
- https://github.com/9001/copyparty/blob/hovudstraum/docs/devnotes.md#building
338
+ %s/blob/hovudstraum/docs/devnotes.md#building
338
339
  """
339
- )
340
+ warn(t % (URL_BUG, URL_PRJ))
340
341
 
341
342
 
342
343
  def configure_ssl_ver(al ) :
@@ -731,6 +732,10 @@ def get_sects():
731
732
  the \033[33m,,\033[35m stops copyparty from reading the rest as flags and
732
733
  the \033[33m--\033[35m stops notify-send from reading the message as args
733
734
  and the alert will be "hey" followed by the messagetext
735
+
736
+ \033[36m--xau zmq:pub:tcp://*:5556\033[35m announces uploads on zeromq;
737
+ \033[36m--xau t3,zmq:push:tcp://*:5557\033[35m also works, and you can
738
+ \033[36m--xau t3,j,zmq:req:tcp://localhost:5555\033[35m too for example
734
739
  \033[0m
735
740
  each hook is executed once for each event, except for \033[36mxiu\033[0m
736
741
  which builds up a backlog of uploads, running the hook just once
@@ -762,11 +767,22 @@ def get_sects():
762
767
  values for --urlform:
763
768
  \033[36mstash\033[35m dumps the data to file and returns length + checksum
764
769
  \033[36msave,get\033[35m dumps to file and returns the page like a GET
765
- \033[36mprint,get\033[35m prints the data in the log and returns GET
766
- (leave out the ",get" to return an error instead)\033[0m
770
+ \033[36mprint \033[35m prints the data to log and returns an error
771
+ \033[36mprint,xm \033[35m prints the data to log and returns --xm output
772
+ \033[36mprint,get\033[35m prints the data to log and returns GET\033[0m
773
+
774
+ note that the \033[35m--xm\033[0m hook will only run if \033[35m--urlform\033[0m is
775
+ either \033[36mprint\033[0m or \033[36mprint,get\033[0m or the default \033[36mprint,xm\033[0m
776
+
777
+ if an \033[35m--xm\033[0m hook returns text, then
778
+ the response code will be HTTP 202;
779
+ http/get responses will be HTTP 200
780
+
781
+ if there are multiple \033[35m--xm\033[0m hooks defined, then
782
+ the first hook that produced output is returned
767
783
 
768
- note that the \033[35m--xm\033[0m hook will only run if \033[35m--urlform\033[0m
769
- is either \033[36mprint\033[0m or the default \033[36mprint,get\033[0m
784
+ if there are no \033[35m--xm\033[0m hooks defined, then the default
785
+ \033[36mprint,xm\033[0m behaves like \033[36mprint,get\033[0m (returning html)
770
786
  """
771
787
  ),
772
788
  ],
@@ -947,7 +963,7 @@ def add_general(ap, nc, srvname):
947
963
  ap2.add_argument("-v", metavar="VOL", type=u, action="append", help="add volume, \033[33mSRC\033[0m:\033[33mDST\033[0m:\033[33mFLAG\033[0m; examples [\033[32m.::r\033[0m], [\033[32m/mnt/nas/music:/music:r:aed\033[0m], see --help-accounts")
948
964
  ap2.add_argument("--grp", metavar="G:N,N", type=u, action="append", help="add group, \033[33mNAME\033[0m:\033[33mUSER1\033[0m,\033[33mUSER2\033[0m,\033[33m...\033[0m; example [\033[32madmins:ed,foo,bar\033[0m]")
949
965
  ap2.add_argument("-ed", action="store_true", help="enable the ?dots url parameter / client option which allows clients to see dotfiles / hidden files (volflag=dots)")
950
- ap2.add_argument("--urlform", metavar="MODE", type=u, default="print,get", help="how to handle url-form POSTs; see \033[33m--help-urlform\033[0m")
966
+ ap2.add_argument("--urlform", metavar="MODE", type=u, default="print,xm", help="how to handle url-form POSTs; see \033[33m--help-urlform\033[0m")
951
967
  ap2.add_argument("--wintitle", metavar="TXT", type=u, default="cpp @ $pub", help="server terminal title, for example [\033[32m$ip-10.1.2.\033[0m] or [\033[32m$ip-]")
952
968
  ap2.add_argument("--name", metavar="TXT", type=u, default=srvname, help="server name (displayed topleft in browser and in mDNS)")
953
969
  ap2.add_argument("--mime", metavar="EXT=MIME", type=u, action="append", help="map file \033[33mEXT\033[0mension to \033[33mMIME\033[0mtype, for example [\033[32mjpg=image/jpeg\033[0m]")
@@ -1320,6 +1336,7 @@ def add_admin(ap):
1320
1336
  ap2.add_argument("--no-ups-page", action="store_true", help="disable ?ru (list of recent uploads)")
1321
1337
  ap2.add_argument("--no-up-list", action="store_true", help="don't show list of incoming files in controlpanel")
1322
1338
  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")
1339
+ 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)")
1323
1340
  ap2.add_argument("--ups-when", action="store_true", help="let everyone see upload timestamps on the ?ru page, not just admins")
1324
1341
 
1325
1342
 
@@ -1360,6 +1377,8 @@ def add_transcoding(ap):
1360
1377
  ap2 = ap.add_argument_group('transcoding options')
1361
1378
  ap2.add_argument("--q-opus", metavar="KBPS", type=int, default=128, help="target bitrate for transcoding to opus; set 0 to disable")
1362
1379
  ap2.add_argument("--q-mp3", metavar="QUALITY", type=u, default="q2", help="target quality for transcoding to mp3, for example [\033[32m192k\033[0m] (CBR) or [\033[32mq0\033[0m] (CQ/CRF, q0=maxquality, q9=smallest); set 0 to disable")
1380
+ ap2.add_argument("--no-caf", action="store_true", help="disable transcoding to caf-opus (affects iOS v12~v17), will use mp3 instead")
1381
+ ap2.add_argument("--no-owa", action="store_true", help="disable transcoding to webm-opus (iOS v18 and later), will use mp3 instead")
1363
1382
  ap2.add_argument("--no-acode", action="store_true", help="disable audio transcoding")
1364
1383
  ap2.add_argument("--no-bacode", action="store_true", help="disable batch audio transcoding by folder download (zip/tar)")
1365
1384
  ap2.add_argument("--ac-maxage", metavar="SEC", type=int, default=86400, help="delete cached transcode output after \033[33mSEC\033[0m seconds")
@@ -1468,12 +1487,14 @@ def add_ui(ap, retry):
1468
1487
  ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
1469
1488
  ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty @ --name", help="title / service-name to show in html documents")
1470
1489
  ap2.add_argument("--bname", metavar="TXT", type=u, default="--name", help="server name (displayed in filebrowser document title)")
1471
- ap2.add_argument("--pb-url", metavar="URL", type=u, default="https://github.com/9001/copyparty", help="powered-by link; disable with \033[33m-np\033[0m")
1490
+ ap2.add_argument("--pb-url", metavar="URL", type=u, default=URL_PRJ, help="powered-by link; disable with \033[33m-np\033[0m")
1472
1491
  ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m)")
1473
1492
  ap2.add_argument("--k304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable k304 on the controlpanel (workaround for buggy reverse-proxies); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
1474
1493
  ap2.add_argument("--no304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable no304 on the controlpanel (workaround for buggy caching in browsers); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
1475
- ap2.add_argument("--md-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to ALLOW for README.md docs (volflag=md_sbf); see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox")
1476
- ap2.add_argument("--lg-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to ALLOW for prologue/epilogue docs (volflag=lg_sbf)")
1494
+ ap2.add_argument("--md-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to allow in the iframe 'sandbox' attribute for README.md docs (volflag=md_sbf); see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox")
1495
+ ap2.add_argument("--lg-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to allow in the iframe 'sandbox' attribute for prologue/epilogue docs (volflag=lg_sbf)")
1496
+ ap2.add_argument("--md-sba", metavar="TXT", type=u, default="", help="the value of the iframe 'allow' attribute for README.md docs, for example [\033[32mfullscreen\033[0m] (volflag=md_sba)")
1497
+ ap2.add_argument("--lg-sba", metavar="TXT", type=u, default="", help="the value of the iframe 'allow' attribute for prologue/epilogue docs (volflag=lg_sba); see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy#iframes")
1477
1498
  ap2.add_argument("--no-sb-md", action="store_true", help="don't sandbox README/PREADME.md documents (volflags: no_sb_md | sb_md)")
1478
1499
  ap2.add_argument("--no-sb-lg", action="store_true", help="don't sandbox prologue/epilogue docs (volflags: no_sb_lg | sb_lg); enables non-js support")
1479
1500
 
copyparty/__version__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 16, 8)
3
+ VERSION = (1, 16, 10)
4
4
  CODENAME = "COPYparty"
5
- BUILD_DT = (2025, 1, 11)
5
+ BUILD_DT = (2025, 1, 25)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
copyparty/authsrv.py CHANGED
@@ -1825,7 +1825,11 @@ class AuthSrv(object):
1825
1825
  if fka and not fk:
1826
1826
  fk = fka
1827
1827
  if fk:
1828
- vol.flags["fk"] = int(fk) if fk is not True else 8
1828
+ fk = 8 if fk is True else int(fk)
1829
+ if fk > 72:
1830
+ t = "max filekey-length is 72; volume /%s specified %d (anything higher than 16 is pointless btw)"
1831
+ raise Exception(t % (vol.vpath, fk))
1832
+ vol.flags["fk"] = fk
1829
1833
  have_fk = True
1830
1834
 
1831
1835
  dk = vol.flags.get("dk")
@@ -2332,6 +2336,7 @@ class AuthSrv(object):
2332
2336
  "frand": bool(vf.get("rand")),
2333
2337
  "lifetime": vf.get("lifetime") or 0,
2334
2338
  "unlist": vf.get("unlist") or "",
2339
+ "sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
2335
2340
  }
2336
2341
  js_htm = {
2337
2342
  "s_name": self.args.bname,
@@ -2344,6 +2349,8 @@ class AuthSrv(object):
2344
2349
  "have_unpost": int(self.args.unpost),
2345
2350
  "have_emp": self.args.emp,
2346
2351
  "sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
2352
+ "sba_md": vf.get("md_sba") or "",
2353
+ "sba_lg": vf.get("lg_sba") or "",
2347
2354
  "txt_ext": self.args.textfiles.replace(",", " "),
2348
2355
  "def_hcols": list(vf.get("mth") or []),
2349
2356
  "unlist0": vf.get("unlist") or "",
copyparty/cfg.py CHANGED
@@ -74,6 +74,8 @@ def vf_vmap() :
74
74
  "html_head",
75
75
  "lg_sbf",
76
76
  "md_sbf",
77
+ "lg_sba",
78
+ "md_sba",
77
79
  "nrand",
78
80
  "og_desc",
79
81
  "og_site",
@@ -91,6 +93,7 @@ def vf_vmap() :
91
93
  "unlist",
92
94
  "u2abort",
93
95
  "u2ts",
96
+ "ups_who",
94
97
  ):
95
98
  ret[k] = k
96
99
  return ret
@@ -144,6 +147,7 @@ flagcats = {
144
147
  "noclone": "take dupe data from clients, even if available on HDD",
145
148
  "nodupe": "rejects existing files (instead of linking/cloning them)",
146
149
  "sparse": "force use of sparse files, mainly for s3-backed storage",
150
+ "nosparse": "deny use of sparse files, mainly for slow storage",
147
151
  "daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
148
152
  "nosub": "forces all uploads into the top folder of the vfs",
149
153
  "magic": "enables filetype detection for nameless uploads",
@@ -240,6 +244,8 @@ flagcats = {
240
244
  "sb_lg": "enable js sandbox for prologue/epilogue (default)",
241
245
  "md_sbf": "list of markdown-sandbox safeguards to disable",
242
246
  "lg_sbf": "list of *logue-sandbox safeguards to disable",
247
+ "md_sba": "value of iframe allow-prop for markdown-sandbox",
248
+ "lg_sba": "value of iframe allow-prop for *logue-sandbox",
243
249
  "nohtml": "return html and markdown as text/html",
244
250
  },
245
251
  "others": {
copyparty/dxml.py CHANGED
@@ -1,9 +1,16 @@
1
+ # coding: utf-8
2
+ from __future__ import print_function, unicode_literals
3
+
1
4
  import importlib
2
5
  import sys
3
6
  import xml.etree.ElementTree as ET
4
7
 
5
8
  from .__init__ import PY2
6
9
 
10
+ class BadXML(Exception):
11
+ pass
12
+
13
+
7
14
  def get_ET() :
8
15
  pn = "xml.etree.ElementTree"
9
16
  cn = "_elementtree"
@@ -30,7 +37,7 @@ def get_ET() :
30
37
  XMLParser = get_ET()
31
38
 
32
39
 
33
- class DXMLParser(XMLParser): # type: ignore
40
+ class _DXMLParser(XMLParser): # type: ignore
34
41
  def __init__(self) :
35
42
  tb = ET.TreeBuilder()
36
43
  super(DXMLParser, self).__init__(target=tb)
@@ -45,8 +52,12 @@ class DXMLParser(XMLParser): # type: ignore
45
52
  raise BadXML("{}, {}".format(a, ka))
46
53
 
47
54
 
48
- class BadXML(Exception):
49
- pass
55
+ class _NG(XMLParser): # type: ignore
56
+ def __int__(self) :
57
+ raise BadXML("dxml selftest failed")
58
+
59
+
60
+ DXMLParser = _DXMLParser
50
61
 
51
62
 
52
63
  def parse_xml(txt ) :
@@ -55,6 +66,40 @@ def parse_xml(txt ) :
55
66
  return parser.close() # type: ignore
56
67
 
57
68
 
69
+ def selftest() :
70
+ qbe = r"""<!DOCTYPE d [
71
+ <!ENTITY a "nice_bakuretsu">
72
+ ]>
73
+ <root>&a;&a;&a;</root>"""
74
+
75
+ emb = r"""<!DOCTYPE d [
76
+ <!ENTITY a SYSTEM "file:///etc/hostname">
77
+ ]>
78
+ <root>&a;</root>"""
79
+
80
+ # future-proofing; there's never been any known vulns
81
+ # regarding DTDs and ET.XMLParser, but might as well
82
+ # block them since webdav-clients don't use them
83
+ dtd = r"""<!DOCTYPE d SYSTEM "a.dtd">
84
+ <root>a</root>"""
85
+
86
+ for txt in (qbe, emb, dtd):
87
+ try:
88
+ parse_xml(txt)
89
+ t = "WARNING: dxml selftest failed:\n%s\n"
90
+ print(t % (txt,), file=sys.stderr)
91
+ return False
92
+ except BadXML:
93
+ pass
94
+
95
+ return True
96
+
97
+
98
+ DXML_OK = selftest()
99
+ if not DXML_OK:
100
+ DXMLParser = _NG
101
+
102
+
58
103
  def mktnod(name , text ) :
59
104
  el = ET.Element(name)
60
105
  el.text = text
copyparty/httpcli.py CHANGED
@@ -128,6 +128,8 @@ NO_CACHE = {"Cache-Control": "no-cache"}
128
128
 
129
129
  ALL_COOKIES = "k304 no304 js idxh dots cppwd cppws".split()
130
130
 
131
+ BADXFF = " due to dangerous misconfiguration (the http-header specified by --xff-hdr was received from an untrusted reverse-proxy)"
132
+
131
133
  H_CONN_KEEPALIVE = "Connection: Keep-Alive"
132
134
  H_CONN_CLOSE = "Connection: Close"
133
135
 
@@ -157,6 +159,8 @@ class HttpCli(object):
157
159
 
158
160
  def __init__(self, conn ) :
159
161
 
162
+ empty_stringlist = []
163
+
160
164
  self.t0 = time.time()
161
165
  self.conn = conn
162
166
  self.u2mutex = conn.u2mutex # mypy404
@@ -202,9 +206,7 @@ class HttpCli(object):
202
206
  self.trailing_slash = True
203
207
  self.uname = " "
204
208
  self.pw = " "
205
- self.rvol = [" "]
206
- self.wvol = [" "]
207
- self.avol = [" "]
209
+ self.rvol = self.wvol = self.avol = empty_stringlist
208
210
  self.do_log = True
209
211
  self.can_read = False
210
212
  self.can_write = False
@@ -385,6 +387,7 @@ class HttpCli(object):
385
387
  ) + "0.0/16"
386
388
  zs2 = ' or "--xff-src=lan"' if self.conn.xff_lan.map(pip) else ""
387
389
  self.log(t % (self.args.xff_hdr, pip, cli_ip, zso, zs, zs2), 3)
390
+ self.bad_xff = True
388
391
  else:
389
392
  self.ip = cli_ip
390
393
  self.is_vproxied = bool(self.args.R)
@@ -505,7 +508,7 @@ class HttpCli(object):
505
508
  return False
506
509
 
507
510
  if "k" in uparam:
508
- m = RE_K.search(uparam["k"])
511
+ m = re_k.search(uparam["k"])
509
512
  if m:
510
513
  zs = uparam["k"]
511
514
  t = "malicious user; illegal filekey; req(%r) k(%r) => %r"
@@ -1888,6 +1891,9 @@ class HttpCli(object):
1888
1891
  if "stash" in opt:
1889
1892
  return self.handle_stash(False)
1890
1893
 
1894
+ xm = []
1895
+ xm_rsp = {}
1896
+
1891
1897
  if "save" in opt:
1892
1898
  post_sz, _, _, _, _, path, _ = self.dump_to_file(False)
1893
1899
  self.log("urlform: %d bytes, %r" % (post_sz, path))
@@ -1910,7 +1916,7 @@ class HttpCli(object):
1910
1916
  plain = plain[4:]
1911
1917
  xm = self.vn.flags.get("xm")
1912
1918
  if xm:
1913
- runhook(
1919
+ xm_rsp = runhook(
1914
1920
  self.log,
1915
1921
  self.conn.hsrv.broker,
1916
1922
  None,
@@ -1934,6 +1940,13 @@ class HttpCli(object):
1934
1940
  except Exception as ex:
1935
1941
  self.log(repr(ex))
1936
1942
 
1943
+ if "xm" in opt:
1944
+ if xm:
1945
+ self.loud_reply(xm_rsp.get("stdout") or "", status=202)
1946
+ return True
1947
+ else:
1948
+ return self.handle_get()
1949
+
1937
1950
  if "get" in opt:
1938
1951
  return self.handle_get()
1939
1952
 
@@ -4334,7 +4347,7 @@ class HttpCli(object):
4334
4347
  self.log,
4335
4348
  self.asrv,
4336
4349
  fgen,
4337
- utf8="utf" in uarg,
4350
+ utf8="utf" in uarg or not uarg,
4338
4351
  pre_crc="crc" in uarg,
4339
4352
  cmp=uarg if cancmp or uarg == "pax" else "",
4340
4353
  )
@@ -4982,8 +4995,16 @@ class HttpCli(object):
4982
4995
  and (self.uname in vol.axs.uread or self.uname in vol.axs.upget)
4983
4996
  }
4984
4997
 
4998
+ bad_xff = hasattr(self, "bad_xff")
4999
+ if bad_xff:
5000
+ allvols = []
5001
+ t = "will not return list of recent uploads" + BADXFF
5002
+ self.log(t, 1)
5003
+ if self.avol:
5004
+ raise Pebkac(500, t)
5005
+
4985
5006
  x = self.conn.hsrv.broker.ask(
4986
- "up2k.get_unfinished_by_user", self.uname, self.ip
5007
+ "up2k.get_unfinished_by_user", self.uname, "" if bad_xff else self.ip
4987
5008
  )
4988
5009
  uret = x.get()
4989
5010
 
@@ -5109,6 +5130,12 @@ class HttpCli(object):
5109
5130
  adm = "*" in vol.axs.uadmin or self.uname in vol.axs.uadmin
5110
5131
  dots = "*" in vol.axs.udot or self.uname in vol.axs.udot
5111
5132
 
5133
+ lvl = int(vol.flags["ups_who"])
5134
+ if not lvl:
5135
+ continue
5136
+ elif lvl == 1 and not adm:
5137
+ continue
5138
+
5112
5139
  n = 1000
5113
5140
  q = "select sz, rd, fn, ip, at from up where at>0 order by at desc"
5114
5141
  for sz, rd, fn, ip, at in cur.execute(q):
@@ -5377,12 +5404,16 @@ class HttpCli(object):
5377
5404
  if self.args.no_del:
5378
5405
  raise Pebkac(403, "the delete feature is disabled in server config")
5379
5406
 
5407
+ unpost = "unpost" in self.uparam
5408
+ if unpost and hasattr(self, "bad_xff"):
5409
+ self.log("unpost was denied" + BADXFF, 1)
5410
+ raise Pebkac(403, "the delete feature is disabled in server config")
5411
+
5380
5412
  if not req:
5381
5413
  req = [self.vpath]
5382
5414
  elif self.is_vproxied:
5383
5415
  req = [x[len(self.args.SR) :] for x in req]
5384
5416
 
5385
- unpost = "unpost" in self.uparam
5386
5417
  nlim = int(self.uparam.get("lim") or 0)
5387
5418
  lim = [nlim, nlim] if nlim else []
5388
5419
 
@@ -5782,7 +5813,7 @@ class HttpCli(object):
5782
5813
  "taglist": [],
5783
5814
  "have_tags_idx": int(e2t),
5784
5815
  "have_b_u": (self.can_write and self.uparam.get("b") == "u"),
5785
- "sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
5816
+ "sb_lg": vn.js_ls["sb_lg"],
5786
5817
  "url_suf": url_suf,
5787
5818
  "title": html_escape("%s %s" % (self.args.bname, self.vpath), crlf=True),
5788
5819
  "srv_info": srv_infot,
copyparty/svchub.py CHANGED
@@ -44,6 +44,8 @@ from .util import (
44
44
  FFMPEG_URL,
45
45
  HAVE_PSUTIL,
46
46
  HAVE_SQLITE3,
47
+ HAVE_ZMQ,
48
+ URL_BUG,
47
49
  UTC,
48
50
  VERSIONS,
49
51
  Daemon,
@@ -54,6 +56,7 @@ from .util import (
54
56
  alltrace,
55
57
  ansi_re,
56
58
  build_netmap,
59
+ expat_ver,
57
60
  load_ipu,
58
61
  min_ex,
59
62
  mp,
@@ -629,6 +632,7 @@ class SvcHub(object):
629
632
  (HAVE_FFPROBE, "ffprobe", t_ff + ", read audio/media tags"),
630
633
  (HAVE_MUTAGEN, "mutagen", "read audio tags (ffprobe is better but slower)"),
631
634
  (HAVE_ARGON2, "argon2", "secure password hashing (advanced users only)"),
635
+ (HAVE_ZMQ, "pyzmq", "send zeromq messages from event-hooks"),
632
636
  (HAVE_HEIF, "pillow-heif", "read .heif images with pillow (rarely useful)"),
633
637
  (HAVE_AVIF, "pillow-avif", "read .avif images with pillow (rarely useful)"),
634
638
  ]
@@ -685,6 +689,15 @@ class SvcHub(object):
685
689
  if self.args.bauth_last:
686
690
  self.log("root", "WARNING: ignoring --bauth-last due to --no-bauth", 3)
687
691
 
692
+ if not self.args.no_dav:
693
+ from .dxml import DXML_OK
694
+
695
+ if not DXML_OK:
696
+ if not self.args.no_dav:
697
+ self.args.no_dav = True
698
+ t = "WARNING:\nDisabling WebDAV support because dxml selftest failed. Please report this bug;\n%s\n...and include the following information in the bug-report:\n%s | expat %s\n"
699
+ self.log("root", t % (URL_BUG, VERSIONS, expat_ver()), 1)
700
+
688
701
  def _process_config(self) :
689
702
  al = self.args
690
703
 
copyparty/th_cli.py CHANGED
@@ -6,7 +6,7 @@ import os
6
6
  from .__init__ import TYPE_CHECKING
7
7
  from .authsrv import VFS
8
8
  from .bos import bos
9
- from .th_srv import HAVE_WEBP, thumb_path
9
+ from .th_srv import EXTS_AC, HAVE_WEBP, thumb_path
10
10
  from .util import Cooldown
11
11
 
12
12
  if TYPE_CHECKING:
@@ -54,13 +54,17 @@ class ThumbCli(object):
54
54
  if is_vid and "dvthumb" in dbv.flags:
55
55
  return None
56
56
 
57
- want_opus = fmt in ("opus", "caf", "mp3")
57
+ want_opus = fmt in EXTS_AC
58
58
  is_au = ext in self.fmt_ffa
59
59
  is_vau = want_opus and ext in self.fmt_ffv
60
60
  if is_au or is_vau:
61
61
  if want_opus:
62
62
  if self.args.no_acode:
63
63
  return None
64
+ elif fmt == "caf" and self.args.no_caf:
65
+ fmt = "mp3"
66
+ elif fmt == "owa" and self.args.no_owa:
67
+ fmt = "mp3"
64
68
  else:
65
69
  if "dathumb" in dbv.flags:
66
70
  return None