copyparty 1.13.6__py3-none-any.whl → 1.13.7__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.
Files changed (42) hide show
  1. copyparty/__main__.py +25 -7
  2. copyparty/__version__.py +2 -2
  3. copyparty/authsrv.py +9 -6
  4. copyparty/cert.py +1 -1
  5. copyparty/fsutil.py +3 -3
  6. copyparty/ftpd.py +15 -2
  7. copyparty/httpcli.py +221 -81
  8. copyparty/httpconn.py +3 -0
  9. copyparty/httpsrv.py +35 -11
  10. copyparty/ico.py +1 -1
  11. copyparty/mtag.py +15 -6
  12. copyparty/pwhash.py +10 -0
  13. copyparty/smbd.py +20 -2
  14. copyparty/ssdp.py +3 -3
  15. copyparty/stolen/dnslib/dns.py +6 -0
  16. copyparty/stolen/ifaddr/__init__.py +15 -1
  17. copyparty/stolen/ifaddr/_shared.py +1 -0
  18. copyparty/stolen/qrcodegen.py +6 -0
  19. copyparty/sutil.py +1 -1
  20. copyparty/svchub.py +72 -3
  21. copyparty/szip.py +1 -3
  22. copyparty/tcpsrv.py +60 -8
  23. copyparty/tftpd.py +30 -4
  24. copyparty/th_srv.py +22 -1
  25. copyparty/u2idx.py +4 -1
  26. copyparty/up2k.py +221 -93
  27. copyparty/util.py +166 -31
  28. copyparty/web/a/u2c.py +10 -3
  29. copyparty/web/browser.css.gz +0 -0
  30. copyparty/web/browser2.html +0 -1
  31. copyparty/web/md.html +3 -0
  32. copyparty/web/mde.html +3 -0
  33. copyparty/web/msg.html +3 -0
  34. copyparty/web/splash.html +3 -0
  35. copyparty/web/svcs.html +3 -0
  36. copyparty/web/up2k.js.gz +0 -0
  37. {copyparty-1.13.6.dist-info → copyparty-1.13.7.dist-info}/METADATA +64 -14
  38. {copyparty-1.13.6.dist-info → copyparty-1.13.7.dist-info}/RECORD +42 -42
  39. {copyparty-1.13.6.dist-info → copyparty-1.13.7.dist-info}/LICENSE +0 -0
  40. {copyparty-1.13.6.dist-info → copyparty-1.13.7.dist-info}/WHEEL +0 -0
  41. {copyparty-1.13.6.dist-info → copyparty-1.13.7.dist-info}/entry_points.txt +0 -0
  42. {copyparty-1.13.6.dist-info → copyparty-1.13.7.dist-info}/top_level.txt +0 -0
copyparty/__main__.py CHANGED
@@ -61,7 +61,13 @@ from .util import (
61
61
  wrap,
62
62
  )
63
63
 
64
+ if PY2:
65
+ range = xrange # type: ignore
66
+
64
67
  try:
68
+ if os.environ.get("PRTY_NO_TLS"):
69
+ raise Exception()
70
+
65
71
  HAVE_SSL = True
66
72
  import ssl
67
73
  except:
@@ -338,7 +344,7 @@ def configure_ssl_ver(al ) :
338
344
  # oh man i love openssl
339
345
  # check this out
340
346
  # hold my beer
341
- assert ssl
347
+ assert ssl # type: ignore
342
348
  ptn = re.compile(r"^OP_NO_(TLS|SSL)v")
343
349
  sslver = terse_sslver(al.ssl_ver).split(",")
344
350
  flags = [k for k in ssl.__dict__ if ptn.match(k)]
@@ -372,7 +378,7 @@ def configure_ssl_ver(al ) :
372
378
 
373
379
 
374
380
  def configure_ssl_ciphers(al ) :
375
- assert ssl
381
+ assert ssl # type: ignore
376
382
  ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
377
383
  if al.ssl_ver:
378
384
  ctx.options &= ~al.ssl_flags_en
@@ -485,6 +491,9 @@ def disable_quickedit() :
485
491
 
486
492
 
487
493
  def sfx_tpoke(top ):
494
+ if os.environ.get("PRTY_NO_TPOKE"):
495
+ return
496
+
488
497
  files = [top] + [
489
498
  os.path.join(dp, p) for dp, dd, df in os.walk(top) for p in dd + df
490
499
  ]
@@ -689,6 +698,11 @@ def get_sects():
689
698
  \033[36mxban\033[0m can be used to overrule / cancel a user ban event;
690
699
  if the program returns 0 (true/OK) then the ban will NOT happen
691
700
 
701
+ effects can be used to redirect uploads into other
702
+ locations, and to delete or index other files based
703
+ on new uploads, but with certain limitations. See
704
+ bin/hooks/reloc* and docs/devnotes.md#hook-effects
705
+
692
706
  except for \033[36mxm\033[0m, only one hook / one action can run at a time,
693
707
  so it's recommended to use the \033[36mf\033[0m flag unless you really need
694
708
  to wait for the hook to finish before continuing (without \033[36mf\033[0m
@@ -949,8 +963,8 @@ def add_upload(ap):
949
963
 
950
964
  def add_network(ap):
951
965
  ap2 = ap.add_argument_group('network options')
952
- ap2.add_argument("-i", metavar="IP", type=u, default="::", help="ip to bind (comma-sep.), default: all IPv4 and IPv6")
953
- ap2.add_argument("-p", metavar="PORT", type=u, default="3923", help="ports to bind (comma/range)")
966
+ ap2.add_argument("-i", metavar="IP", type=u, default="::", help="ip to bind (comma-sep.) and/or [\033[32munix:/tmp/a.sock\033[0m], default: all IPv4 and IPv6")
967
+ ap2.add_argument("-p", metavar="PORT", type=u, default="3923", help="ports to bind (comma/range); ignored for unix-sockets")
954
968
  ap2.add_argument("--ll", action="store_true", help="include link-local IPv4/IPv6 in mDNS replies, even if the NIC has routable IPs (breaks some mDNS clients)")
955
969
  ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=1, help="which ip to associate clients with; [\033[32m0\033[0m]=tcp, [\033[32m1\033[0m]=origin (first x-fwd, unsafe), [\033[32m2\033[0m]=outermost-proxy, [\033[32m3\033[0m]=second-proxy, [\033[32m-1\033[0m]=closest-proxy")
956
970
  ap2.add_argument("--xff-hdr", metavar="NAME", type=u, default="x-forwarded-for", help="if reverse-proxied, which http header to read the client's real ip from")
@@ -1117,6 +1131,7 @@ def add_hooks(ap):
1117
1131
  ap2.add_argument("--xad", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m after a file delete")
1118
1132
  ap2.add_argument("--xm", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m on message")
1119
1133
  ap2.add_argument("--xban", metavar="CMD", type=u, action="append", help="execute \033[33mCMD\033[0m if someone gets banned (pw/404/403/url)")
1134
+ ap2.add_argument("--hook-v", action="store_true", help="verbose hooks")
1120
1135
 
1121
1136
 
1122
1137
  def add_stats(ap):
@@ -1345,9 +1360,10 @@ def add_ui(ap, retry):
1345
1360
  ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
1346
1361
  ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
1347
1362
  ap2.add_argument("--mpmc", metavar="URL", type=u, default="", help="change the mediaplayer-toggle mouse cursor; URL to a folder with {2..5}.png inside (or disable with [\033[32m.\033[0m])")
1348
- ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include")
1349
- ap2.add_argument("--css-browser", metavar="L", type=u, default="", help="URL to additional CSS to include")
1350
- ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages; can be @PATH to send the contents of a file at PATH, and/or begin with %% to render as jinja2 template (volflag=html_head)")
1363
+ ap2.add_argument("--css-browser", metavar="L", type=u, default="", help="URL to additional CSS to include in the filebrowser html")
1364
+ ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include in the filebrowser html")
1365
+ ap2.add_argument("--js-other", metavar="L", type=u, default="", help="URL to additional JS to include in all other pages")
1366
+ ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages (except for basic-browser); can be @PATH to send the contents of a file at PATH, and/or begin with %% to render as jinja2 template (volflag=html_head)")
1351
1367
  ap2.add_argument("--ih", action="store_true", help="if a folder contains index.html, show that instead of the directory listing by default (can be changed in the client settings UI, or add ?v to URL for override)")
1352
1368
  ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
1353
1369
  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)")
@@ -1366,12 +1382,14 @@ def add_debug(ap):
1366
1382
  ap2 = ap.add_argument_group('debug options')
1367
1383
  ap2.add_argument("--vc", action="store_true", help="verbose config file parser (explain config)")
1368
1384
  ap2.add_argument("--cgen", action="store_true", help="generate config file from current config (best-effort; probably buggy)")
1385
+ ap2.add_argument("--deps", action="store_true", help="list information about detected optional dependencies")
1369
1386
  if hasattr(select, "poll"):
1370
1387
  ap2.add_argument("--no-poll", action="store_true", help="kernel-bug workaround: disable poll; use select instead (limits max num clients to ~700)")
1371
1388
  ap2.add_argument("--no-sendfile", action="store_true", help="kernel-bug workaround: disable sendfile; do a safe and slow read-send-loop instead")
1372
1389
  ap2.add_argument("--no-scandir", action="store_true", help="kernel-bug workaround: disable scandir; do a listdir + stat on each file instead")
1373
1390
  ap2.add_argument("--no-fastboot", action="store_true", help="wait for initial filesystem indexing before accepting client requests")
1374
1391
  ap2.add_argument("--no-htp", action="store_true", help="disable httpserver threadpool, create threads as-needed instead")
1392
+ ap2.add_argument("--rm-sck", action="store_true", help="when listening on unix-sockets, do a basic delete+bind instead of the default atomic bind")
1375
1393
  ap2.add_argument("--srch-dbg", action="store_true", help="explain search processing, and do some extra expensive sanity checks")
1376
1394
  ap2.add_argument("--rclone-mdns", action="store_true", help="use mdns-domain instead of server-ip on /?hc")
1377
1395
  ap2.add_argument("--stackmon", metavar="P,S", type=u, default="", help="write stacktrace to \033[33mP\033[0math every \033[33mS\033[0m second, for example --stackmon=\033[32m./st/%%Y-%%m/%%d/%%H%%M.xz,60")
copyparty/__version__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 13, 6)
3
+ VERSION = (1, 13, 7)
4
4
  CODENAME = "race the beam"
5
- BUILD_DT = (2024, 7, 29)
5
+ BUILD_DT = (2024, 8, 12)
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
@@ -12,7 +12,7 @@ import threading
12
12
  import time
13
13
  from datetime import datetime
14
14
 
15
- from .__init__ import ANYWIN, TYPE_CHECKING, WINDOWS, E
15
+ from .__init__ import ANYWIN, PY2, TYPE_CHECKING, WINDOWS, E
16
16
  from .bos import bos
17
17
  from .cfg import flagdescs, permdescs, vf_bmap, vf_cmap, vf_vmap
18
18
  from .pwhash import PWHash
@@ -49,6 +49,9 @@ if TYPE_CHECKING:
49
49
  # Vflags: TypeAlias = dict[str, Any]
50
50
  # Mflags: TypeAlias = dict[str, Vflags]
51
51
 
52
+ if PY2:
53
+ range = xrange # type: ignore
54
+
52
55
 
53
56
  LEELOO_DALLAS = "leeloo_dallas"
54
57
 
@@ -434,7 +437,7 @@ class VFS(object):
434
437
 
435
438
  def _find(self, vpath ) :
436
439
  """return [vfs,remainder]"""
437
- if vpath == "":
440
+ if not vpath:
438
441
  return self, ""
439
442
 
440
443
  if "/" in vpath:
@@ -444,7 +447,7 @@ class VFS(object):
444
447
  rem = ""
445
448
 
446
449
  if name in self.nodes:
447
- return self.nodes[name]._find(undot(rem))
450
+ return self.nodes[name]._find(rem)
448
451
 
449
452
  return self, vpath
450
453
 
@@ -511,8 +514,8 @@ class VFS(object):
511
514
  t = "{} has no {} in [{}] => [{}] => [{}]"
512
515
  self.log("vfs", t.format(uname, msg, vpath, cvpath, ap), 6)
513
516
 
514
- t = 'you don\'t have %s-access in "/%s"'
515
- raise Pebkac(err, t % (msg, cvpath))
517
+ t = 'you don\'t have %s-access in "/%s" or below "/%s"'
518
+ raise Pebkac(err, t % (msg, cvpath, vn.vpath))
516
519
 
517
520
  return vn, rem
518
521
 
@@ -1888,7 +1891,7 @@ class AuthSrv(object):
1888
1891
  self.log(t.format(vol.vpath), 1)
1889
1892
  del vol.flags["lifetime"]
1890
1893
 
1891
- needs_e2d = [x for x in hooks if x != "xm"]
1894
+ needs_e2d = [x for x in hooks if x in ("xau", "xiu")]
1892
1895
  drop = [x for x in needs_e2d if vol.flags.get(x)]
1893
1896
  if drop:
1894
1897
  t = 'removing [{}] from volume "/{}" because e2d is disabled'
copyparty/cert.py CHANGED
@@ -9,7 +9,7 @@ import time
9
9
  from .__init__ import ANYWIN
10
10
  from .util import Netdev, runcmd, wrename, wunlink
11
11
 
12
- HAVE_CFSSL = True
12
+ HAVE_CFSSL = not os.environ.get("PRTY_NO_CFSSL")
13
13
 
14
14
  if ANYWIN:
15
15
  VF = {"mv_re_t": 5, "rm_re_t": 5, "mv_re_r": 0.1, "rm_re_r": 0.1}
copyparty/fsutil.py CHANGED
@@ -9,7 +9,7 @@ import time
9
9
  from .__init__ import ANYWIN, MACOS
10
10
  from .authsrv import AXS, VFS
11
11
  from .bos import bos
12
- from .util import chkcmd, min_ex
12
+ from .util import chkcmd, min_ex, undot
13
13
 
14
14
  class Fstab(object):
15
15
  def __init__(self, log , args ):
@@ -46,7 +46,7 @@ class Fstab(object):
46
46
  self.log(msg.format(path, fs, min_ex()), 3)
47
47
  return fs
48
48
 
49
- path = path.lstrip("/")
49
+ path = undot(path)
50
50
  try:
51
51
  return self.cache[path]
52
52
  except:
@@ -118,7 +118,7 @@ class Fstab(object):
118
118
  if ANYWIN:
119
119
  path = self._winpath(path)
120
120
 
121
- path = path.lstrip("/")
121
+ path = undot(path)
122
122
  ptn = re.compile(r"^[^\\/]*")
123
123
  vn, rem = self.tab._find(path)
124
124
  if not self.trusted:
copyparty/ftpd.py CHANGED
@@ -37,6 +37,10 @@ from .util import (
37
37
  if TYPE_CHECKING:
38
38
  from .svchub import SvcHub
39
39
 
40
+ if PY2:
41
+ range = xrange # type: ignore
42
+
43
+
40
44
  class FSE(FilesystemError):
41
45
  def __init__(self, msg , severity = 0) :
42
46
  super(FilesystemError, self).__init__(msg)
@@ -345,7 +349,7 @@ class FtpFs(AbstractedFS):
345
349
  svp = join(self.cwd, src).lstrip("/")
346
350
  dvp = join(self.cwd, dst).lstrip("/")
347
351
  try:
348
- self.hub.up2k.handle_mv(self.uname, svp, dvp)
352
+ self.hub.up2k.handle_mv(self.uname, self.h.cli_ip, svp, dvp)
349
353
  except Exception as ex:
350
354
  raise FSE(str(ex))
351
355
 
@@ -463,6 +467,9 @@ class FtpHandler(FTPHandler):
463
467
  xbu = vfs.flags.get("xbu")
464
468
  if xbu and not runhook(
465
469
  None,
470
+ None,
471
+ self.hub.up2k,
472
+ "xbu.ftpd",
466
473
  xbu,
467
474
  ap,
468
475
  vp,
@@ -472,7 +479,7 @@ class FtpHandler(FTPHandler):
472
479
  0,
473
480
  0,
474
481
  self.cli_ip,
475
- 0,
482
+ time.time(),
476
483
  "",
477
484
  ):
478
485
  raise FSE("Upload blocked by xbu server config")
@@ -575,9 +582,15 @@ class Ftpd(object):
575
582
  if "::" in ips:
576
583
  ips.append("0.0.0.0")
577
584
 
585
+ ips = [x for x in ips if "unix:" not in x]
586
+
578
587
  if self.args.ftp4:
579
588
  ips = [x for x in ips if ":" not in x]
580
589
 
590
+ if not ips:
591
+ lgr.fatal("cannot start ftp-server; no compatible IPs in -i")
592
+ return
593
+
581
594
  ips = list(ODict.fromkeys(ips)) # dedup
582
595
 
583
596
  ioloop = IOLoop()