copyparty 1.15.10__py3-none-any.whl → 1.16.1__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/__init__.py +1 -0
- copyparty/__main__.py +17 -6
- copyparty/__version__.py +3 -3
- copyparty/authsrv.py +47 -21
- copyparty/broker_mp.py +40 -5
- copyparty/broker_mpw.py +20 -19
- copyparty/broker_thr.py +2 -2
- copyparty/cfg.py +4 -0
- copyparty/ftpd.py +1 -0
- copyparty/httpcli.py +159 -9
- copyparty/httpsrv.py +39 -0
- copyparty/metrics.py +3 -0
- copyparty/mtag.py +29 -0
- copyparty/pwhash.py +10 -8
- copyparty/svchub.py +16 -30
- copyparty/szip.py +1 -1
- copyparty/tcpsrv.py +2 -2
- copyparty/tftpd.py +1 -0
- copyparty/th_srv.py +10 -3
- copyparty/up2k.py +333 -64
- copyparty/util.py +45 -5
- copyparty/web/a/partyfuse.py +2 -1
- copyparty/web/a/u2c.py +20 -5
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/shares.js.gz +0 -0
- copyparty/web/splash.css.gz +0 -0
- copyparty/web/splash.html +12 -0
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/ui.css.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/METADATA +8 -4
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/RECORD +38 -38
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/WHEEL +1 -1
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/LICENSE +0 -0
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/entry_points.txt +0 -0
- {copyparty-1.15.10.dist-info → copyparty-1.16.1.dist-info}/top_level.txt +0 -0
copyparty/httpcli.py
CHANGED
@@ -181,6 +181,7 @@ class HttpCli(object):
|
|
181
181
|
self.rem = " "
|
182
182
|
self.vpath = " "
|
183
183
|
self.vpaths = " "
|
184
|
+
self.dl_id = ""
|
184
185
|
self.gctx = " " # additional context for garda
|
185
186
|
self.trailing_slash = True
|
186
187
|
self.uname = " "
|
@@ -632,7 +633,7 @@ class HttpCli(object):
|
|
632
633
|
avn.can_access("", self.uname) if avn else [False] * 8
|
633
634
|
)
|
634
635
|
self.avn = avn
|
635
|
-
self.vn = vn
|
636
|
+
self.vn = vn # note: do not dbv due to walk/zipgen
|
636
637
|
self.rem = rem
|
637
638
|
|
638
639
|
self.s.settimeout(self.args.s_tbody or None)
|
@@ -721,6 +722,11 @@ class HttpCli(object):
|
|
721
722
|
except Pebkac:
|
722
723
|
return False
|
723
724
|
|
725
|
+
finally:
|
726
|
+
if self.dl_id:
|
727
|
+
self.conn.hsrv.dli.pop(self.dl_id, None)
|
728
|
+
self.conn.hsrv.dls.pop(self.dl_id, None)
|
729
|
+
|
724
730
|
def dip(self) :
|
725
731
|
if self.args.plain_ip:
|
726
732
|
return self.ip.replace(":", ".")
|
@@ -1191,6 +1197,9 @@ class HttpCli(object):
|
|
1191
1197
|
if "move" in self.uparam:
|
1192
1198
|
return self.handle_mv()
|
1193
1199
|
|
1200
|
+
if "copy" in self.uparam:
|
1201
|
+
return self.handle_cp()
|
1202
|
+
|
1194
1203
|
if not self.vpath and self.ouparam:
|
1195
1204
|
if "reload" in self.uparam:
|
1196
1205
|
return self.handle_reload()
|
@@ -1210,6 +1219,9 @@ class HttpCli(object):
|
|
1210
1219
|
if "shares" in self.uparam:
|
1211
1220
|
return self.tx_shares()
|
1212
1221
|
|
1222
|
+
if "dls" in self.uparam:
|
1223
|
+
return self.tx_dls()
|
1224
|
+
|
1213
1225
|
if "h" in self.uparam:
|
1214
1226
|
return self.tx_mounts()
|
1215
1227
|
|
@@ -1445,6 +1457,7 @@ class HttpCli(object):
|
|
1445
1457
|
not self.args.no_scandir,
|
1446
1458
|
[[True, False]],
|
1447
1459
|
lstat="davrt" not in vn.flags,
|
1460
|
+
throw=True,
|
1448
1461
|
)
|
1449
1462
|
if not self.can_read:
|
1450
1463
|
vfs_ls = []
|
@@ -1782,6 +1795,9 @@ class HttpCli(object):
|
|
1782
1795
|
if "move" in self.uparam:
|
1783
1796
|
return self.handle_mv()
|
1784
1797
|
|
1798
|
+
if "copy" in self.uparam:
|
1799
|
+
return self.handle_cp()
|
1800
|
+
|
1785
1801
|
if "delete" in self.uparam:
|
1786
1802
|
return self.handle_rm([])
|
1787
1803
|
|
@@ -3665,6 +3681,8 @@ class HttpCli(object):
|
|
3665
3681
|
self.args.s_wr_sz,
|
3666
3682
|
self.args.s_wr_slp,
|
3667
3683
|
not self.args.no_poll,
|
3684
|
+
{},
|
3685
|
+
"",
|
3668
3686
|
)
|
3669
3687
|
res.close()
|
3670
3688
|
|
@@ -3879,6 +3897,19 @@ class HttpCli(object):
|
|
3879
3897
|
self.send_headers(length=upper - lower, status=status, mime=mime)
|
3880
3898
|
return True
|
3881
3899
|
|
3900
|
+
dls = self.conn.hsrv.dls
|
3901
|
+
if upper - lower > 0x400000: # 4m
|
3902
|
+
now = time.time()
|
3903
|
+
self.dl_id = "%s:%s" % (self.ip, self.addr[1])
|
3904
|
+
dls[self.dl_id] = (now, 0)
|
3905
|
+
self.conn.hsrv.dli[self.dl_id] = (
|
3906
|
+
now,
|
3907
|
+
upper - lower,
|
3908
|
+
self.vn,
|
3909
|
+
self.vpath,
|
3910
|
+
self.uname,
|
3911
|
+
)
|
3912
|
+
|
3882
3913
|
if ptop is not None:
|
3883
3914
|
return self.tx_pipe(
|
3884
3915
|
ptop, req_path, ap_data, job, lower, upper, status, mime, logmsg
|
@@ -3898,6 +3929,8 @@ class HttpCli(object):
|
|
3898
3929
|
self.args.s_wr_sz,
|
3899
3930
|
self.args.s_wr_slp,
|
3900
3931
|
not self.args.no_poll,
|
3932
|
+
dls,
|
3933
|
+
self.dl_id,
|
3901
3934
|
)
|
3902
3935
|
|
3903
3936
|
if remains > 0:
|
@@ -4048,6 +4081,8 @@ class HttpCli(object):
|
|
4048
4081
|
wr_sz,
|
4049
4082
|
wr_slp,
|
4050
4083
|
not self.args.no_poll,
|
4084
|
+
self.conn.hsrv.dls,
|
4085
|
+
self.dl_id,
|
4051
4086
|
)
|
4052
4087
|
|
4053
4088
|
spd = self._spd((upper - lower) - remains)
|
@@ -4133,6 +4168,18 @@ class HttpCli(object):
|
|
4133
4168
|
self.log("transcoding to [{}]".format(cfmt))
|
4134
4169
|
fgen = gfilter(fgen, self.thumbcli, self.uname, vpath, cfmt)
|
4135
4170
|
|
4171
|
+
now = time.time()
|
4172
|
+
self.dl_id = "%s:%s" % (self.ip, self.addr[1])
|
4173
|
+
self.conn.hsrv.dli[self.dl_id] = (
|
4174
|
+
now,
|
4175
|
+
0,
|
4176
|
+
self.vn,
|
4177
|
+
"%s :%s" % (self.vpath, ext),
|
4178
|
+
self.uname,
|
4179
|
+
)
|
4180
|
+
dls = self.conn.hsrv.dls
|
4181
|
+
dls[self.dl_id] = (time.time(), 0)
|
4182
|
+
|
4136
4183
|
bgen = packer(
|
4137
4184
|
self.log,
|
4138
4185
|
self.asrv,
|
@@ -4141,6 +4188,7 @@ class HttpCli(object):
|
|
4141
4188
|
pre_crc="crc" in uarg,
|
4142
4189
|
cmp=uarg if cancmp or uarg == "pax" else "",
|
4143
4190
|
)
|
4191
|
+
n = 0
|
4144
4192
|
bsent = 0
|
4145
4193
|
for buf in bgen.gen():
|
4146
4194
|
if not buf:
|
@@ -4154,6 +4202,11 @@ class HttpCli(object):
|
|
4154
4202
|
bgen.stop()
|
4155
4203
|
break
|
4156
4204
|
|
4205
|
+
n += 1
|
4206
|
+
if n >= 4:
|
4207
|
+
n = 0
|
4208
|
+
dls[self.dl_id] = (time.time(), bsent)
|
4209
|
+
|
4157
4210
|
spd = self._spd(bsent)
|
4158
4211
|
self.log("{}, {}".format(logmsg, spd))
|
4159
4212
|
return True
|
@@ -4410,6 +4463,32 @@ class HttpCli(object):
|
|
4410
4463
|
}
|
4411
4464
|
|
4412
4465
|
|
4466
|
+
dls = dl_list = []
|
4467
|
+
if self.conn.hsrv.tdls:
|
4468
|
+
zi = self.args.dl_list
|
4469
|
+
if zi == 2 or (zi == 1 and self.avol):
|
4470
|
+
dl_list = self.get_dls()
|
4471
|
+
for t0, t1, sent, sz, vp, dl_id, uname in dl_list:
|
4472
|
+
rem = sz - sent
|
4473
|
+
td = max(0.1, now - t0)
|
4474
|
+
rd, fn = vsplit(vp)
|
4475
|
+
if not rd:
|
4476
|
+
rd = "/"
|
4477
|
+
erd = quotep(rd)
|
4478
|
+
rds = rd.replace("/", " / ")
|
4479
|
+
spd = humansize(sent / td, True) + "/s"
|
4480
|
+
hsent = humansize(sent, True)
|
4481
|
+
idle = s2hms(now - t1, True)
|
4482
|
+
usr = "%s @%s" % (dl_id, uname) if dl_id else uname
|
4483
|
+
if sz and sent and td:
|
4484
|
+
eta = s2hms((sz - sent) / (sent / td), True)
|
4485
|
+
perc = int(100 * sent / sz)
|
4486
|
+
else:
|
4487
|
+
eta = perc = "--"
|
4488
|
+
|
4489
|
+
fn = html_escape(fn) if fn else self.conn.hsrv.iiam
|
4490
|
+
dls.append((perc, hsent, spd, eta, idle, usr, erd, rds, fn))
|
4491
|
+
|
4413
4492
|
fmt = self.uparam.get("ls", "")
|
4414
4493
|
if not fmt and (self.ua.startswith("curl/") or self.ua.startswith("fetch")):
|
4415
4494
|
fmt = "v"
|
@@ -4431,6 +4510,12 @@ class HttpCli(object):
|
|
4431
4510
|
txt += "\n%s" % (", ".join((str(x) for x in zt)),)
|
4432
4511
|
txt += "\n"
|
4433
4512
|
|
4513
|
+
if dls:
|
4514
|
+
txt += "\n\nactive downloads:"
|
4515
|
+
for zt in dls:
|
4516
|
+
txt += "\n%s" % (", ".join((str(x) for x in zt)),)
|
4517
|
+
txt += "\n"
|
4518
|
+
|
4434
4519
|
if rvol:
|
4435
4520
|
txt += "\nyou can browse:"
|
4436
4521
|
for v in rvol:
|
@@ -4454,6 +4539,7 @@ class HttpCli(object):
|
|
4454
4539
|
avol=avol,
|
4455
4540
|
in_shr=self.args.shr and self.vpath.startswith(self.args.shr1),
|
4456
4541
|
vstate=vstate,
|
4542
|
+
dls=dls,
|
4457
4543
|
ups=ups,
|
4458
4544
|
scanning=vs["scanning"],
|
4459
4545
|
hashq=vs["hashq"],
|
@@ -4516,8 +4602,14 @@ class HttpCli(object):
|
|
4516
4602
|
|
4517
4603
|
t = t.format(self.args.SR)
|
4518
4604
|
qv = quotep(self.vpaths) + self.ourlq()
|
4519
|
-
|
4520
|
-
|
4605
|
+
html = self.j2s(
|
4606
|
+
"splash",
|
4607
|
+
this=self,
|
4608
|
+
qvpath=qv,
|
4609
|
+
msg=t,
|
4610
|
+
in_shr=self.args.shr and self.vpath.startswith(self.args.shr1),
|
4611
|
+
ahttps="" if self.is_https else "https://" + self.host + self.req,
|
4612
|
+
)
|
4521
4613
|
self.reply(html.encode("utf-8"), status=rc)
|
4522
4614
|
return True
|
4523
4615
|
|
@@ -4565,7 +4657,7 @@ class HttpCli(object):
|
|
4565
4657
|
if self.args.no_reload:
|
4566
4658
|
raise Pebkac(403, "the reload feature is disabled in server config")
|
4567
4659
|
|
4568
|
-
x = self.conn.hsrv.broker.ask("reload")
|
4660
|
+
x = self.conn.hsrv.broker.ask("reload", True, True)
|
4569
4661
|
return self.redirect("", "?h", x.get(), "return to", False)
|
4570
4662
|
|
4571
4663
|
def tx_stack(self) :
|
@@ -4668,6 +4760,40 @@ class HttpCli(object):
|
|
4668
4760
|
ret["a"] = dirs
|
4669
4761
|
return ret
|
4670
4762
|
|
4763
|
+
def get_dls(self) :
|
4764
|
+
ret = []
|
4765
|
+
dls = self.conn.hsrv.tdls
|
4766
|
+
for dl_id, (t0, sz, vn, vp, uname) in self.conn.hsrv.tdli.items():
|
4767
|
+
t1, sent = dls[dl_id]
|
4768
|
+
if sent > 0x100000: # 1m; buffers 2~4
|
4769
|
+
sent -= 0x100000
|
4770
|
+
if self.uname not in vn.axs.uread:
|
4771
|
+
vp = ""
|
4772
|
+
elif self.uname not in vn.axs.udot and (vp.startswith(".") or "/." in vp):
|
4773
|
+
vp = ""
|
4774
|
+
if self.uname not in vn.axs.uadmin:
|
4775
|
+
dl_id = uname = ""
|
4776
|
+
|
4777
|
+
ret.append([t0, t1, sent, sz, vp, dl_id, uname])
|
4778
|
+
return ret
|
4779
|
+
|
4780
|
+
def tx_dls(self) :
|
4781
|
+
ret = [
|
4782
|
+
{
|
4783
|
+
"t0": x[0],
|
4784
|
+
"t1": x[1],
|
4785
|
+
"sent": x[2],
|
4786
|
+
"size": x[3],
|
4787
|
+
"path": x[4],
|
4788
|
+
"conn": x[5],
|
4789
|
+
"uname": x[6],
|
4790
|
+
}
|
4791
|
+
for x in self.get_dls()
|
4792
|
+
]
|
4793
|
+
zs = json.dumps(ret, separators=(",\n", ": "))
|
4794
|
+
self.reply(zs.encode("utf-8", "replace"), mime="application/json")
|
4795
|
+
return True
|
4796
|
+
|
4671
4797
|
def tx_ups(self) :
|
4672
4798
|
idx = self.conn.get_u2idx()
|
4673
4799
|
if not idx or not hasattr(idx, "p_end"):
|
@@ -4852,7 +4978,7 @@ class HttpCli(object):
|
|
4852
4978
|
|
4853
4979
|
cur.connection.commit()
|
4854
4980
|
if reload:
|
4855
|
-
self.conn.hsrv.broker.ask("
|
4981
|
+
self.conn.hsrv.broker.ask("reload", False, False).get()
|
4856
4982
|
self.conn.hsrv.broker.ask("up2k.wake_rescanner").get()
|
4857
4983
|
|
4858
4984
|
self.redirect(self.args.SRS + "?shares")
|
@@ -4943,7 +5069,7 @@ class HttpCli(object):
|
|
4943
5069
|
cur.execute(q, (skey, fn))
|
4944
5070
|
|
4945
5071
|
cur.connection.commit()
|
4946
|
-
self.conn.hsrv.broker.ask("
|
5072
|
+
self.conn.hsrv.broker.ask("reload", False, False).get()
|
4947
5073
|
self.conn.hsrv.broker.ask("up2k.wake_rescanner").get()
|
4948
5074
|
|
4949
5075
|
fn = quotep(fns[0]) if len(fns) == 1 else ""
|
@@ -4994,16 +5120,39 @@ class HttpCli(object):
|
|
4994
5120
|
return self._mv(self.vpath, dst.lstrip("/"))
|
4995
5121
|
|
4996
5122
|
def _mv(self, vsrc , vdst ) :
|
4997
|
-
if not self.can_move:
|
4998
|
-
raise Pebkac(403, "not allowed for user " + self.uname)
|
4999
|
-
|
5000
5123
|
if self.args.no_mv:
|
5001
5124
|
raise Pebkac(403, "the rename/move feature is disabled in server config")
|
5002
5125
|
|
5126
|
+
self.asrv.vfs.get(vsrc, self.uname, True, False, True)
|
5127
|
+
self.asrv.vfs.get(vdst, self.uname, False, True)
|
5128
|
+
|
5003
5129
|
x = self.conn.hsrv.broker.ask("up2k.handle_mv", self.uname, self.ip, vsrc, vdst)
|
5004
5130
|
self.loud_reply(x.get(), status=201)
|
5005
5131
|
return True
|
5006
5132
|
|
5133
|
+
def handle_cp(self) :
|
5134
|
+
# full path of new loc (incl filename)
|
5135
|
+
dst = self.uparam.get("copy")
|
5136
|
+
|
5137
|
+
if self.is_vproxied and dst and dst.startswith(self.args.SR):
|
5138
|
+
dst = dst[len(self.args.RS) :]
|
5139
|
+
|
5140
|
+
if not dst:
|
5141
|
+
raise Pebkac(400, "need dst vpath")
|
5142
|
+
|
5143
|
+
return self._cp(self.vpath, dst.lstrip("/"))
|
5144
|
+
|
5145
|
+
def _cp(self, vsrc , vdst ) :
|
5146
|
+
if self.args.no_cp:
|
5147
|
+
raise Pebkac(403, "the copy feature is disabled in server config")
|
5148
|
+
|
5149
|
+
self.asrv.vfs.get(vsrc, self.uname, True, False)
|
5150
|
+
self.asrv.vfs.get(vdst, self.uname, False, True)
|
5151
|
+
|
5152
|
+
x = self.conn.hsrv.broker.ask("up2k.handle_cp", self.uname, self.ip, vsrc, vdst)
|
5153
|
+
self.loud_reply(x.get(), status=201)
|
5154
|
+
return True
|
5155
|
+
|
5007
5156
|
def tx_ls(self, ls ) :
|
5008
5157
|
dirs = ls["dirs"]
|
5009
5158
|
files = ls["files"]
|
@@ -5433,6 +5582,7 @@ class HttpCli(object):
|
|
5433
5582
|
not self.args.no_scandir,
|
5434
5583
|
[[True, False], [False, True]],
|
5435
5584
|
lstat="lt" in self.uparam,
|
5585
|
+
throw=True,
|
5436
5586
|
)
|
5437
5587
|
stats = {k: v for k, v in vfs_ls}
|
5438
5588
|
ls_names = [x[0] for x in vfs_ls]
|
copyparty/httpsrv.py
CHANGED
@@ -81,6 +81,7 @@ from .util import (
|
|
81
81
|
)
|
82
82
|
|
83
83
|
if TYPE_CHECKING:
|
84
|
+
from .authsrv import VFS
|
84
85
|
from .broker_util import BrokerCli
|
85
86
|
from .ssdp import SSDPr
|
86
87
|
|
@@ -127,6 +128,12 @@ class HttpSrv(object):
|
|
127
128
|
self.bans = {}
|
128
129
|
self.aclose = {}
|
129
130
|
|
131
|
+
dli = {} # info
|
132
|
+
dls = {} # state
|
133
|
+
self.dli = self.tdli = dli
|
134
|
+
self.dls = self.tdls = dls
|
135
|
+
self.iiam = '<img src="%s.cpr/iiam.gif" />' % (self.args.SRS,)
|
136
|
+
|
130
137
|
self.bound = set()
|
131
138
|
self.name = "hsrv" + nsuf
|
132
139
|
self.mutex = threading.Lock()
|
@@ -201,6 +208,9 @@ class HttpSrv(object):
|
|
201
208
|
self.start_threads(4)
|
202
209
|
|
203
210
|
if nid:
|
211
|
+
self.tdli = {}
|
212
|
+
self.tdls = {}
|
213
|
+
|
204
214
|
if self.args.stackmon:
|
205
215
|
start_stackmon(self.args.stackmon, nid)
|
206
216
|
|
@@ -573,3 +583,32 @@ class HttpSrv(object):
|
|
573
583
|
ident += "a"
|
574
584
|
|
575
585
|
self.u2idx_free[ident] = u2idx
|
586
|
+
|
587
|
+
def read_dls(
|
588
|
+
self,
|
589
|
+
):
|
590
|
+
|
591
|
+
|
592
|
+
"""
|
593
|
+
mp-broker asking for local dl-info + dl-state;
|
594
|
+
reduce overhead by sending just the vfs vpath
|
595
|
+
"""
|
596
|
+
dli = {k: (a, b, c.vpath, d, e) for k, (a, b, c, d, e) in self.dli.items()}
|
597
|
+
return (dli, self.dls)
|
598
|
+
|
599
|
+
def write_dls(
|
600
|
+
self,
|
601
|
+
sdli ,
|
602
|
+
dls ,
|
603
|
+
) :
|
604
|
+
"""
|
605
|
+
mp-broker pushing total dl-info + dl-state;
|
606
|
+
swap out the vfs vpath with the vfs node
|
607
|
+
"""
|
608
|
+
dli = {}
|
609
|
+
for k, (a, b, c, d, e) in sdli.items():
|
610
|
+
vn = self.asrv.vfs.all_nodes[c]
|
611
|
+
dli[k] = (a, b, vn, d, e)
|
612
|
+
|
613
|
+
self.tdli = dli
|
614
|
+
self.tdls = dls
|
copyparty/metrics.py
CHANGED
@@ -72,6 +72,9 @@ class Metrics(object):
|
|
72
72
|
v = "{:.3f}".format(self.hsrv.t0)
|
73
73
|
addug("cpp_boot_unixtime", "seconds", v, t)
|
74
74
|
|
75
|
+
t = "number of active downloads"
|
76
|
+
addg("cpp_active_dl", str(len(self.hsrv.tdls)), t)
|
77
|
+
|
75
78
|
t = "number of open http(s) client connections"
|
76
79
|
addg("cpp_http_conns", str(self.hsrv.ncli), t)
|
77
80
|
|
copyparty/mtag.py
CHANGED
@@ -4,6 +4,7 @@ from __future__ import print_function, unicode_literals
|
|
4
4
|
import argparse
|
5
5
|
import json
|
6
6
|
import os
|
7
|
+
import re
|
7
8
|
import shutil
|
8
9
|
import subprocess as sp
|
9
10
|
import sys
|
@@ -56,6 +57,9 @@ def have_ff(scmd ) :
|
|
56
57
|
HAVE_FFMPEG = not os.environ.get("PRTY_NO_FFMPEG") and have_ff("ffmpeg")
|
57
58
|
HAVE_FFPROBE = not os.environ.get("PRTY_NO_FFPROBE") and have_ff("ffprobe")
|
58
59
|
|
60
|
+
CBZ_PICS = set("png jpg jpeg gif bmp tga tif tiff webp avif".split())
|
61
|
+
CBZ_01 = re.compile(r"(^|[^0-9v])0+[01]\b")
|
62
|
+
|
59
63
|
|
60
64
|
class MParser(object):
|
61
65
|
def __init__(self, cmdline ) :
|
@@ -120,6 +124,7 @@ def au_unpk(
|
|
120
124
|
log , fmt_map , abspath , vn = None
|
121
125
|
) :
|
122
126
|
ret = ""
|
127
|
+
maxsz = 1024 * 1024 * 64
|
123
128
|
try:
|
124
129
|
ext = abspath.split(".")[-1].lower()
|
125
130
|
au, pk = fmt_map[ext].split(".")
|
@@ -142,17 +147,41 @@ def au_unpk(
|
|
142
147
|
zf = zipfile.ZipFile(abspath, "r")
|
143
148
|
zil = zf.infolist()
|
144
149
|
zil = [x for x in zil if x.filename.lower().split(".")[-1] == au]
|
150
|
+
if not zil:
|
151
|
+
raise Exception("no audio inside zip")
|
145
152
|
fi = zf.open(zil[0])
|
146
153
|
|
154
|
+
elif pk == "cbz":
|
155
|
+
import zipfile
|
156
|
+
|
157
|
+
zf = zipfile.ZipFile(abspath, "r")
|
158
|
+
znil = [(x.filename.lower(), x) for x in zf.infolist()]
|
159
|
+
nf = len(znil)
|
160
|
+
znil = [x for x in znil if x[0].split(".")[-1] in CBZ_PICS]
|
161
|
+
znil = [x for x in znil if "cover" in x[0]] or znil
|
162
|
+
znil = [x for x in znil if CBZ_01.search(x[0])] or znil
|
163
|
+
t = "cbz: %d files, %d hits" % (nf, len(znil))
|
164
|
+
if znil:
|
165
|
+
t += ", using " + znil[0][1].filename
|
166
|
+
log(t)
|
167
|
+
if not znil:
|
168
|
+
raise Exception("no images inside cbz")
|
169
|
+
fi = zf.open(znil[0][1])
|
170
|
+
|
147
171
|
else:
|
148
172
|
raise Exception("unknown compression %s" % (pk,))
|
149
173
|
|
174
|
+
fsz = 0
|
150
175
|
with os.fdopen(fd, "wb") as fo:
|
151
176
|
while True:
|
152
177
|
buf = fi.read(32768)
|
153
178
|
if not buf:
|
154
179
|
break
|
155
180
|
|
181
|
+
fsz += len(buf)
|
182
|
+
if fsz > maxsz:
|
183
|
+
raise Exception("zipbomb defused")
|
184
|
+
|
156
185
|
fo.write(buf)
|
157
186
|
|
158
187
|
return ret
|
copyparty/pwhash.py
CHANGED
@@ -24,17 +24,13 @@ class PWHash(object):
|
|
24
24
|
def __init__(self, args ):
|
25
25
|
self.args = args
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
except:
|
30
|
-
alg = args.ah_alg
|
31
|
-
ac = {}
|
32
|
-
|
27
|
+
zsl = args.ah_alg.split(",")
|
28
|
+
alg = zsl[0]
|
33
29
|
if alg == "none":
|
34
30
|
alg = ""
|
35
31
|
|
36
32
|
self.alg = alg
|
37
|
-
self.ac =
|
33
|
+
self.ac = zsl[1:]
|
38
34
|
if not alg:
|
39
35
|
self.on = False
|
40
36
|
self.hash = unicode
|
@@ -90,17 +86,23 @@ class PWHash(object):
|
|
90
86
|
its = 2
|
91
87
|
blksz = 8
|
92
88
|
para = 4
|
89
|
+
ramcap = 0 # openssl 1.1 = 32 MiB
|
93
90
|
try:
|
94
91
|
cost = 2 << int(self.ac[0])
|
95
92
|
its = int(self.ac[1])
|
96
93
|
blksz = int(self.ac[2])
|
97
94
|
para = int(self.ac[3])
|
95
|
+
ramcap = int(self.ac[4]) * 1024 * 1024
|
98
96
|
except:
|
99
97
|
pass
|
100
98
|
|
99
|
+
cfg = {"salt": self.salt, "n": cost, "r": blksz, "p": para, "dklen": 24}
|
100
|
+
if ramcap:
|
101
|
+
cfg["maxmem"] = ramcap
|
102
|
+
|
101
103
|
ret = plain.encode("utf-8")
|
102
104
|
for _ in range(its):
|
103
|
-
ret = hashlib.scrypt(ret,
|
105
|
+
ret = hashlib.scrypt(ret, **cfg)
|
104
106
|
|
105
107
|
return "+" + base64.urlsafe_b64encode(ret).decode("utf-8")
|
106
108
|
|
copyparty/svchub.py
CHANGED
@@ -106,7 +106,7 @@ class SvcHub(object):
|
|
106
106
|
self.stopping = False
|
107
107
|
self.stopped = False
|
108
108
|
self.reload_req = False
|
109
|
-
self.
|
109
|
+
self.reload_mutex = threading.Lock()
|
110
110
|
self.stop_cond = threading.Condition()
|
111
111
|
self.nsigs = 3
|
112
112
|
self.retcode = 0
|
@@ -205,6 +205,15 @@ class SvcHub(object):
|
|
205
205
|
t = "WARNING: --s-rd-sz (%d) is larger than --iobuf (%d); this may lead to reduced performance"
|
206
206
|
self.log("root", t % (args.s_rd_sz, args.iobuf), 3)
|
207
207
|
|
208
|
+
zs = ""
|
209
|
+
if args.th_ram_max < 0.22:
|
210
|
+
zs = "generate thumbnails"
|
211
|
+
elif args.th_ram_max < 1:
|
212
|
+
zs = "generate audio waveforms or spectrograms"
|
213
|
+
if zs:
|
214
|
+
t = "WARNING: --th-ram-max is very small (%.2f GiB); will not be able to %s"
|
215
|
+
self.log("root", t % (args.th_ram_max, zs), 3)
|
216
|
+
|
208
217
|
if args.chpw and args.idp_h_usr:
|
209
218
|
t = "ERROR: user-changeable passwords is incompatible with IdP/identity-providers; you must disable either --chpw or --idp-h-usr"
|
210
219
|
self.log("root", t, 1)
|
@@ -994,41 +1003,18 @@ class SvcHub(object):
|
|
994
1003
|
except:
|
995
1004
|
self.log("root", "ssdp startup failed;\n" + min_ex(), 3)
|
996
1005
|
|
997
|
-
def reload(self) :
|
998
|
-
|
999
|
-
|
1000
|
-
return "cannot reload; already in progress"
|
1001
|
-
self.reloading = 1
|
1002
|
-
|
1003
|
-
Daemon(self._reload, "reloading")
|
1004
|
-
return "reload initiated"
|
1005
|
-
|
1006
|
-
def _reload(self, rescan_all_vols = True, up2k = True) :
|
1007
|
-
with self.up2k.mutex:
|
1008
|
-
if self.reloading != 1:
|
1009
|
-
return
|
1010
|
-
self.reloading = 2
|
1006
|
+
def reload(self, rescan_all_vols , up2k ) :
|
1007
|
+
t = "config has been reloaded"
|
1008
|
+
with self.reload_mutex:
|
1011
1009
|
self.log("root", "reloading config")
|
1012
1010
|
self.asrv.reload(9 if up2k else 4)
|
1013
1011
|
if up2k:
|
1014
1012
|
self.up2k.reload(rescan_all_vols)
|
1013
|
+
t += "; volumes are now reinitializing"
|
1015
1014
|
else:
|
1016
1015
|
self.log("root", "reload done")
|
1017
1016
|
self.broker.reload()
|
1018
|
-
|
1019
|
-
|
1020
|
-
def _reload_blocking(self, rescan_all_vols = True, up2k = True) :
|
1021
|
-
while True:
|
1022
|
-
with self.up2k.mutex:
|
1023
|
-
if self.reloading < 2:
|
1024
|
-
self.reloading = 1
|
1025
|
-
break
|
1026
|
-
time.sleep(0.05)
|
1027
|
-
|
1028
|
-
# try to handle multiple pending IdP reloads at once:
|
1029
|
-
time.sleep(0.2)
|
1030
|
-
|
1031
|
-
self._reload(rescan_all_vols=rescan_all_vols, up2k=up2k)
|
1017
|
+
return t
|
1032
1018
|
|
1033
1019
|
def _reload_sessions(self) :
|
1034
1020
|
with self.asrv.mutex:
|
@@ -1042,7 +1028,7 @@ class SvcHub(object):
|
|
1042
1028
|
|
1043
1029
|
if self.reload_req:
|
1044
1030
|
self.reload_req = False
|
1045
|
-
self.reload()
|
1031
|
+
self.reload(True, True)
|
1046
1032
|
|
1047
1033
|
self.shutdown()
|
1048
1034
|
|
copyparty/szip.py
CHANGED
@@ -94,7 +94,7 @@ def gen_hdr(
|
|
94
94
|
|
95
95
|
# spec says to put zeros when !crc if bit3 (streaming)
|
96
96
|
# however infozip does actual sz and it even works on winxp
|
97
|
-
# (same
|
97
|
+
# (same reasoning for z64 extradata later)
|
98
98
|
vsz = 0xFFFFFFFF if z64 else sz
|
99
99
|
ret += spack(b"<LL", vsz, vsz)
|
100
100
|
|
copyparty/tcpsrv.py
CHANGED
@@ -368,7 +368,7 @@ class TcpSrv(object):
|
|
368
368
|
if self.args.q:
|
369
369
|
print(msg)
|
370
370
|
|
371
|
-
self.hub.broker.say("listen", srv)
|
371
|
+
self.hub.broker.say("httpsrv.listen", srv)
|
372
372
|
|
373
373
|
self.srv = srvs
|
374
374
|
self.bound = bound
|
@@ -376,7 +376,7 @@ class TcpSrv(object):
|
|
376
376
|
self._distribute_netdevs()
|
377
377
|
|
378
378
|
def _distribute_netdevs(self):
|
379
|
-
self.hub.broker.say("set_netdevs", self.netdevs)
|
379
|
+
self.hub.broker.say("httpsrv.set_netdevs", self.netdevs)
|
380
380
|
self.hub.start_zeroconf()
|
381
381
|
gencert(self.log, self.args, self.netdevs)
|
382
382
|
self.hub.restart_ftpd()
|
copyparty/tftpd.py
CHANGED
@@ -266,6 +266,7 @@ class Tftpd(object):
|
|
266
266
|
"*",
|
267
267
|
not self.args.no_scandir,
|
268
268
|
[[True, False]],
|
269
|
+
throw=True,
|
269
270
|
)
|
270
271
|
dnames = set([x[0] for x in vfs_ls if stat.S_ISDIR(x[1].st_mode)])
|
271
272
|
dirs1 = [(v.st_mtime, v.st_size, k + "/") for k, v in vfs_ls if k in dnames]
|
copyparty/th_srv.py
CHANGED
@@ -20,7 +20,6 @@ from .util import (
|
|
20
20
|
FFMPEG_URL,
|
21
21
|
Cooldown,
|
22
22
|
Daemon,
|
23
|
-
Pebkac,
|
24
23
|
afsenc,
|
25
24
|
fsenc,
|
26
25
|
min_ex,
|
@@ -161,6 +160,7 @@ class ThumbSrv(object):
|
|
161
160
|
self.ram = {}
|
162
161
|
self.memcond = threading.Condition(self.mutex)
|
163
162
|
self.stopping = False
|
163
|
+
self.rm_nullthumbs = True # forget failed conversions on startup
|
164
164
|
self.nthr = max(1, self.args.th_mt)
|
165
165
|
|
166
166
|
self.q = Queue(self.nthr * 4)
|
@@ -858,7 +858,6 @@ class ThumbSrv(object):
|
|
858
858
|
def cleaner(self) :
|
859
859
|
interval = self.args.th_clean
|
860
860
|
while True:
|
861
|
-
time.sleep(interval)
|
862
861
|
ndirs = 0
|
863
862
|
for vol, histpath in self.asrv.vfs.histtab.items():
|
864
863
|
if histpath.startswith(vol):
|
@@ -872,6 +871,8 @@ class ThumbSrv(object):
|
|
872
871
|
self.log("\033[Jcln err in %s: %r" % (histpath, ex), 3)
|
873
872
|
|
874
873
|
self.log("\033[Jcln ok; rm {} dirs".format(ndirs))
|
874
|
+
self.rm_nullthumbs = False
|
875
|
+
time.sleep(interval)
|
875
876
|
|
876
877
|
def clean(self, histpath ) :
|
877
878
|
ret = 0
|
@@ -892,7 +893,9 @@ class ThumbSrv(object):
|
|
892
893
|
prev_b64 = None
|
893
894
|
prev_fp = ""
|
894
895
|
try:
|
895
|
-
t1 = statdir(
|
896
|
+
t1 = statdir(
|
897
|
+
self.log_func, not self.args.no_scandir, False, thumbpath, False
|
898
|
+
)
|
896
899
|
ents = sorted(list(t1))
|
897
900
|
except:
|
898
901
|
return 0
|
@@ -933,6 +936,10 @@ class ThumbSrv(object):
|
|
933
936
|
|
934
937
|
continue
|
935
938
|
|
939
|
+
if self.rm_nullthumbs and not inf.st_size:
|
940
|
+
bos.unlink(fp)
|
941
|
+
continue
|
942
|
+
|
936
943
|
if b64 == prev_b64:
|
937
944
|
self.log("rm replaced [{}]".format(fp))
|
938
945
|
bos.unlink(prev_fp)
|