copyparty 1.19.16__py3-none-any.whl → 1.19.18__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 +25 -1
- copyparty/__main__.py +32 -9
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +78 -31
- copyparty/bos/bos.py +5 -1
- copyparty/cfg.py +20 -0
- copyparty/ftpd.py +6 -4
- copyparty/httpcli.py +166 -45
- copyparty/httpsrv.py +2 -2
- copyparty/mtag.py +2 -2
- copyparty/smbd.py +1 -1
- copyparty/svchub.py +3 -0
- copyparty/tftpd.py +1 -1
- copyparty/up2k.py +39 -15
- copyparty/util.py +15 -5
- copyparty/web/a/partyfuse.py.gz +0 -0
- copyparty/web/a/u2c.py.gz +0 -0
- copyparty/web/a/webdav-cfg.txt.gz +0 -0
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.html +3 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/splash.html +3 -0
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/svcs.html +1 -1
- copyparty/web/tl/chi.js.gz +0 -0
- copyparty/web/tl/cze.js.gz +0 -0
- copyparty/web/tl/deu.js.gz +0 -0
- copyparty/web/tl/epo.js.gz +0 -0
- copyparty/web/tl/fin.js.gz +0 -0
- copyparty/web/tl/fra.js.gz +0 -0
- copyparty/web/tl/grc.js.gz +0 -0
- copyparty/web/tl/ita.js.gz +0 -0
- copyparty/web/tl/kor.js.gz +0 -0
- copyparty/web/tl/nld.js.gz +0 -0
- copyparty/web/tl/nno.js.gz +0 -0
- copyparty/web/tl/nor.js.gz +0 -0
- copyparty/web/tl/pol.js.gz +0 -0
- copyparty/web/tl/por.js.gz +0 -0
- copyparty/web/tl/rus.js.gz +0 -0
- copyparty/web/tl/spa.js.gz +0 -0
- copyparty/web/tl/swe.js.gz +0 -0
- copyparty/web/tl/tur.js.gz +0 -0
- copyparty/web/tl/ukr.js.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/METADATA +26 -4
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/RECORD +52 -33
- copyparty/web/a/partyfuse.py +0 -947
- copyparty/web/a/u2c.py +0 -1718
- copyparty/web/a/webdav-cfg.bat +0 -45
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/WHEEL +0 -0
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/entry_points.txt +0 -0
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/licenses/LICENSE +0 -0
- {copyparty-1.19.16.dist-info → copyparty-1.19.18.dist-info}/top_level.txt +0 -0
copyparty/httpcli.py
CHANGED
|
@@ -30,7 +30,7 @@ try:
|
|
|
30
30
|
except:
|
|
31
31
|
pass
|
|
32
32
|
|
|
33
|
-
from .__init__ import ANYWIN, RES, TYPE_CHECKING, EnvParams, unicode
|
|
33
|
+
from .__init__ import ANYWIN, RES, RESM, TYPE_CHECKING, EnvParams, unicode
|
|
34
34
|
from .__version__ import S_VERSION
|
|
35
35
|
from .authsrv import LEELOO_DALLAS, VFS # typechk
|
|
36
36
|
from .bos import bos
|
|
@@ -161,6 +161,12 @@ RE_MDV = re.compile(r"(.*)\.([0-9]+\.[0-9]{3})(\.[Mm][Dd])$")
|
|
|
161
161
|
|
|
162
162
|
UPARAM_CC_OK = set("doc move tree".split())
|
|
163
163
|
|
|
164
|
+
PERMS_rwh = [
|
|
165
|
+
[True, False],
|
|
166
|
+
[False, True],
|
|
167
|
+
[False, False, False, False, False, False, True],
|
|
168
|
+
]
|
|
169
|
+
|
|
164
170
|
|
|
165
171
|
class HttpCli(object):
|
|
166
172
|
"""
|
|
@@ -224,6 +230,7 @@ class HttpCli(object):
|
|
|
224
230
|
self.can_delete = False
|
|
225
231
|
self.can_get = False
|
|
226
232
|
self.can_upget = False
|
|
233
|
+
self.can_html = False
|
|
227
234
|
self.can_admin = False
|
|
228
235
|
self.can_dot = False
|
|
229
236
|
self.out_headerlist = []
|
|
@@ -270,7 +277,7 @@ class HttpCli(object):
|
|
|
270
277
|
tpl = self.conn.hsrv.j2[name]
|
|
271
278
|
ka["r"] = self.args.SR if self.is_vproxied else ""
|
|
272
279
|
ka["ts"] = self.conn.hsrv.cachebuster()
|
|
273
|
-
ka["lang"] = self.args.lang
|
|
280
|
+
ka["lang"] = self.cookies.get("cplng") or self.args.lang
|
|
274
281
|
ka["favico"] = self.args.favico
|
|
275
282
|
ka["s_doctitle"] = self.args.doctitle
|
|
276
283
|
ka["tcolor"] = self.vn.flags["tcolor"]
|
|
@@ -732,18 +739,21 @@ class HttpCli(object):
|
|
|
732
739
|
if "bcasechk" in vn.flags and not vn.casechk(rem, True):
|
|
733
740
|
return self.tx_404() and False
|
|
734
741
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
742
|
+
try:
|
|
743
|
+
(
|
|
744
|
+
self.can_read,
|
|
745
|
+
self.can_write,
|
|
746
|
+
self.can_move,
|
|
747
|
+
self.can_delete,
|
|
748
|
+
self.can_get,
|
|
749
|
+
self.can_upget,
|
|
750
|
+
self.can_html,
|
|
751
|
+
self.can_admin,
|
|
752
|
+
self.can_dot,
|
|
753
|
+
) = avn.uaxs[self.uname]
|
|
754
|
+
except:
|
|
755
|
+
pass # default is all-false
|
|
756
|
+
|
|
747
757
|
self.avn = avn
|
|
748
758
|
self.vn = vn # note: do not dbv due to walk/zipgen
|
|
749
759
|
self.rem = rem
|
|
@@ -857,6 +867,16 @@ class HttpCli(object):
|
|
|
857
867
|
return self.conn.iphash.s(self.ip)
|
|
858
868
|
|
|
859
869
|
def cbonk(self, g , v , reason , descr ) :
|
|
870
|
+
cond = self.args.dont_ban
|
|
871
|
+
if (
|
|
872
|
+
cond == "any"
|
|
873
|
+
or (cond == "auth" and self.uname != "*")
|
|
874
|
+
or (cond == "aa" and self.avol)
|
|
875
|
+
or (cond == "av" and self.can_admin)
|
|
876
|
+
or (cond == "rw" and self.can_read and self.can_write)
|
|
877
|
+
):
|
|
878
|
+
return False
|
|
879
|
+
|
|
860
880
|
self.conn.hsrv.nsus += 1
|
|
861
881
|
if not g.lim:
|
|
862
882
|
return False
|
|
@@ -881,7 +901,7 @@ class HttpCli(object):
|
|
|
881
901
|
0,
|
|
882
902
|
self.ip,
|
|
883
903
|
time.time(),
|
|
884
|
-
reason,
|
|
904
|
+
[reason, reason],
|
|
885
905
|
):
|
|
886
906
|
self.log("client banned: %s" % (descr,), 1)
|
|
887
907
|
self.conn.hsrv.bans[ip] = bonk
|
|
@@ -1260,6 +1280,20 @@ class HttpCli(object):
|
|
|
1260
1280
|
else:
|
|
1261
1281
|
return self.tx_res(res_path)
|
|
1262
1282
|
|
|
1283
|
+
if res_path in RESM:
|
|
1284
|
+
ap = self.E.mod_ + RESM[res_path]
|
|
1285
|
+
if (
|
|
1286
|
+
"txt" not in self.uparam
|
|
1287
|
+
and "mime" not in self.uparam
|
|
1288
|
+
and not self.ouparam.get("dl")
|
|
1289
|
+
):
|
|
1290
|
+
# return mimetype matching request extension
|
|
1291
|
+
self.ouparam["dl"] = res_path.split("/")[-1]
|
|
1292
|
+
if bos.path.exists(ap) or bos.path.exists(ap + ".gz"):
|
|
1293
|
+
return self.tx_file(ap)
|
|
1294
|
+
else:
|
|
1295
|
+
return self.tx_res(res_path)
|
|
1296
|
+
|
|
1263
1297
|
self.tx_404()
|
|
1264
1298
|
return False
|
|
1265
1299
|
|
|
@@ -1516,6 +1550,64 @@ class HttpCli(object):
|
|
|
1516
1550
|
self.log("rss: %d hits, %d bytes" % (len(hits), len(bret)))
|
|
1517
1551
|
return True
|
|
1518
1552
|
|
|
1553
|
+
def tx_zls(self, abspath) :
|
|
1554
|
+
if self.do_log:
|
|
1555
|
+
self.log("zls %s @%s" % (self.req, self.uname))
|
|
1556
|
+
if self.args.no_zls:
|
|
1557
|
+
raise Pebkac(405, "zip browsing is disabled in server config")
|
|
1558
|
+
|
|
1559
|
+
import zipfile
|
|
1560
|
+
|
|
1561
|
+
try:
|
|
1562
|
+
with zipfile.ZipFile(abspath, "r") as zf:
|
|
1563
|
+
filelist = [{"fn": f.filename} for f in zf.infolist()]
|
|
1564
|
+
ret = json.dumps(filelist).encode("utf-8", "replace")
|
|
1565
|
+
self.reply(ret, mime="application/json")
|
|
1566
|
+
return True
|
|
1567
|
+
except (zipfile.BadZipfile, RuntimeError):
|
|
1568
|
+
raise Pebkac(404, "requested file is not a valid zip file")
|
|
1569
|
+
|
|
1570
|
+
def tx_zget(self, abspath) :
|
|
1571
|
+
maxsz = 1024 * 1024 * 64
|
|
1572
|
+
|
|
1573
|
+
inner_path = self.uparam.get("zget")
|
|
1574
|
+
if not inner_path:
|
|
1575
|
+
raise Pebkac(405, "inner path is required")
|
|
1576
|
+
if self.do_log:
|
|
1577
|
+
self.log(
|
|
1578
|
+
"zget %s \033[35m%s\033[0m @%s" % (self.req, inner_path, self.uname)
|
|
1579
|
+
)
|
|
1580
|
+
if self.args.no_zls:
|
|
1581
|
+
raise Pebkac(405, "zip browsing is disabled in server config")
|
|
1582
|
+
|
|
1583
|
+
import zipfile
|
|
1584
|
+
|
|
1585
|
+
try:
|
|
1586
|
+
with zipfile.ZipFile(abspath, "r") as zf:
|
|
1587
|
+
zi = zf.getinfo(inner_path)
|
|
1588
|
+
if zi.file_size >= maxsz:
|
|
1589
|
+
raise Pebkac(404, "zip bomb defused")
|
|
1590
|
+
with zf.open(zi, "r") as fi:
|
|
1591
|
+
self.send_headers(length=zi.file_size, mime=guess_mime(inner_path))
|
|
1592
|
+
|
|
1593
|
+
sendfile_py(
|
|
1594
|
+
self.log,
|
|
1595
|
+
0,
|
|
1596
|
+
zi.file_size,
|
|
1597
|
+
fi,
|
|
1598
|
+
self.s,
|
|
1599
|
+
self.args.s_wr_sz,
|
|
1600
|
+
self.args.s_wr_slp,
|
|
1601
|
+
not self.args.no_poll,
|
|
1602
|
+
{},
|
|
1603
|
+
"",
|
|
1604
|
+
)
|
|
1605
|
+
except KeyError:
|
|
1606
|
+
raise Pebkac(404, "no such file in archive")
|
|
1607
|
+
except (zipfile.BadZipfile, RuntimeError):
|
|
1608
|
+
raise Pebkac(404, "requested file is not a valid zip file")
|
|
1609
|
+
return True
|
|
1610
|
+
|
|
1519
1611
|
def handle_propfind(self) :
|
|
1520
1612
|
if self.do_log:
|
|
1521
1613
|
self.log("PFIND %s @%s" % (self.req, self.uname))
|
|
@@ -2079,7 +2171,7 @@ class HttpCli(object):
|
|
|
2079
2171
|
t = "urlform_raw %d @ %r\n %r\n"
|
|
2080
2172
|
self.log(t % (len(orig), "/" + self.vpath, orig))
|
|
2081
2173
|
try:
|
|
2082
|
-
zb = unquote(buf.replace(b"+", b" "))
|
|
2174
|
+
zb = unquote(buf.replace(b"+", b" ").replace(b"&", b"\n"))
|
|
2083
2175
|
plain = zb.decode("utf-8", "replace")
|
|
2084
2176
|
if buf.startswith(b"msg="):
|
|
2085
2177
|
plain = plain[4:]
|
|
@@ -2100,7 +2192,7 @@ class HttpCli(object):
|
|
|
2100
2192
|
len(buf),
|
|
2101
2193
|
self.ip,
|
|
2102
2194
|
time.time(),
|
|
2103
|
-
plain,
|
|
2195
|
+
[plain, orig],
|
|
2104
2196
|
)
|
|
2105
2197
|
|
|
2106
2198
|
t = "urlform_dec %d @ %r\n %r\n"
|
|
@@ -2259,7 +2351,7 @@ class HttpCli(object):
|
|
|
2259
2351
|
remains,
|
|
2260
2352
|
self.ip,
|
|
2261
2353
|
at,
|
|
2262
|
-
|
|
2354
|
+
None,
|
|
2263
2355
|
)
|
|
2264
2356
|
t = hr.get("rejectmsg") or ""
|
|
2265
2357
|
if t or not hr:
|
|
@@ -2394,7 +2486,7 @@ class HttpCli(object):
|
|
|
2394
2486
|
post_sz,
|
|
2395
2487
|
self.ip,
|
|
2396
2488
|
at,
|
|
2397
|
-
|
|
2489
|
+
None,
|
|
2398
2490
|
)
|
|
2399
2491
|
t = hr.get("rejectmsg") or ""
|
|
2400
2492
|
if t or not hr:
|
|
@@ -3226,7 +3318,7 @@ class HttpCli(object):
|
|
|
3226
3318
|
0,
|
|
3227
3319
|
self.ip,
|
|
3228
3320
|
time.time(),
|
|
3229
|
-
|
|
3321
|
+
None,
|
|
3230
3322
|
)
|
|
3231
3323
|
t = hr.get("rejectmsg") or ""
|
|
3232
3324
|
if t or not hr:
|
|
@@ -3398,7 +3490,7 @@ class HttpCli(object):
|
|
|
3398
3490
|
0,
|
|
3399
3491
|
self.ip,
|
|
3400
3492
|
at,
|
|
3401
|
-
|
|
3493
|
+
None,
|
|
3402
3494
|
)
|
|
3403
3495
|
t = hr.get("rejectmsg") or ""
|
|
3404
3496
|
if t or not hr:
|
|
@@ -3505,7 +3597,7 @@ class HttpCli(object):
|
|
|
3505
3597
|
sz,
|
|
3506
3598
|
self.ip,
|
|
3507
3599
|
at,
|
|
3508
|
-
|
|
3600
|
+
None,
|
|
3509
3601
|
)
|
|
3510
3602
|
t = hr.get("rejectmsg") or ""
|
|
3511
3603
|
if t or not hr:
|
|
@@ -3816,7 +3908,7 @@ class HttpCli(object):
|
|
|
3816
3908
|
0,
|
|
3817
3909
|
self.ip,
|
|
3818
3910
|
time.time(),
|
|
3819
|
-
|
|
3911
|
+
None,
|
|
3820
3912
|
)
|
|
3821
3913
|
t = hr.get("rejectmsg") or ""
|
|
3822
3914
|
if t or not hr:
|
|
@@ -3864,7 +3956,7 @@ class HttpCli(object):
|
|
|
3864
3956
|
sz,
|
|
3865
3957
|
self.ip,
|
|
3866
3958
|
new_lastmod,
|
|
3867
|
-
|
|
3959
|
+
None,
|
|
3868
3960
|
)
|
|
3869
3961
|
t = hr.get("rejectmsg") or ""
|
|
3870
3962
|
if t or not hr:
|
|
@@ -4093,8 +4185,11 @@ class HttpCli(object):
|
|
|
4093
4185
|
# force download
|
|
4094
4186
|
|
|
4095
4187
|
if "dl" in self.ouparam:
|
|
4096
|
-
cdis =
|
|
4097
|
-
|
|
4188
|
+
cdis = self.ouparam["dl"] or req_path
|
|
4189
|
+
zs = gen_content_disposition(os.path.basename(cdis))
|
|
4190
|
+
self.out_headers["Content-Disposition"] = zs
|
|
4191
|
+
else:
|
|
4192
|
+
cdis = req_path
|
|
4098
4193
|
|
|
4099
4194
|
#
|
|
4100
4195
|
# if-modified
|
|
@@ -4160,7 +4255,7 @@ class HttpCli(object):
|
|
|
4160
4255
|
elif "mime" in self.uparam:
|
|
4161
4256
|
mime = str(self.uparam.get("mime"))
|
|
4162
4257
|
else:
|
|
4163
|
-
mime = guess_mime(
|
|
4258
|
+
mime = guess_mime(cdis)
|
|
4164
4259
|
|
|
4165
4260
|
logmsg += unicode(status) + logtail
|
|
4166
4261
|
|
|
@@ -4267,8 +4362,11 @@ class HttpCli(object):
|
|
|
4267
4362
|
# force download
|
|
4268
4363
|
|
|
4269
4364
|
if "dl" in self.ouparam:
|
|
4270
|
-
cdis =
|
|
4271
|
-
|
|
4365
|
+
cdis = self.ouparam["dl"] or req_path
|
|
4366
|
+
zs = gen_content_disposition(os.path.basename(cdis))
|
|
4367
|
+
self.out_headers["Content-Disposition"] = zs
|
|
4368
|
+
else:
|
|
4369
|
+
cdis = req_path
|
|
4272
4370
|
|
|
4273
4371
|
#
|
|
4274
4372
|
# if-modified
|
|
@@ -4396,7 +4494,7 @@ class HttpCli(object):
|
|
|
4396
4494
|
elif "rmagic" in self.vn.flags:
|
|
4397
4495
|
mime = guess_mime(req_path, fs_path)
|
|
4398
4496
|
else:
|
|
4399
|
-
mime = guess_mime(
|
|
4497
|
+
mime = guess_mime(cdis)
|
|
4400
4498
|
|
|
4401
4499
|
if "nohtml" in self.vn.flags and "html" in mime:
|
|
4402
4500
|
mime = "text/plain; charset=utf-8"
|
|
@@ -4998,7 +5096,7 @@ class HttpCli(object):
|
|
|
4998
5096
|
"edit": "edit" in self.uparam,
|
|
4999
5097
|
"title": html_escape(self.vpath, crlf=True),
|
|
5000
5098
|
"lastmod": int(ts_md * 1000),
|
|
5001
|
-
"lang": self.args.lang,
|
|
5099
|
+
"lang": self.cookies.get("cplng") or self.args.lang,
|
|
5002
5100
|
"favico": self.args.favico,
|
|
5003
5101
|
"have_emp": int(self.args.emp),
|
|
5004
5102
|
"md_no_br": int(vn.flags.get("md_no_br") or 0),
|
|
@@ -5170,7 +5268,7 @@ class HttpCli(object):
|
|
|
5170
5268
|
dls.append((perc, hsent, spd, eta, idle, usr, erd, rds, fn))
|
|
5171
5269
|
|
|
5172
5270
|
if self.args.have_unlistc:
|
|
5173
|
-
allvols = self.asrv.vfs.
|
|
5271
|
+
allvols = self.asrv.vfs.all_nodes
|
|
5174
5272
|
rvol = [x for x in rvol if "unlistcr" not in allvols[x[1:-1]].flags]
|
|
5175
5273
|
wvol = [x for x in wvol if "unlistcw" not in allvols[x[1:-1]].flags]
|
|
5176
5274
|
|
|
@@ -5383,13 +5481,20 @@ class HttpCli(object):
|
|
|
5383
5481
|
return self.redirect("", "?h", x.get(), "return to", False)
|
|
5384
5482
|
|
|
5385
5483
|
def tx_stack(self) :
|
|
5386
|
-
|
|
5484
|
+
zs = self.args.stack_who
|
|
5485
|
+
if zs == "all" or (
|
|
5486
|
+
(zs == "a" and self.avol)
|
|
5487
|
+
or (zs == "rw" and [x for x in self.wvol if x in self.rvol])
|
|
5488
|
+
):
|
|
5489
|
+
pass
|
|
5490
|
+
else:
|
|
5387
5491
|
raise Pebkac(403, "'stack' not allowed for user " + self.uname)
|
|
5388
5492
|
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5493
|
+
ret = html_escape(alltrace(self.args.stack_v))
|
|
5494
|
+
if self.args.stack_v:
|
|
5495
|
+
ret = "<pre>%s\n%s" % (time.time(), ret)
|
|
5496
|
+
else:
|
|
5497
|
+
ret = "<pre>%s" % (ret,)
|
|
5393
5498
|
self.reply(ret.encode("utf-8"))
|
|
5394
5499
|
return True
|
|
5395
5500
|
|
|
@@ -5442,7 +5547,7 @@ class HttpCli(object):
|
|
|
5442
5547
|
rem,
|
|
5443
5548
|
self.uname,
|
|
5444
5549
|
not self.args.no_scandir,
|
|
5445
|
-
|
|
5550
|
+
PERMS_rwh,
|
|
5446
5551
|
)
|
|
5447
5552
|
dots = self.uname in vn.axs.udot
|
|
5448
5553
|
dk_sz = vn.flags.get("dk")
|
|
@@ -5474,7 +5579,13 @@ class HttpCli(object):
|
|
|
5474
5579
|
for x in vfs_virt:
|
|
5475
5580
|
if x != excl:
|
|
5476
5581
|
try:
|
|
5477
|
-
dvn, drem = vfs.get(vjoin(top, x), self.uname,
|
|
5582
|
+
dvn, drem = vfs.get(vjoin(top, x), self.uname, False, False)
|
|
5583
|
+
if (
|
|
5584
|
+
self.uname not in dvn.axs.uread
|
|
5585
|
+
and self.uname not in dvn.axs.uwrite
|
|
5586
|
+
and self.uname not in dvn.axs.uhtml
|
|
5587
|
+
):
|
|
5588
|
+
raise Exception()
|
|
5478
5589
|
bos.stat(dvn.canonical(drem, False))
|
|
5479
5590
|
except:
|
|
5480
5591
|
x += "\n"
|
|
@@ -6399,8 +6510,7 @@ class HttpCli(object):
|
|
|
6399
6510
|
return self.tx_svg("upload\nonly")
|
|
6400
6511
|
|
|
6401
6512
|
if not self.can_read and self.can_get and self.avn:
|
|
6402
|
-
|
|
6403
|
-
if self.uname not in axs.uhtml:
|
|
6513
|
+
if not self.can_html:
|
|
6404
6514
|
pass
|
|
6405
6515
|
elif is_dir:
|
|
6406
6516
|
for fn in ("index.htm", "index.html"):
|
|
@@ -6421,6 +6531,7 @@ class HttpCli(object):
|
|
|
6421
6531
|
|
|
6422
6532
|
fk_pass = True
|
|
6423
6533
|
is_dir = False
|
|
6534
|
+
add_og = False
|
|
6424
6535
|
rem = vjoin(rem, fn)
|
|
6425
6536
|
vrem = vjoin(vrem, fn)
|
|
6426
6537
|
abspath = ap2
|
|
@@ -6456,14 +6567,23 @@ class HttpCli(object):
|
|
|
6456
6567
|
):
|
|
6457
6568
|
return self.tx_md(vn, abspath)
|
|
6458
6569
|
|
|
6570
|
+
if "zls" in self.uparam:
|
|
6571
|
+
return self.tx_zls(abspath)
|
|
6572
|
+
if "zget" in self.uparam:
|
|
6573
|
+
return self.tx_zget(abspath)
|
|
6574
|
+
|
|
6459
6575
|
if not add_og or not og_fn:
|
|
6460
|
-
|
|
6461
|
-
abspath, None
|
|
6462
|
-
|
|
6576
|
+
if st.st_size or "nopipe" in vn.flags:
|
|
6577
|
+
return self.tx_file(abspath, None)
|
|
6578
|
+
else:
|
|
6579
|
+
return self.tx_file(abspath, vn.get_dbv("")[0].realpath)
|
|
6463
6580
|
|
|
6464
6581
|
elif is_dir and not self.can_read:
|
|
6465
6582
|
if use_dirkey:
|
|
6466
6583
|
is_dk = True
|
|
6584
|
+
elif self.can_get and "doc" in self.uparam:
|
|
6585
|
+
zs = vjoin(self.vpath, self.uparam["doc"]) + "?v"
|
|
6586
|
+
return self.redirect(zs, flavor="redirecting to", use302=True)
|
|
6467
6587
|
elif not self.can_write:
|
|
6468
6588
|
return self.tx_404(True)
|
|
6469
6589
|
|
|
@@ -6545,6 +6665,7 @@ class HttpCli(object):
|
|
|
6545
6665
|
"acct": self.uname,
|
|
6546
6666
|
"perms": perms,
|
|
6547
6667
|
}
|
|
6668
|
+
# also see `js_htm` in authsrv.py
|
|
6548
6669
|
j2a = {
|
|
6549
6670
|
"cgv1": vn.js_htm,
|
|
6550
6671
|
"cgv": cgv,
|
|
@@ -6604,7 +6725,7 @@ class HttpCli(object):
|
|
|
6604
6725
|
rem,
|
|
6605
6726
|
self.uname,
|
|
6606
6727
|
not self.args.no_scandir,
|
|
6607
|
-
|
|
6728
|
+
PERMS_rwh,
|
|
6608
6729
|
lstat="lt" in self.uparam,
|
|
6609
6730
|
throw=True,
|
|
6610
6731
|
)
|
copyparty/httpsrv.py
CHANGED
|
@@ -378,8 +378,8 @@ class HttpSrv(object):
|
|
|
378
378
|
if nloris < nconn / 2:
|
|
379
379
|
continue
|
|
380
380
|
|
|
381
|
-
t = "
|
|
382
|
-
self.log(self.name, t
|
|
381
|
+
t = "slow%s (idle-conn): %s banned for %d min" # slowloris
|
|
382
|
+
self.log(self.name, t % ("loris", ip, self.args.loris), 1)
|
|
383
383
|
self.bans[ip] = int(time.time() + self.args.loris * 60)
|
|
384
384
|
|
|
385
385
|
if self.args.log_conn:
|
copyparty/mtag.py
CHANGED
|
@@ -162,12 +162,12 @@ def au_unpk(
|
|
|
162
162
|
znil = [x for x in znil if "cover" in x[0]] or znil
|
|
163
163
|
znil = [x for x in znil if CBZ_01.search(x[0])] or znil
|
|
164
164
|
t = "cbz: %d files, %d hits" % (nf, len(znil))
|
|
165
|
+
if not znil:
|
|
166
|
+
raise Exception("no images inside cbz")
|
|
165
167
|
using = sorted(znil)[0][1].filename
|
|
166
168
|
if znil:
|
|
167
169
|
t += ", using " + using
|
|
168
170
|
log(t)
|
|
169
|
-
if not znil:
|
|
170
|
-
raise Exception("no images inside cbz")
|
|
171
171
|
fi = zf.open(using)
|
|
172
172
|
|
|
173
173
|
elif pk == "epub":
|
copyparty/smbd.py
CHANGED
copyparty/svchub.py
CHANGED
|
@@ -285,6 +285,9 @@ class SvcHub(object):
|
|
|
285
285
|
ch = "abcdefghijklmnopqrstuvwx"[int(args.theme / 2)]
|
|
286
286
|
args.theme = "{0}{1} {0} {1}".format(ch, bri)
|
|
287
287
|
|
|
288
|
+
if args.no_stack:
|
|
289
|
+
args.stack_who = "no"
|
|
290
|
+
|
|
288
291
|
if args.nid:
|
|
289
292
|
args.du_who = "no"
|
|
290
293
|
args.du_iwho = n_du_who(args.du_who)
|
copyparty/tftpd.py
CHANGED
copyparty/up2k.py
CHANGED
|
@@ -10,6 +10,7 @@ import re
|
|
|
10
10
|
import shutil
|
|
11
11
|
import stat
|
|
12
12
|
import subprocess as sp
|
|
13
|
+
import sys
|
|
13
14
|
import tempfile
|
|
14
15
|
import threading
|
|
15
16
|
import time
|
|
@@ -27,6 +28,7 @@ from .mtag import MParser, MTag
|
|
|
27
28
|
from .util import (
|
|
28
29
|
E_FS_CRIT,
|
|
29
30
|
E_FS_MEH,
|
|
31
|
+
HAVE_FICLONE,
|
|
30
32
|
HAVE_SQLITE3,
|
|
31
33
|
SYMTIME,
|
|
32
34
|
VF_CAREFUL,
|
|
@@ -85,6 +87,10 @@ DB_VER = 6
|
|
|
85
87
|
if TYPE_CHECKING:
|
|
86
88
|
from .svchub import SvcHub
|
|
87
89
|
|
|
90
|
+
USE_FICLONE = HAVE_FICLONE and sys.version_info < (3, 14)
|
|
91
|
+
if USE_FICLONE:
|
|
92
|
+
import fcntl
|
|
93
|
+
|
|
88
94
|
zsg = "avif,avifs,bmp,gif,heic,heics,heif,heifs,ico,j2p,j2k,jp2,jpeg,jpg,jpx,png,tga,tif,tiff,webp"
|
|
89
95
|
ICV_EXTS = set(zsg.split(","))
|
|
90
96
|
|
|
@@ -3282,7 +3288,7 @@ class Up2k(object):
|
|
|
3282
3288
|
job["size"],
|
|
3283
3289
|
job["addr"],
|
|
3284
3290
|
job["at"],
|
|
3285
|
-
|
|
3291
|
+
None,
|
|
3286
3292
|
)
|
|
3287
3293
|
t = hr.get("rejectmsg") or ""
|
|
3288
3294
|
if t or not hr:
|
|
@@ -3514,11 +3520,26 @@ class Up2k(object):
|
|
|
3514
3520
|
|
|
3515
3521
|
linked = False
|
|
3516
3522
|
try:
|
|
3517
|
-
if
|
|
3518
|
-
|
|
3523
|
+
if rm and bos.path.exists(dst):
|
|
3524
|
+
wunlink(self.log, dst, flags)
|
|
3525
|
+
|
|
3519
3526
|
if not is_mv and not flags.get("dedup"):
|
|
3520
3527
|
raise Exception("dedup is disabled in config")
|
|
3521
3528
|
|
|
3529
|
+
if "reflink" in flags:
|
|
3530
|
+
if not USE_FICLONE:
|
|
3531
|
+
raise Exception("reflink") # python 3.14 or newer; no need
|
|
3532
|
+
try:
|
|
3533
|
+
with open(fsenc(src), "rb") as fi, open(fsenc(dst), "wb") as fo:
|
|
3534
|
+
fcntl.ioctl(fo.fileno(), fcntl.FICLONE, fi.fileno())
|
|
3535
|
+
except:
|
|
3536
|
+
if bos.path.exists(dst):
|
|
3537
|
+
wunlink(self.log, dst, flags)
|
|
3538
|
+
raise
|
|
3539
|
+
if lmod:
|
|
3540
|
+
bos.utime_c(self.log, dst, int(lmod), False)
|
|
3541
|
+
return
|
|
3542
|
+
|
|
3522
3543
|
lsrc = src
|
|
3523
3544
|
ldst = dst
|
|
3524
3545
|
fs1 = bos.stat(os.path.dirname(src)).st_dev
|
|
@@ -3545,9 +3566,6 @@ class Up2k(object):
|
|
|
3545
3566
|
lsrc = lsrc.replace("/", "\\")
|
|
3546
3567
|
ldst = ldst.replace("/", "\\")
|
|
3547
3568
|
|
|
3548
|
-
if rm and bos.path.exists(dst):
|
|
3549
|
-
wunlink(self.log, dst, flags)
|
|
3550
|
-
|
|
3551
3569
|
try:
|
|
3552
3570
|
if "hardlink" in flags:
|
|
3553
3571
|
os.link(fsenc(absreal(src)), fsenc(dst))
|
|
@@ -3962,7 +3980,7 @@ class Up2k(object):
|
|
|
3962
3980
|
sz,
|
|
3963
3981
|
ip,
|
|
3964
3982
|
at or time.time(),
|
|
3965
|
-
|
|
3983
|
+
None,
|
|
3966
3984
|
)
|
|
3967
3985
|
t = hr.get("rejectmsg") or ""
|
|
3968
3986
|
if t or not hr:
|
|
@@ -4197,7 +4215,7 @@ class Up2k(object):
|
|
|
4197
4215
|
st.st_size,
|
|
4198
4216
|
ip,
|
|
4199
4217
|
time.time(),
|
|
4200
|
-
|
|
4218
|
+
None,
|
|
4201
4219
|
):
|
|
4202
4220
|
t = "delete blocked by xbd server config: %r"
|
|
4203
4221
|
self.log(t % (abspath,), 1)
|
|
@@ -4237,7 +4255,7 @@ class Up2k(object):
|
|
|
4237
4255
|
st.st_size,
|
|
4238
4256
|
ip,
|
|
4239
4257
|
time.time(),
|
|
4240
|
-
|
|
4258
|
+
None,
|
|
4241
4259
|
)
|
|
4242
4260
|
|
|
4243
4261
|
if is_dir:
|
|
@@ -4365,7 +4383,7 @@ class Up2k(object):
|
|
|
4365
4383
|
fsize,
|
|
4366
4384
|
ip,
|
|
4367
4385
|
time.time(),
|
|
4368
|
-
|
|
4386
|
+
None,
|
|
4369
4387
|
):
|
|
4370
4388
|
t = "copy blocked by xbr server config: %r" % (svp,)
|
|
4371
4389
|
self.log(t, 1)
|
|
@@ -4465,7 +4483,7 @@ class Up2k(object):
|
|
|
4465
4483
|
fsize,
|
|
4466
4484
|
ip,
|
|
4467
4485
|
time.time(),
|
|
4468
|
-
|
|
4486
|
+
None,
|
|
4469
4487
|
)
|
|
4470
4488
|
|
|
4471
4489
|
return "k"
|
|
@@ -4616,7 +4634,7 @@ class Up2k(object):
|
|
|
4616
4634
|
fsize,
|
|
4617
4635
|
ip,
|
|
4618
4636
|
time.time(),
|
|
4619
|
-
|
|
4637
|
+
None,
|
|
4620
4638
|
):
|
|
4621
4639
|
t = "move blocked by xbr server config: %r" % (svp,)
|
|
4622
4640
|
self.log(t, 1)
|
|
@@ -4656,7 +4674,7 @@ class Up2k(object):
|
|
|
4656
4674
|
fsize,
|
|
4657
4675
|
ip,
|
|
4658
4676
|
time.time(),
|
|
4659
|
-
|
|
4677
|
+
None,
|
|
4660
4678
|
)
|
|
4661
4679
|
|
|
4662
4680
|
return "k"
|
|
@@ -4667,6 +4685,12 @@ class Up2k(object):
|
|
|
4667
4685
|
has_dupes = False
|
|
4668
4686
|
if w:
|
|
4669
4687
|
if c2 and c2 != c1:
|
|
4688
|
+
if "nodupem" in dvn.flags:
|
|
4689
|
+
q = "select w from up where substr(w,1,16) = ?"
|
|
4690
|
+
for (w2,) in c2.execute(q, (w[:16],)):
|
|
4691
|
+
if w == w2:
|
|
4692
|
+
t = "file exists in target volume, and dupes are forbidden in config"
|
|
4693
|
+
raise Pebkac(400, t)
|
|
4670
4694
|
self._copy_tags(c1, c2, w)
|
|
4671
4695
|
|
|
4672
4696
|
xlink = bool(svn.flags.get("xlink"))
|
|
@@ -4775,7 +4799,7 @@ class Up2k(object):
|
|
|
4775
4799
|
fsize,
|
|
4776
4800
|
ip,
|
|
4777
4801
|
time.time(),
|
|
4778
|
-
|
|
4802
|
+
None,
|
|
4779
4803
|
)
|
|
4780
4804
|
|
|
4781
4805
|
return "k"
|
|
@@ -5112,7 +5136,7 @@ class Up2k(object):
|
|
|
5112
5136
|
job["size"],
|
|
5113
5137
|
job["addr"],
|
|
5114
5138
|
job["t0"],
|
|
5115
|
-
|
|
5139
|
+
None,
|
|
5116
5140
|
)
|
|
5117
5141
|
t = hr.get("rejectmsg") or ""
|
|
5118
5142
|
if t or not hr:
|