copyparty 1.13.6__py3-none-any.whl → 1.13.8__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 +25 -7
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +9 -6
- copyparty/cert.py +1 -1
- copyparty/fsutil.py +3 -3
- copyparty/ftpd.py +15 -2
- copyparty/httpcli.py +224 -81
- copyparty/httpconn.py +3 -0
- copyparty/httpsrv.py +38 -11
- copyparty/ico.py +1 -1
- copyparty/mtag.py +15 -6
- copyparty/pwhash.py +10 -0
- copyparty/smbd.py +20 -2
- copyparty/ssdp.py +3 -3
- copyparty/stolen/dnslib/dns.py +6 -0
- copyparty/stolen/ifaddr/__init__.py +15 -1
- copyparty/stolen/ifaddr/_shared.py +1 -0
- copyparty/stolen/qrcodegen.py +6 -0
- copyparty/sutil.py +1 -1
- copyparty/svchub.py +72 -3
- copyparty/szip.py +1 -3
- copyparty/tcpsrv.py +63 -8
- copyparty/tftpd.py +30 -4
- copyparty/th_srv.py +22 -1
- copyparty/u2idx.py +4 -1
- copyparty/up2k.py +221 -93
- copyparty/util.py +166 -31
- copyparty/web/a/u2c.py +10 -3
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser2.html +0 -1
- copyparty/web/md.html +3 -0
- copyparty/web/mde.html +3 -0
- copyparty/web/msg.html +3 -0
- copyparty/web/splash.html +3 -0
- copyparty/web/svcs.html +3 -0
- copyparty/web/up2k.js.gz +0 -0
- {copyparty-1.13.6.dist-info → copyparty-1.13.8.dist-info}/METADATA +64 -14
- {copyparty-1.13.6.dist-info → copyparty-1.13.8.dist-info}/RECORD +42 -42
- {copyparty-1.13.6.dist-info → copyparty-1.13.8.dist-info}/LICENSE +0 -0
- {copyparty-1.13.6.dist-info → copyparty-1.13.8.dist-info}/WHEEL +0 -0
- {copyparty-1.13.6.dist-info → copyparty-1.13.8.dist-info}/entry_points.txt +0 -0
- {copyparty-1.13.6.dist-info → copyparty-1.13.8.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("--
|
1349
|
-
ap2.add_argument("--
|
1350
|
-
ap2.add_argument("--
|
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
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(
|
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
|
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
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
|
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
|
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
|
-
|
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()
|