copyparty 1.19.0__py3-none-any.whl → 1.19.2__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 +131 -27
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +33 -7
- copyparty/cfg.py +7 -1
- copyparty/dxml.py +3 -0
- copyparty/ftpd.py +21 -6
- copyparty/httpcli.py +92 -23
- copyparty/httpsrv.py +6 -0
- copyparty/mdns.py +2 -1
- copyparty/mtag.py +88 -6
- copyparty/svchub.py +76 -5
- copyparty/tcpsrv.py +6 -0
- copyparty/th_cli.py +5 -1
- copyparty/th_srv.py +160 -51
- copyparty/u2idx.py +1 -1
- copyparty/up2k.py +80 -39
- copyparty/util.py +25 -1
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/rups.js.gz +0 -0
- copyparty/web/splash.css.gz +0 -0
- copyparty/web/splash.html +8 -1
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/svcs.html +1 -1
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.19.0.dist-info → copyparty-1.19.2.dist-info}/METADATA +39 -3
- {copyparty-1.19.0.dist-info → copyparty-1.19.2.dist-info}/RECORD +33 -33
- {copyparty-1.19.0.dist-info → copyparty-1.19.2.dist-info}/WHEEL +0 -0
- {copyparty-1.19.0.dist-info → copyparty-1.19.2.dist-info}/entry_points.txt +0 -0
- {copyparty-1.19.0.dist-info → copyparty-1.19.2.dist-info}/licenses/LICENSE +0 -0
- {copyparty-1.19.0.dist-info → copyparty-1.19.2.dist-info}/top_level.txt +0 -0
copyparty/httpcli.py
CHANGED
@@ -557,7 +557,7 @@ class HttpCli(object):
|
|
557
557
|
|
558
558
|
zso = self.headers.get("cookie")
|
559
559
|
if zso:
|
560
|
-
if len(zso) >
|
560
|
+
if len(zso) > self.args.cookie_cmax:
|
561
561
|
self.loud_reply("cookie header too big", status=400)
|
562
562
|
return False
|
563
563
|
zsll = [x.split("=", 1) for x in zso.split(";") if "=" in x]
|
@@ -565,11 +565,15 @@ class HttpCli(object):
|
|
565
565
|
cookie_pw = cookies.get("cppws") or cookies.get("cppwd") or ""
|
566
566
|
if "b" in cookies and "b" not in uparam:
|
567
567
|
uparam["b"] = cookies["b"]
|
568
|
+
if len(cookies) > self.args.cookie_nmax:
|
569
|
+
self.loud_reply("too many cookies", status=400)
|
568
570
|
else:
|
569
571
|
cookies = {}
|
570
572
|
cookie_pw = ""
|
571
573
|
|
572
|
-
if len(uparam) >
|
574
|
+
if len(uparam) > 12:
|
575
|
+
t = "http-request rejected; num.params: %d %r"
|
576
|
+
self.log(t % (len(uparam), self.req), 3)
|
573
577
|
self.loud_reply("u wot m8", status=400)
|
574
578
|
return False
|
575
579
|
|
@@ -615,8 +619,22 @@ class HttpCli(object):
|
|
615
619
|
or "*"
|
616
620
|
)
|
617
621
|
|
618
|
-
if self.args.
|
619
|
-
idp_usr =
|
622
|
+
if self.args.have_idp_hdrs:
|
623
|
+
idp_usr = ""
|
624
|
+
if self.args.idp_hm_usr:
|
625
|
+
for hn, hmv in self.args.idp_hm_usr_p.items():
|
626
|
+
zs = self.headers.get(hn)
|
627
|
+
if zs:
|
628
|
+
for zs1, zs2 in hmv.items():
|
629
|
+
if zs == zs1:
|
630
|
+
idp_usr = zs2
|
631
|
+
break
|
632
|
+
if idp_usr:
|
633
|
+
break
|
634
|
+
for hn in self.args.idp_h_usr:
|
635
|
+
if idp_usr:
|
636
|
+
break
|
637
|
+
idp_usr = self.headers.get(hn)
|
620
638
|
if idp_usr:
|
621
639
|
idp_grp = (
|
622
640
|
self.headers.get(self.args.idp_h_grp) or ""
|
@@ -672,8 +690,14 @@ class HttpCli(object):
|
|
672
690
|
else:
|
673
691
|
self.log("unknown username: %r" % (idp_usr,), 1)
|
674
692
|
|
675
|
-
if self.args.
|
676
|
-
self.
|
693
|
+
if self.args.have_ipu_or_ipr:
|
694
|
+
if self.args.ipu and self.uname == "*":
|
695
|
+
self.uname = self.conn.ipu_iu[self.conn.ipu_nm.map(self.ip)]
|
696
|
+
ipr = self.conn.hsrv.ipr
|
697
|
+
if ipr and self.uname in ipr:
|
698
|
+
if not ipr[self.uname].map(self.ip):
|
699
|
+
self.log("username [%s] rejected by --ipr" % (self.uname,), 3)
|
700
|
+
self.uname = "*"
|
677
701
|
|
678
702
|
self.rvol = self.asrv.vfs.aread[self.uname]
|
679
703
|
self.wvol = self.asrv.vfs.awrite[self.uname]
|
@@ -695,7 +719,7 @@ class HttpCli(object):
|
|
695
719
|
cookies["b"] = ""
|
696
720
|
|
697
721
|
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False)
|
698
|
-
if "xdev" in vn.flags or "xvol" in vn.flags:
|
722
|
+
if vn.realpath and ("xdev" in vn.flags or "xvol" in vn.flags):
|
699
723
|
ap = vn.canonical(rem)
|
700
724
|
avn = vn.chk_ap(ap)
|
701
725
|
else:
|
@@ -1594,6 +1618,10 @@ class HttpCli(object):
|
|
1594
1618
|
"quota-available-bytes": str(bfree),
|
1595
1619
|
"quota-used-bytes": str(btot - bfree),
|
1596
1620
|
}
|
1621
|
+
if "quotaused" in props: # macos finder crazytalk
|
1622
|
+
df["quotaused"] = df["quota-used-bytes"]
|
1623
|
+
if "quota" in props:
|
1624
|
+
df["quota"] = df["quota-available-bytes"] # idk, makes it happy
|
1597
1625
|
else:
|
1598
1626
|
df = {}
|
1599
1627
|
else:
|
@@ -1975,6 +2003,9 @@ class HttpCli(object):
|
|
1975
2003
|
if "eshare" in self.uparam:
|
1976
2004
|
return self.handle_eshare()
|
1977
2005
|
|
2006
|
+
if "fs_abrt" in self.uparam:
|
2007
|
+
return self.handle_fs_abrt()
|
2008
|
+
|
1978
2009
|
if "application/octet-stream" in ctype:
|
1979
2010
|
return self.handle_post_binary()
|
1980
2011
|
|
@@ -4949,7 +4980,7 @@ class HttpCli(object):
|
|
4949
4980
|
rip = host
|
4950
4981
|
|
4951
4982
|
vp = (self.uparam["hc"] or "").lstrip("/")
|
4952
|
-
pw = self.pw or "hunter2"
|
4983
|
+
pw = self.ouparam.get("pw") or "hunter2"
|
4953
4984
|
if pw in self.asrv.sesa:
|
4954
4985
|
pw = "hunter2"
|
4955
4986
|
|
@@ -5447,6 +5478,10 @@ class HttpCli(object):
|
|
5447
5478
|
and ("*" in x.axs.uwrite or self.uname in x.axs.uwrite or x == shr_dbv)
|
5448
5479
|
]
|
5449
5480
|
|
5481
|
+
q = ""
|
5482
|
+
qp = (0,)
|
5483
|
+
q_c = -1
|
5484
|
+
|
5450
5485
|
for vol in allvols:
|
5451
5486
|
cur = idx.get_cur(vol)
|
5452
5487
|
if not cur:
|
@@ -5454,17 +5489,31 @@ class HttpCli(object):
|
|
5454
5489
|
|
5455
5490
|
nfk, fk_alg = fk_vols.get(vol) or (0, 0)
|
5456
5491
|
|
5492
|
+
zi = vol.flags["unp_who"]
|
5493
|
+
if q_c != zi:
|
5494
|
+
q_c = zi
|
5495
|
+
q = "select sz, rd, fn, at from up where "
|
5496
|
+
if zi == 1:
|
5497
|
+
q += "ip=? and un=?"
|
5498
|
+
qp = (self.ip, self.uname, lim)
|
5499
|
+
elif zi == 2:
|
5500
|
+
q += "ip=?"
|
5501
|
+
qp = (self.ip, lim)
|
5502
|
+
if zi == 3:
|
5503
|
+
q += "un=?"
|
5504
|
+
qp = (self.uname, lim)
|
5505
|
+
q += " and at>? order by at desc"
|
5506
|
+
|
5457
5507
|
n = 2000
|
5458
|
-
|
5459
|
-
for sz, rd, fn, at in cur.execute(q, (self.ip, lim)):
|
5508
|
+
for sz, rd, fn, at in cur.execute(q, qp):
|
5460
5509
|
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
5461
|
-
if nfi == 0 or (nfi == 1 and vfi in vp):
|
5510
|
+
if nfi == 0 or (nfi == 1 and vfi in vp.lower()):
|
5462
5511
|
pass
|
5463
5512
|
elif nfi == 2:
|
5464
|
-
if not vp.startswith(vfi):
|
5513
|
+
if not vp.lower().startswith(vfi):
|
5465
5514
|
continue
|
5466
5515
|
elif nfi == 3:
|
5467
|
-
if not vp.endswith(vfi):
|
5516
|
+
if not vp.lower().endswith(vfi):
|
5468
5517
|
continue
|
5469
5518
|
else:
|
5470
5519
|
continue
|
@@ -5580,16 +5629,16 @@ class HttpCli(object):
|
|
5580
5629
|
continue
|
5581
5630
|
|
5582
5631
|
n = 1000
|
5583
|
-
q = "select sz, rd, fn, ip, at from up where at>0 order by at desc"
|
5584
|
-
for sz, rd, fn, ip, at in cur.execute(q):
|
5632
|
+
q = "select sz, rd, fn, ip, at, un from up where at>0 order by at desc"
|
5633
|
+
for sz, rd, fn, ip, at, un in cur.execute(q):
|
5585
5634
|
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
5586
|
-
if nfi == 0 or (nfi == 1 and vfi in vp):
|
5635
|
+
if nfi == 0 or (nfi == 1 and vfi in vp.lower()):
|
5587
5636
|
pass
|
5588
5637
|
elif nfi == 2:
|
5589
|
-
if not vp.startswith(vfi):
|
5638
|
+
if not vp.lower().startswith(vfi):
|
5590
5639
|
continue
|
5591
5640
|
elif nfi == 3:
|
5592
|
-
if not vp.endswith(vfi):
|
5641
|
+
if not vp.lower().endswith(vfi):
|
5593
5642
|
continue
|
5594
5643
|
else:
|
5595
5644
|
continue
|
@@ -5602,6 +5651,7 @@ class HttpCli(object):
|
|
5602
5651
|
"sz": sz,
|
5603
5652
|
"ip": ip,
|
5604
5653
|
"at": at,
|
5654
|
+
"un": un,
|
5605
5655
|
"nfk": nfk,
|
5606
5656
|
"adm": adm,
|
5607
5657
|
}
|
@@ -5646,12 +5696,16 @@ class HttpCli(object):
|
|
5646
5696
|
adm = rv.pop("adm")
|
5647
5697
|
if not adm:
|
5648
5698
|
rv["ip"] = "(You)" if rv["ip"] == self.ip else "(?)"
|
5699
|
+
if rv["un"] not in ("*", self.uname):
|
5700
|
+
rv["un"] = "(?)"
|
5649
5701
|
else:
|
5650
5702
|
for rv in ret:
|
5651
5703
|
adm = rv.pop("adm")
|
5652
5704
|
if not adm:
|
5653
5705
|
rv["ip"] = "(You)" if rv["ip"] == self.ip else "(?)"
|
5654
5706
|
rv["at"] = 0
|
5707
|
+
if rv["un"] not in ("*", self.uname):
|
5708
|
+
rv["un"] = "(?)"
|
5655
5709
|
|
5656
5710
|
if self.is_vproxied:
|
5657
5711
|
for v in ret:
|
@@ -5930,7 +5984,9 @@ class HttpCli(object):
|
|
5930
5984
|
self.asrv.vfs.get(vdst, self.uname, False, True, False, True)
|
5931
5985
|
wunlink(self.log, dabs, dvn.flags)
|
5932
5986
|
|
5933
|
-
x = self.conn.hsrv.broker.ask(
|
5987
|
+
x = self.conn.hsrv.broker.ask(
|
5988
|
+
"up2k.handle_mv", self.ouparam.get("akey"), self.uname, self.ip, vsrc, vdst
|
5989
|
+
)
|
5934
5990
|
self.loud_reply(x.get(), status=201)
|
5935
5991
|
return True
|
5936
5992
|
|
@@ -5960,10 +6016,21 @@ class HttpCli(object):
|
|
5960
6016
|
self.asrv.vfs.get(vdst, self.uname, False, True, False, True)
|
5961
6017
|
wunlink(self.log, dabs, dvn.flags)
|
5962
6018
|
|
5963
|
-
x = self.conn.hsrv.broker.ask(
|
6019
|
+
x = self.conn.hsrv.broker.ask(
|
6020
|
+
"up2k.handle_cp", self.ouparam.get("akey"), self.uname, self.ip, vsrc, vdst
|
6021
|
+
)
|
5964
6022
|
self.loud_reply(x.get(), status=201)
|
5965
6023
|
return True
|
5966
6024
|
|
6025
|
+
def handle_fs_abrt(self):
|
6026
|
+
if self.args.no_fs_abrt:
|
6027
|
+
t = "aborting an ongoing copy/move is disabled in server config"
|
6028
|
+
raise Pebkac(403, t)
|
6029
|
+
|
6030
|
+
self.conn.hsrv.broker.say("up2k.handle_fs_abrt", self.uparam["fs_abrt"])
|
6031
|
+
self.loud_reply("aborting", status=200)
|
6032
|
+
return True
|
6033
|
+
|
5967
6034
|
def tx_ls(self, ls ) :
|
5968
6035
|
dirs = ls["dirs"]
|
5969
6036
|
files = ls["files"]
|
@@ -6559,13 +6626,15 @@ class HttpCli(object):
|
|
6559
6626
|
tags = {k: v for k, v in r}
|
6560
6627
|
|
6561
6628
|
if is_admin:
|
6562
|
-
q = "select ip, at from up where rd=? and fn=?"
|
6629
|
+
q = "select ip, at, un from up where rd=? and fn=?"
|
6563
6630
|
try:
|
6564
|
-
zs1, zs2 = icur.execute(q, erd_efn).fetchone()
|
6631
|
+
zs1, zs2, zs3 = icur.execute(q, erd_efn).fetchone()
|
6565
6632
|
if zs1:
|
6566
6633
|
tags["up_ip"] = zs1
|
6567
6634
|
if zs2:
|
6568
6635
|
tags[".up_at"] = zs2
|
6636
|
+
if zs3:
|
6637
|
+
tags["up_by"] = zs3
|
6569
6638
|
except:
|
6570
6639
|
pass
|
6571
6640
|
elif add_up_at:
|
@@ -6586,7 +6655,7 @@ class HttpCli(object):
|
|
6586
6655
|
|
6587
6656
|
lmte = list(mte)
|
6588
6657
|
if self.can_admin:
|
6589
|
-
lmte.extend(("up_ip", ".up_at"))
|
6658
|
+
lmte.extend(("up_by", "up_ip", ".up_at"))
|
6590
6659
|
|
6591
6660
|
if "nodirsz" not in vf:
|
6592
6661
|
tagset.add(".files")
|
copyparty/httpsrv.py
CHANGED
@@ -70,6 +70,7 @@ from .util import (
|
|
70
70
|
build_netmap,
|
71
71
|
has_resource,
|
72
72
|
ipnorm,
|
73
|
+
load_ipr,
|
73
74
|
load_ipu,
|
74
75
|
load_resource,
|
75
76
|
min_ex,
|
@@ -189,6 +190,11 @@ class HttpSrv(object):
|
|
189
190
|
else:
|
190
191
|
self.ipu_iu = self.ipu_nm = None
|
191
192
|
|
193
|
+
if self.args.ipr:
|
194
|
+
self.ipr = load_ipr(self.log, self.args.ipr)
|
195
|
+
else:
|
196
|
+
self.ipr = None
|
197
|
+
|
192
198
|
self.ipa_nm = build_netmap(self.args.ipa)
|
193
199
|
self.xff_nm = build_netmap(self.args.xff_src)
|
194
200
|
self.xff_lan = build_netmap("lan")
|
copyparty/mdns.py
CHANGED
@@ -72,7 +72,8 @@ class MDNS(MCast):
|
|
72
72
|
if not self.args.zm_nwa_1:
|
73
73
|
set_avahi_379()
|
74
74
|
|
75
|
-
zs = self.args.name + ".local
|
75
|
+
zs = self.args.zm_fqdn or (self.args.name + ".local")
|
76
|
+
zs = zs.replace("--name", self.args.name).rstrip(".") + "."
|
76
77
|
zs = zs.encode("ascii", "replace").decode("ascii", "replace")
|
77
78
|
self.hn = "-".join(x for x in zs.split("?") if x) or (
|
78
79
|
"vault-{}".format(random.randint(1, 255))
|
copyparty/mtag.py
CHANGED
@@ -170,6 +170,9 @@ def au_unpk(
|
|
170
170
|
raise Exception("no images inside cbz")
|
171
171
|
fi = zf.open(using)
|
172
172
|
|
173
|
+
elif pk == "epub":
|
174
|
+
fi = get_cover_from_epub(log, abspath)
|
175
|
+
|
173
176
|
else:
|
174
177
|
raise Exception("unknown compression %s" % (pk,))
|
175
178
|
|
@@ -199,7 +202,7 @@ def au_unpk(
|
|
199
202
|
|
200
203
|
def ffprobe(
|
201
204
|
abspath , timeout = 60
|
202
|
-
)
|
205
|
+
) :
|
203
206
|
cmd = [
|
204
207
|
b"ffprobe",
|
205
208
|
b"-hide_banner",
|
@@ -213,8 +216,17 @@ def ffprobe(
|
|
213
216
|
return parse_ffprobe(so)
|
214
217
|
|
215
218
|
|
216
|
-
def parse_ffprobe(
|
217
|
-
|
219
|
+
def parse_ffprobe(
|
220
|
+
txt ,
|
221
|
+
) :
|
222
|
+
"""
|
223
|
+
txt: output from ffprobe -show_format -show_streams
|
224
|
+
returns:
|
225
|
+
* normalized tags
|
226
|
+
* original/raw tags
|
227
|
+
* list of streams
|
228
|
+
* format props
|
229
|
+
"""
|
218
230
|
streams = []
|
219
231
|
fmt = {}
|
220
232
|
g = {}
|
@@ -307,7 +319,7 @@ def parse_ffprobe(txt ) :
|
|
307
319
|
ret[rk] = v1
|
308
320
|
|
309
321
|
if ret.get("vc") == "ansi": # shellscript
|
310
|
-
return {}, {}
|
322
|
+
return {}, {}, [], {}
|
311
323
|
|
312
324
|
for strm in streams:
|
313
325
|
for sk, sv in strm.items():
|
@@ -356,7 +368,77 @@ def parse_ffprobe(txt ) :
|
|
356
368
|
zero = int("0")
|
357
369
|
zd = {k: (zero, v) for k, v in ret.items()}
|
358
370
|
|
359
|
-
return zd, md
|
371
|
+
return zd, md, streams, fmt
|
372
|
+
|
373
|
+
|
374
|
+
def get_cover_from_epub(log , abspath ) :
|
375
|
+
import zipfile
|
376
|
+
|
377
|
+
from .dxml import parse_xml
|
378
|
+
|
379
|
+
try:
|
380
|
+
from urlparse import urljoin # Python2
|
381
|
+
except ImportError:
|
382
|
+
from urllib.parse import urljoin # Python3
|
383
|
+
|
384
|
+
with zipfile.ZipFile(abspath, "r") as z:
|
385
|
+
# First open the container file to find the package document (.opf file)
|
386
|
+
try:
|
387
|
+
container_root = parse_xml(z.read("META-INF/container.xml").decode())
|
388
|
+
except KeyError:
|
389
|
+
log("epub: no container file found in %s" % (abspath,))
|
390
|
+
return None
|
391
|
+
|
392
|
+
# https://www.w3.org/TR/epub-33/#sec-container.xml-rootfile-elem
|
393
|
+
container_ns = {"": "urn:oasis:names:tc:opendocument:xmlns:container"}
|
394
|
+
# One file could contain multiple package documents, default to the first one
|
395
|
+
rootfile_path = container_root.find("./rootfiles/rootfile", container_ns).get(
|
396
|
+
"full-path"
|
397
|
+
)
|
398
|
+
|
399
|
+
# Then open the first package document to find the path of the cover image
|
400
|
+
try:
|
401
|
+
package_root = parse_xml(z.read(rootfile_path).decode())
|
402
|
+
except KeyError:
|
403
|
+
log("epub: no package document found in %s" % (abspath,))
|
404
|
+
return None
|
405
|
+
|
406
|
+
# https://www.w3.org/TR/epub-33/#sec-package-doc
|
407
|
+
package_ns = {"": "http://www.idpf.org/2007/opf"}
|
408
|
+
# https://www.w3.org/TR/epub-33/#sec-cover-image
|
409
|
+
coverimage_path_node = package_root.find(
|
410
|
+
"./manifest/item[@properties='cover-image']", package_ns
|
411
|
+
)
|
412
|
+
if coverimage_path_node is not None:
|
413
|
+
coverimage_path = coverimage_path_node.get("href")
|
414
|
+
else:
|
415
|
+
# This might be an EPUB2 file, try the legacy way of specifying covers
|
416
|
+
coverimage_path = _get_cover_from_epub2(log, package_root, package_ns)
|
417
|
+
|
418
|
+
# This url is either absolute (in the .epub) or relative to the package document
|
419
|
+
adjusted_cover_path = urljoin(rootfile_path, coverimage_path)
|
420
|
+
|
421
|
+
return z.open(adjusted_cover_path)
|
422
|
+
|
423
|
+
|
424
|
+
def _get_cover_from_epub2(
|
425
|
+
log , package_root, package_ns
|
426
|
+
) :
|
427
|
+
# <meta name="cover" content="id-to-cover-image"> in <metadata>, then
|
428
|
+
# <item> in <manifest>
|
429
|
+
cover_id = package_root.find("./metadata/meta[@name='cover']", package_ns).get(
|
430
|
+
"content"
|
431
|
+
)
|
432
|
+
|
433
|
+
if not cover_id:
|
434
|
+
return None
|
435
|
+
|
436
|
+
for node in package_root.iterfind("./manifest/item", package_ns):
|
437
|
+
if node.get("id") == cover_id:
|
438
|
+
cover_path = node.get("href")
|
439
|
+
return cover_path
|
440
|
+
|
441
|
+
return None
|
360
442
|
|
361
443
|
|
362
444
|
class MTag(object):
|
@@ -627,7 +709,7 @@ class MTag(object):
|
|
627
709
|
if not bos.path.isfile(abspath):
|
628
710
|
return {}
|
629
711
|
|
630
|
-
ret, md = ffprobe(abspath, self.args.mtag_to)
|
712
|
+
ret, md, _, _ = ffprobe(abspath, self.args.mtag_to)
|
631
713
|
|
632
714
|
if self.args.mtag_vv:
|
633
715
|
for zd in (ret, dict(md)):
|
copyparty/svchub.py
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
4
|
import argparse
|
5
|
+
import atexit
|
5
6
|
import errno
|
6
7
|
import logging
|
7
8
|
import os
|
@@ -32,6 +33,7 @@ from .th_srv import (
|
|
32
33
|
HAVE_FFPROBE,
|
33
34
|
HAVE_HEIF,
|
34
35
|
HAVE_PIL,
|
36
|
+
HAVE_RAW,
|
35
37
|
HAVE_VIPS,
|
36
38
|
HAVE_WEBP,
|
37
39
|
ThumbSrv,
|
@@ -58,6 +60,7 @@ from .util import (
|
|
58
60
|
build_netmap,
|
59
61
|
expat_ver,
|
60
62
|
gzip,
|
63
|
+
load_ipr,
|
61
64
|
load_ipu,
|
62
65
|
lock_file,
|
63
66
|
min_ex,
|
@@ -66,6 +69,7 @@ from .util import (
|
|
66
69
|
pybin,
|
67
70
|
start_log_thrs,
|
68
71
|
start_stackmon,
|
72
|
+
termsize,
|
69
73
|
ub64enc,
|
70
74
|
)
|
71
75
|
|
@@ -146,6 +150,7 @@ class SvcHub(object):
|
|
146
150
|
args.no_del = True
|
147
151
|
args.no_mv = True
|
148
152
|
args.hardlink = True
|
153
|
+
args.dav_auth = True
|
149
154
|
args.vague_403 = True
|
150
155
|
args.nih = True
|
151
156
|
|
@@ -234,7 +239,7 @@ class SvcHub(object):
|
|
234
239
|
t = "WARNING: --th-ram-max is very small (%.2f GiB); will not be able to %s"
|
235
240
|
self.log("root", t % (args.th_ram_max, zs), 3)
|
236
241
|
|
237
|
-
if args.chpw and args.
|
242
|
+
if args.chpw and args.have_idp_hdrs:
|
238
243
|
t = "ERROR: user-changeable passwords is incompatible with IdP/identity-providers; you must disable either --chpw or --idp-h-usr"
|
239
244
|
self.log("root", t, 1)
|
240
245
|
raise Exception(t)
|
@@ -250,6 +255,10 @@ class SvcHub(object):
|
|
250
255
|
setattr(args, "ipu_iu", iu)
|
251
256
|
setattr(args, "ipu_nm", nm)
|
252
257
|
|
258
|
+
if args.ipr:
|
259
|
+
ipr = load_ipr(self.log, args.ipr, True)
|
260
|
+
setattr(args, "ipr_u", ipr)
|
261
|
+
|
253
262
|
for zs in "ah_salt fk_salt dk_salt".split():
|
254
263
|
if getattr(args, "show_%s" % (zs,)):
|
255
264
|
self.log("root", "effective %s is %s" % (zs, getattr(args, zs)))
|
@@ -259,7 +268,7 @@ class SvcHub(object):
|
|
259
268
|
args.no_ses = True
|
260
269
|
args.shr = ""
|
261
270
|
|
262
|
-
if args.idp_store and args.
|
271
|
+
if args.idp_store and args.have_idp_hdrs:
|
263
272
|
self.setup_db("idp")
|
264
273
|
|
265
274
|
if not self.args.no_ses:
|
@@ -315,6 +324,8 @@ class SvcHub(object):
|
|
315
324
|
decs.pop("vips", None)
|
316
325
|
if not HAVE_PIL:
|
317
326
|
decs.pop("pil", None)
|
327
|
+
if not HAVE_RAW:
|
328
|
+
decs.pop("raw", None)
|
318
329
|
if not HAVE_FFMPEG or not HAVE_FFPROBE:
|
319
330
|
decs.pop("ff", None)
|
320
331
|
|
@@ -421,6 +432,9 @@ class SvcHub(object):
|
|
421
432
|
getattr(args, zs).mutex = threading.Lock()
|
422
433
|
except:
|
423
434
|
pass
|
435
|
+
if args.ipr:
|
436
|
+
for nm in args.ipr_u.values():
|
437
|
+
nm.mutex = threading.Lock()
|
424
438
|
|
425
439
|
def _db_onfail_ses(self) :
|
426
440
|
self.args.no_ses = True
|
@@ -762,6 +776,39 @@ class SvcHub(object):
|
|
762
776
|
def sigterm(self) :
|
763
777
|
self.signal_handler(signal.SIGTERM, None)
|
764
778
|
|
779
|
+
def sticky_qr(self) :
|
780
|
+
tw, th = termsize()
|
781
|
+
zs1, qr = self.tcpsrv.qr.split("\n", 1)
|
782
|
+
url, colr = zs1.split(" ", 1)
|
783
|
+
nl = len(qr.split("\n")) # numlines
|
784
|
+
lp = 3 if nl * 2 + 4 < tw else 0 # leftpad
|
785
|
+
lp0 = lp
|
786
|
+
if self.args.qr_pin == 2:
|
787
|
+
url = ""
|
788
|
+
else:
|
789
|
+
while lp and (nl + lp) * 2 + len(url) + 1 > tw:
|
790
|
+
lp -= 1
|
791
|
+
if (nl + lp) * 2 + len(url) + 1 > tw:
|
792
|
+
qr = url + "\n" + qr
|
793
|
+
url = ""
|
794
|
+
nl += 1
|
795
|
+
lp = lp0
|
796
|
+
sh = 1 + th - nl
|
797
|
+
if lp:
|
798
|
+
zs = " " * lp
|
799
|
+
qr = zs + qr.replace("\n", "\n" + zs)
|
800
|
+
if url:
|
801
|
+
url = "%s\033[%d;%dH%s\033[0m" % (colr, sh + 1, (nl + lp) * 2, url)
|
802
|
+
qr = colr + qr
|
803
|
+
|
804
|
+
def unlock():
|
805
|
+
print("\033[s\033[r\033[u", file=sys.stderr)
|
806
|
+
|
807
|
+
atexit.register(unlock)
|
808
|
+
t = "%s\033[%dA" % ("\n" * nl, nl)
|
809
|
+
t = "%s\033[s\033[1;%dr\033[%dH%s%s\033[u" % (t, sh - 1, sh, qr, url)
|
810
|
+
self.pr(t, file=sys.stderr)
|
811
|
+
|
765
812
|
def cb_httpsrv_up(self) :
|
766
813
|
self.httpsrv_up += 1
|
767
814
|
if self.httpsrv_up != self.broker.num_workers:
|
@@ -774,7 +821,10 @@ class SvcHub(object):
|
|
774
821
|
break
|
775
822
|
|
776
823
|
if self.tcpsrv.qr:
|
777
|
-
|
824
|
+
if self.args.qr_pin:
|
825
|
+
self.sticky_qr()
|
826
|
+
else:
|
827
|
+
self.log("qr-code", self.tcpsrv.qr)
|
778
828
|
else:
|
779
829
|
self.log("root", "workers OK\n")
|
780
830
|
|
@@ -801,6 +851,7 @@ class SvcHub(object):
|
|
801
851
|
(HAVE_ZMQ, "pyzmq", "send zeromq messages from event-hooks"),
|
802
852
|
(HAVE_HEIF, "pillow-heif", "read .heif images with pillow (rarely useful)"),
|
803
853
|
(HAVE_AVIF, "pillow-avif", "read .avif images with pillow (rarely useful)"),
|
854
|
+
(HAVE_RAW, "rawpy", "read RAW images"),
|
804
855
|
]
|
805
856
|
if ANYWIN:
|
806
857
|
to_check += [
|
@@ -959,10 +1010,23 @@ class SvcHub(object):
|
|
959
1010
|
al.sus_urls = None
|
960
1011
|
|
961
1012
|
al.xff_hdr = al.xff_hdr.lower()
|
962
|
-
al.idp_h_usr =
|
1013
|
+
al.idp_h_usr = [x.lower() for x in al.idp_h_usr or []]
|
963
1014
|
al.idp_h_grp = al.idp_h_grp.lower()
|
964
1015
|
al.idp_h_key = al.idp_h_key.lower()
|
965
1016
|
|
1017
|
+
al.idp_hm_usr_p = {}
|
1018
|
+
for zs0 in al.idp_hm_usr or []:
|
1019
|
+
try:
|
1020
|
+
sep = zs0[:1]
|
1021
|
+
hn, zs1, zs2 = zs0[1:].split(sep)
|
1022
|
+
hn = hn.lower()
|
1023
|
+
if hn in al.idp_hm_usr_p:
|
1024
|
+
al.idp_hm_usr_p[hn][zs1] = zs2
|
1025
|
+
else:
|
1026
|
+
al.idp_hm_usr_p[hn] = {zs1: zs2}
|
1027
|
+
except:
|
1028
|
+
raise Exception("invalid --idp-hm-usr [%s]" % (zs0,))
|
1029
|
+
|
966
1030
|
al.ftp_ipa_nm = build_netmap(al.ftp_ipa or al.ipa, True)
|
967
1031
|
al.tftp_ipa_nm = build_netmap(al.tftp_ipa or al.ipa, True)
|
968
1032
|
|
@@ -1390,7 +1454,14 @@ class SvcHub(object):
|
|
1390
1454
|
|
1391
1455
|
fmt = "\033[36m%s \033[33m%-21s \033[0m%s\n"
|
1392
1456
|
if self.no_ansi:
|
1393
|
-
|
1457
|
+
if c == 1:
|
1458
|
+
fmt = "%s %-21s CRIT: %s\n"
|
1459
|
+
elif c == 3:
|
1460
|
+
fmt = "%s %-21s WARN: %s\n"
|
1461
|
+
elif c == 6:
|
1462
|
+
fmt = "%s %-21s BTW: %s\n"
|
1463
|
+
else:
|
1464
|
+
fmt = "%s %-21s LOG: %s\n"
|
1394
1465
|
if "\033" in msg:
|
1395
1466
|
msg = RE_ANSI.sub("", msg)
|
1396
1467
|
if "\033" in src:
|
copyparty/tcpsrv.py
CHANGED
@@ -611,6 +611,10 @@ class TcpSrv(object):
|
|
611
611
|
|
612
612
|
fg = self.args.qr_fg
|
613
613
|
bg = self.args.qr_bg
|
614
|
+
nocolor = fg == -1
|
615
|
+
if nocolor:
|
616
|
+
fg = 0
|
617
|
+
|
614
618
|
pad = self.args.qrp
|
615
619
|
zoom = self.args.qrz
|
616
620
|
qrc = QrCode.encode_binary(btxt)
|
@@ -638,6 +642,8 @@ class TcpSrv(object):
|
|
638
642
|
|
639
643
|
qr = qr.replace("\n", "\033[K\n") + "\033[K" # win10do
|
640
644
|
cc = " \033[0;38;5;{0};47;48;5;{1}m" if fg else " \033[0;30;47m"
|
645
|
+
if nocolor:
|
646
|
+
cc = " \033[0m"
|
641
647
|
t = cc + "\n{2}\033[999G\033[0m\033[J"
|
642
648
|
t = t.format(fg, bg, qr)
|
643
649
|
if ANYWIN:
|
copyparty/th_cli.py
CHANGED
@@ -33,11 +33,15 @@ class ThumbCli(object):
|
|
33
33
|
if not c:
|
34
34
|
raise Exception()
|
35
35
|
except:
|
36
|
-
c = {
|
36
|
+
c = {
|
37
|
+
k: set()
|
38
|
+
for k in ["thumbable", "pil", "vips", "raw", "ffi", "ffv", "ffa"]
|
39
|
+
}
|
37
40
|
|
38
41
|
self.thumbable = c["thumbable"]
|
39
42
|
self.fmt_pil = c["pil"]
|
40
43
|
self.fmt_vips = c["vips"]
|
44
|
+
self.fmt_raw = c["raw"]
|
41
45
|
self.fmt_ffi = c["ffi"]
|
42
46
|
self.fmt_ffv = c["ffv"]
|
43
47
|
self.fmt_ffa = c["ffa"]
|