copyparty 1.16.4__py3-none-any.whl → 1.16.6__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 +3 -0
- copyparty/__main__.py +6 -3
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +31 -18
- copyparty/fsutil.py +4 -4
- copyparty/httpcli.py +220 -79
- copyparty/httpsrv.py +6 -5
- copyparty/mtag.py +4 -4
- copyparty/smbd.py +1 -1
- copyparty/sutil.py +1 -1
- copyparty/tcpsrv.py +6 -6
- copyparty/tftpd.py +1 -1
- copyparty/th_cli.py +3 -3
- copyparty/th_srv.py +6 -6
- copyparty/u2idx.py +3 -3
- copyparty/up2k.py +91 -86
- copyparty/util.py +13 -13
- copyparty/web/deps/marked.js.gz +0 -0
- copyparty/web/rups.css.gz +0 -0
- copyparty/web/rups.html +67 -0
- copyparty/web/rups.js.gz +0 -0
- copyparty/web/shares.css.gz +0 -0
- copyparty/web/shares.html +2 -0
- copyparty/web/splash.html +1 -0
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/svcs.html +0 -2
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.16.4.dist-info → copyparty-1.16.6.dist-info}/METADATA +20 -3
- {copyparty-1.16.4.dist-info → copyparty-1.16.6.dist-info}/RECORD +34 -31
- {copyparty-1.16.4.dist-info → copyparty-1.16.6.dist-info}/LICENSE +0 -0
- {copyparty-1.16.4.dist-info → copyparty-1.16.6.dist-info}/WHEEL +0 -0
- {copyparty-1.16.4.dist-info → copyparty-1.16.6.dist-info}/entry_points.txt +0 -0
- {copyparty-1.16.4.dist-info → copyparty-1.16.6.dist-info}/top_level.txt +0 -0
copyparty/httpcli.py
CHANGED
@@ -474,8 +474,8 @@ class HttpCli(object):
|
|
474
474
|
if vpath.startswith(self.args.R):
|
475
475
|
vpath = vpath[len(self.args.R) + 1 :]
|
476
476
|
else:
|
477
|
-
t = "incorrect --rp-loc or webserver config; expected vpath starting with
|
478
|
-
self.log(t
|
477
|
+
t = "incorrect --rp-loc or webserver config; expected vpath starting with %r but got %r"
|
478
|
+
self.log(t % (self.args.R, vpath), 1)
|
479
479
|
|
480
480
|
self.ouparam = uparam.copy()
|
481
481
|
|
@@ -513,7 +513,7 @@ class HttpCli(object):
|
|
513
513
|
return self.tx_qr()
|
514
514
|
|
515
515
|
if relchk(self.vpath) and (self.vpath != "*" or self.mode != "OPTIONS"):
|
516
|
-
self.log("invalid relpath
|
516
|
+
self.log("invalid relpath %r" % ("/" + self.vpath,))
|
517
517
|
self.cbonk(self.conn.hsrv.gmal, self.req, "bad_vp", "invalid relpaths")
|
518
518
|
return self.tx_404() and self.keepalive
|
519
519
|
|
@@ -537,8 +537,14 @@ class HttpCli(object):
|
|
537
537
|
except:
|
538
538
|
pass
|
539
539
|
|
540
|
+
self.pw = uparam.get("pw") or self.headers.get("pw") or bauth or cookie_pw
|
541
|
+
self.uname = (
|
542
|
+
self.asrv.sesa.get(self.pw)
|
543
|
+
or self.asrv.iacct.get(self.asrv.ah.hash(self.pw))
|
544
|
+
or "*"
|
545
|
+
)
|
546
|
+
|
540
547
|
if self.args.idp_h_usr:
|
541
|
-
self.pw = ""
|
542
548
|
idp_usr = self.headers.get(self.args.idp_h_usr) or ""
|
543
549
|
if idp_usr:
|
544
550
|
idp_grp = (
|
@@ -583,20 +589,11 @@ class HttpCli(object):
|
|
583
589
|
idp_grp = ""
|
584
590
|
|
585
591
|
if idp_usr in self.asrv.vfs.aread:
|
592
|
+
self.pw = ""
|
586
593
|
self.uname = idp_usr
|
587
594
|
self.html_head += "<script>var is_idp=1</script>\n"
|
588
595
|
else:
|
589
|
-
self.log("unknown username:
|
590
|
-
self.uname = "*"
|
591
|
-
else:
|
592
|
-
self.uname = "*"
|
593
|
-
else:
|
594
|
-
self.pw = uparam.get("pw") or self.headers.get("pw") or bauth or cookie_pw
|
595
|
-
self.uname = (
|
596
|
-
self.asrv.sesa.get(self.pw)
|
597
|
-
or self.asrv.iacct.get(self.asrv.ah.hash(self.pw))
|
598
|
-
or "*"
|
599
|
-
)
|
596
|
+
self.log("unknown username: %r" % (idp_usr,), 1)
|
600
597
|
|
601
598
|
if self.args.ipu and self.uname == "*":
|
602
599
|
self.uname = self.conn.ipu_iu[self.conn.ipu_nm.map(self.ip)]
|
@@ -661,7 +658,7 @@ class HttpCli(object):
|
|
661
658
|
origin = self.headers.get("origin", "<?>")
|
662
659
|
proto = "https://" if self.is_https else "http://"
|
663
660
|
guess = "modifying" if (origin and host) else "stripping"
|
664
|
-
t = "cors-reject %s because request-header Origin
|
661
|
+
t = "cors-reject %s because request-header Origin=%r does not match request-protocol %r and host %r based on request-header Host=%r (note: if this request is not malicious, check if your reverse-proxy is accidentally %s request headers, in particular 'Origin', for example by running copyparty with --ihead='*' to show all request headers)"
|
665
662
|
self.log(t % (self.mode, origin, proto, self.host, host, guess), 3)
|
666
663
|
raise Pebkac(403, "rejected by cors-check")
|
667
664
|
|
@@ -707,7 +704,7 @@ class HttpCli(object):
|
|
707
704
|
|
708
705
|
if pex.code != 404 or self.do_log:
|
709
706
|
self.log(
|
710
|
-
"http%d: %s\033[0m, %
|
707
|
+
"http%d: %s\033[0m, %r" % (pex.code, msg, "/" + self.vpath),
|
711
708
|
6 if em.startswith("client d/c ") else 3,
|
712
709
|
)
|
713
710
|
|
@@ -1116,6 +1113,8 @@ class HttpCli(object):
|
|
1116
1113
|
logmsg += " [\033[36m" + rval + "\033[0m]"
|
1117
1114
|
|
1118
1115
|
self.log(logmsg)
|
1116
|
+
if "%" in self.req:
|
1117
|
+
self.log(" `-- %r" % (self.vpath,))
|
1119
1118
|
|
1120
1119
|
# "embedded" resources
|
1121
1120
|
if self.vpath.startswith(".cpr"):
|
@@ -1150,8 +1149,8 @@ class HttpCli(object):
|
|
1150
1149
|
return self.tx_res(res_path)
|
1151
1150
|
|
1152
1151
|
if res_path != undot(res_path):
|
1153
|
-
t = "malicious user; attempted path traversal
|
1154
|
-
self.log(t
|
1152
|
+
t = "malicious user; attempted path traversal %r => %r"
|
1153
|
+
self.log(t % ("/" + self.vpath, res_path), 1)
|
1155
1154
|
self.cbonk(self.conn.hsrv.gmal, self.req, "trav", "path traversal")
|
1156
1155
|
|
1157
1156
|
self.tx_404()
|
@@ -1162,11 +1161,11 @@ class HttpCli(object):
|
|
1162
1161
|
return True
|
1163
1162
|
|
1164
1163
|
if not self.can_read and not self.can_write and not self.can_get:
|
1165
|
-
t = "
|
1164
|
+
t = "@%s has no access to %r"
|
1166
1165
|
|
1167
1166
|
if "on403" in self.vn.flags:
|
1168
1167
|
t += " (on403)"
|
1169
|
-
self.log(t
|
1168
|
+
self.log(t % (self.uname, "/" + self.vpath))
|
1170
1169
|
ret = self.on40x(self.vn.flags["on403"], self.vn, self.rem)
|
1171
1170
|
if ret == "true":
|
1172
1171
|
return True
|
@@ -1185,7 +1184,7 @@ class HttpCli(object):
|
|
1185
1184
|
if self.vpath:
|
1186
1185
|
ptn = self.args.nonsus_urls
|
1187
1186
|
if not ptn or not ptn.search(self.vpath):
|
1188
|
-
self.log(t
|
1187
|
+
self.log(t % (self.uname, "/" + self.vpath))
|
1189
1188
|
|
1190
1189
|
return self.tx_404(True)
|
1191
1190
|
|
@@ -1229,6 +1228,9 @@ class HttpCli(object):
|
|
1229
1228
|
if "dls" in self.uparam:
|
1230
1229
|
return self.tx_dls()
|
1231
1230
|
|
1231
|
+
if "ru" in self.uparam:
|
1232
|
+
return self.tx_rups()
|
1233
|
+
|
1232
1234
|
if "h" in self.uparam:
|
1233
1235
|
return self.tx_mounts()
|
1234
1236
|
|
@@ -1379,6 +1381,8 @@ class HttpCli(object):
|
|
1379
1381
|
def handle_propfind(self) :
|
1380
1382
|
if self.do_log:
|
1381
1383
|
self.log("PFIND %s @%s" % (self.req, self.uname))
|
1384
|
+
if "%" in self.req:
|
1385
|
+
self.log(" `-- %r" % (self.vpath,))
|
1382
1386
|
|
1383
1387
|
if self.args.no_dav:
|
1384
1388
|
raise Pebkac(405, "WebDAV is disabled in server config")
|
@@ -1429,14 +1433,14 @@ class HttpCli(object):
|
|
1429
1433
|
if depth == "infinity":
|
1430
1434
|
# allow depth:0 from unmapped root, but require read-axs otherwise
|
1431
1435
|
if not self.can_read and (self.vpath or self.asrv.vfs.realpath):
|
1432
|
-
t = "depth:infinity requires read-access in
|
1433
|
-
t = t % (self.vpath,)
|
1436
|
+
t = "depth:infinity requires read-access in %r"
|
1437
|
+
t = t % ("/" + self.vpath,)
|
1434
1438
|
self.log(t, 3)
|
1435
1439
|
raise Pebkac(401, t)
|
1436
1440
|
|
1437
1441
|
if not stat.S_ISDIR(topdir["st"].st_mode):
|
1438
|
-
t = "depth:infinity can only be used on folders;
|
1439
|
-
t = t % (self.vpath, topdir["st"])
|
1442
|
+
t = "depth:infinity can only be used on folders; %r is 0o%o"
|
1443
|
+
t = t % ("/" + self.vpath, topdir["st"])
|
1440
1444
|
self.log(t, 3)
|
1441
1445
|
raise Pebkac(400, t)
|
1442
1446
|
|
@@ -1462,7 +1466,7 @@ class HttpCli(object):
|
|
1462
1466
|
elif depth == "0" or not stat.S_ISDIR(st.st_mode):
|
1463
1467
|
# propfind on a file; return as topdir
|
1464
1468
|
if not self.can_read and not self.can_get:
|
1465
|
-
self.log("inaccessible:
|
1469
|
+
self.log("inaccessible: %r" % ("/" + self.vpath,))
|
1466
1470
|
raise Pebkac(401, "authenticate")
|
1467
1471
|
|
1468
1472
|
elif depth == "1":
|
@@ -1489,7 +1493,7 @@ class HttpCli(object):
|
|
1489
1493
|
raise Pebkac(412, t.format(depth, t2))
|
1490
1494
|
|
1491
1495
|
if not self.can_read and not self.can_write and not fgen:
|
1492
|
-
self.log("inaccessible:
|
1496
|
+
self.log("inaccessible: %r" % ("/" + self.vpath,))
|
1493
1497
|
raise Pebkac(401, "authenticate")
|
1494
1498
|
|
1495
1499
|
fgen = itertools.chain([topdir], fgen)
|
@@ -1560,12 +1564,14 @@ class HttpCli(object):
|
|
1560
1564
|
def handle_proppatch(self) :
|
1561
1565
|
if self.do_log:
|
1562
1566
|
self.log("PPATCH %s @%s" % (self.req, self.uname))
|
1567
|
+
if "%" in self.req:
|
1568
|
+
self.log(" `-- %r" % (self.vpath,))
|
1563
1569
|
|
1564
1570
|
if self.args.no_dav:
|
1565
1571
|
raise Pebkac(405, "WebDAV is disabled in server config")
|
1566
1572
|
|
1567
1573
|
if not self.can_write:
|
1568
|
-
self.log("
|
1574
|
+
self.log("%s tried to proppatch %r" % (self.uname, "/" + self.vpath))
|
1569
1575
|
raise Pebkac(401, "authenticate")
|
1570
1576
|
|
1571
1577
|
from xml.etree import ElementTree as ET
|
@@ -1612,13 +1618,15 @@ class HttpCli(object):
|
|
1612
1618
|
def handle_lock(self) :
|
1613
1619
|
if self.do_log:
|
1614
1620
|
self.log("LOCK %s @%s" % (self.req, self.uname))
|
1621
|
+
if "%" in self.req:
|
1622
|
+
self.log(" `-- %r" % (self.vpath,))
|
1615
1623
|
|
1616
1624
|
if self.args.no_dav:
|
1617
1625
|
raise Pebkac(405, "WebDAV is disabled in server config")
|
1618
1626
|
|
1619
1627
|
# win7+ deadlocks if we say no; just smile and nod
|
1620
1628
|
if not self.can_write and "Microsoft-WebDAV" not in self.ua:
|
1621
|
-
self.log("
|
1629
|
+
self.log("%s tried to lock %r" % (self.uname, "/" + self.vpath))
|
1622
1630
|
raise Pebkac(401, "authenticate")
|
1623
1631
|
|
1624
1632
|
from xml.etree import ElementTree as ET
|
@@ -1677,12 +1685,14 @@ class HttpCli(object):
|
|
1677
1685
|
def handle_unlock(self) :
|
1678
1686
|
if self.do_log:
|
1679
1687
|
self.log("UNLOCK %s @%s" % (self.req, self.uname))
|
1688
|
+
if "%" in self.req:
|
1689
|
+
self.log(" `-- %r" % (self.vpath,))
|
1680
1690
|
|
1681
1691
|
if self.args.no_dav:
|
1682
1692
|
raise Pebkac(405, "WebDAV is disabled in server config")
|
1683
1693
|
|
1684
1694
|
if not self.can_write and "Microsoft-WebDAV" not in self.ua:
|
1685
|
-
self.log("
|
1695
|
+
self.log("%s tried to lock %r" % (self.uname, "/" + self.vpath))
|
1686
1696
|
raise Pebkac(401, "authenticate")
|
1687
1697
|
|
1688
1698
|
self.send_headers(None, 204)
|
@@ -1694,6 +1704,8 @@ class HttpCli(object):
|
|
1694
1704
|
|
1695
1705
|
if self.do_log:
|
1696
1706
|
self.log("MKCOL %s @%s" % (self.req, self.uname))
|
1707
|
+
if "%" in self.req:
|
1708
|
+
self.log(" `-- %r" % (self.vpath,))
|
1697
1709
|
|
1698
1710
|
try:
|
1699
1711
|
return self._mkdir(self.vpath, True)
|
@@ -1745,6 +1757,8 @@ class HttpCli(object):
|
|
1745
1757
|
def handle_options(self) :
|
1746
1758
|
if self.do_log:
|
1747
1759
|
self.log("OPTIONS %s @%s" % (self.req, self.uname))
|
1760
|
+
if "%" in self.req:
|
1761
|
+
self.log(" `-- %r" % (self.vpath,))
|
1748
1762
|
|
1749
1763
|
oh = self.out_headers
|
1750
1764
|
oh["Allow"] = ", ".join(self.conn.hsrv.mallow)
|
@@ -1760,10 +1774,14 @@ class HttpCli(object):
|
|
1760
1774
|
|
1761
1775
|
def handle_delete(self) :
|
1762
1776
|
self.log("DELETE %s @%s" % (self.req, self.uname))
|
1777
|
+
if "%" in self.req:
|
1778
|
+
self.log(" `-- %r" % (self.vpath,))
|
1763
1779
|
return self.handle_rm([])
|
1764
1780
|
|
1765
1781
|
def handle_put(self) :
|
1766
|
-
self.log("PUT
|
1782
|
+
self.log("PUT %s @%s" % (self.req, self.uname))
|
1783
|
+
if "%" in self.req:
|
1784
|
+
self.log(" `-- %r" % (self.vpath,))
|
1767
1785
|
|
1768
1786
|
if not self.can_write:
|
1769
1787
|
t = "user %s does not have write-access under /%s"
|
@@ -1782,6 +1800,8 @@ class HttpCli(object):
|
|
1782
1800
|
|
1783
1801
|
def handle_post(self) :
|
1784
1802
|
self.log("POST %s @%s" % (self.req, self.uname))
|
1803
|
+
if "%" in self.req:
|
1804
|
+
self.log(" `-- %r" % (self.vpath,))
|
1785
1805
|
|
1786
1806
|
if self.headers.get("expect", "").lower() == "100-continue":
|
1787
1807
|
try:
|
@@ -1826,7 +1846,7 @@ class HttpCli(object):
|
|
1826
1846
|
|
1827
1847
|
if "save" in opt:
|
1828
1848
|
post_sz, _, _, _, path, _ = self.dump_to_file(False)
|
1829
|
-
self.log("urlform:
|
1849
|
+
self.log("urlform: %d bytes, %r" % (post_sz, path))
|
1830
1850
|
elif "print" in opt:
|
1831
1851
|
reader, _ = self.get_body_reader()
|
1832
1852
|
buf = b""
|
@@ -1837,8 +1857,8 @@ class HttpCli(object):
|
|
1837
1857
|
|
1838
1858
|
if buf:
|
1839
1859
|
orig = buf.decode("utf-8", "replace")
|
1840
|
-
t = "urlform_raw
|
1841
|
-
self.log(t
|
1860
|
+
t = "urlform_raw %d @ %r\n %r\n"
|
1861
|
+
self.log(t % (len(orig), "/" + self.vpath, orig))
|
1842
1862
|
try:
|
1843
1863
|
zb = unquote(buf.replace(b"+", b" "))
|
1844
1864
|
plain = zb.decode("utf-8", "replace")
|
@@ -1864,8 +1884,8 @@ class HttpCli(object):
|
|
1864
1884
|
plain,
|
1865
1885
|
)
|
1866
1886
|
|
1867
|
-
t = "urlform_dec
|
1868
|
-
self.log(t
|
1887
|
+
t = "urlform_dec %d @ %r\n %r\n"
|
1888
|
+
self.log(t % (len(plain), "/" + self.vpath, plain))
|
1869
1889
|
|
1870
1890
|
except Exception as ex:
|
1871
1891
|
self.log(repr(ex))
|
@@ -2113,7 +2133,7 @@ class HttpCli(object):
|
|
2113
2133
|
try:
|
2114
2134
|
ext = self.conn.hsrv.magician.ext(path)
|
2115
2135
|
except Exception as ex:
|
2116
|
-
self.log("filetype detection failed for
|
2136
|
+
self.log("filetype detection failed for %r: %s" % (path, ex), 6)
|
2117
2137
|
ext = None
|
2118
2138
|
|
2119
2139
|
if ext:
|
@@ -2213,8 +2233,8 @@ class HttpCli(object):
|
|
2213
2233
|
def handle_stash(self, is_put ) :
|
2214
2234
|
post_sz, sha_hex, sha_b64, remains, path, url = self.dump_to_file(is_put)
|
2215
2235
|
spd = self._spd(post_sz)
|
2216
|
-
t = "
|
2217
|
-
self.log(t
|
2236
|
+
t = "%s wrote %d/%d bytes to %r # %s"
|
2237
|
+
self.log(t % (spd, post_sz, remains, path, sha_b64[:28])) # 21
|
2218
2238
|
|
2219
2239
|
ac = self.uparam.get(
|
2220
2240
|
"want", self.headers.get("accept", "").lower().split(";")[-1]
|
@@ -2244,7 +2264,7 @@ class HttpCli(object):
|
|
2244
2264
|
flags ,
|
2245
2265
|
) :
|
2246
2266
|
now = time.time()
|
2247
|
-
t = "bad-chunk: %.3f %s %s %d %s %s %
|
2267
|
+
t = "bad-chunk: %.3f %s %s %d %s %s %r"
|
2248
2268
|
t = t % (now, bad_sha, good_sha, ofs, self.ip, self.uname, ap)
|
2249
2269
|
self.log(t, 5)
|
2250
2270
|
|
@@ -2383,7 +2403,7 @@ class HttpCli(object):
|
|
2383
2403
|
body = json.loads(json_buf.decode(enc, "replace"))
|
2384
2404
|
try:
|
2385
2405
|
zds = {k: v for k, v in body.items()}
|
2386
|
-
zds["hash"] = "%d chunks" % (len(body["hash"]))
|
2406
|
+
zds["hash"] = "%d chunks" % (len(body["hash"]),)
|
2387
2407
|
except:
|
2388
2408
|
zds = body
|
2389
2409
|
t = "POST len=%d type=%s ip=%s user=%s req=%r json=%s"
|
@@ -2427,7 +2447,7 @@ class HttpCli(object):
|
|
2427
2447
|
if not bos.path.isdir(dst):
|
2428
2448
|
bos.makedirs(dst)
|
2429
2449
|
except OSError as ex:
|
2430
|
-
self.log("makedirs failed
|
2450
|
+
self.log("makedirs failed %r" % (dst,))
|
2431
2451
|
if not bos.path.isdir(dst):
|
2432
2452
|
if ex.errno == errno.EACCES:
|
2433
2453
|
raise Pebkac(500, "the server OS denied write-access")
|
@@ -2452,7 +2472,7 @@ class HttpCli(object):
|
|
2452
2472
|
# strip common suffix (uploader's folder structure)
|
2453
2473
|
vp_req, vp_vfs = vroots(self.vpath, vjoin(dbv.vpath, vrem))
|
2454
2474
|
if not ret["purl"].startswith(vp_vfs):
|
2455
|
-
t = "share-mapping failed; req
|
2475
|
+
t = "share-mapping failed; req=%r dbv=%r vrem=%r n1=%r n2=%r purl=%r"
|
2456
2476
|
zt = (self.vpath, dbv.vpath, vrem, vp_req, vp_vfs, ret["purl"])
|
2457
2477
|
raise Pebkac(500, t % zt)
|
2458
2478
|
ret["purl"] = vp_req + ret["purl"][len(vp_vfs) :]
|
@@ -2501,13 +2521,13 @@ class HttpCli(object):
|
|
2501
2521
|
# search by query params
|
2502
2522
|
q = body["q"]
|
2503
2523
|
n = body.get("n", self.args.srch_hits)
|
2504
|
-
self.log("qj:
|
2524
|
+
self.log("qj: %r |%d|" % (q, n))
|
2505
2525
|
hits, taglist, trunc = idx.search(self.uname, vols, q, n)
|
2506
2526
|
msg = len(hits)
|
2507
2527
|
|
2508
2528
|
idx.p_end = time.time()
|
2509
2529
|
idx.p_dur = idx.p_end - t0
|
2510
|
-
self.log("q#:
|
2530
|
+
self.log("q#: %r (%.2fs)" % (msg, idx.p_dur))
|
2511
2531
|
|
2512
2532
|
order = []
|
2513
2533
|
for t in self.args.mte:
|
@@ -2618,7 +2638,7 @@ class HttpCli(object):
|
|
2618
2638
|
t = "your client is sending %d bytes which is too much (server expected %d bytes at most)"
|
2619
2639
|
raise Pebkac(400, t % (remains, maxsize))
|
2620
2640
|
|
2621
|
-
t = "writing %
|
2641
|
+
t = "writing %r %s+%d #%d+%d %s"
|
2622
2642
|
chunkno = cstart0[0] // chunksize
|
2623
2643
|
zs = " ".join([chashes[0][:15]] + [x[:9] for x in chashes[1:]])
|
2624
2644
|
self.log(t % (path, cstart0, remains, chunkno, len(chashes), zs))
|
@@ -2730,7 +2750,7 @@ class HttpCli(object):
|
|
2730
2750
|
cinf = self.headers.get("x-up2k-stat", "")
|
2731
2751
|
|
2732
2752
|
spd = self._spd(postsize)
|
2733
|
-
self.log("
|
2753
|
+
self.log("%70s thank %r" % (spd, cinf))
|
2734
2754
|
self.reply(b"thank")
|
2735
2755
|
return True
|
2736
2756
|
|
@@ -2809,7 +2829,7 @@ class HttpCli(object):
|
|
2809
2829
|
logpwd = "%" + ub64enc(zb[:12]).decode("ascii")
|
2810
2830
|
|
2811
2831
|
if pwd != "x":
|
2812
|
-
self.log("invalid password:
|
2832
|
+
self.log("invalid password: %r" % (logpwd,), 3)
|
2813
2833
|
self.cbonk(self.conn.hsrv.gpwd, pwd, "pw", "invalid passwords")
|
2814
2834
|
|
2815
2835
|
msg = "naw dude"
|
@@ -2844,7 +2864,7 @@ class HttpCli(object):
|
|
2844
2864
|
rem = sanitize_vpath(rem, "/")
|
2845
2865
|
fn = vfs.canonical(rem)
|
2846
2866
|
if not fn.startswith(vfs.realpath):
|
2847
|
-
self.log("invalid mkdir
|
2867
|
+
self.log("invalid mkdir %r %r" % (self.gctx, vpath), 1)
|
2848
2868
|
raise Pebkac(422)
|
2849
2869
|
|
2850
2870
|
if not nullwrite:
|
@@ -3009,9 +3029,9 @@ class HttpCli(object):
|
|
3009
3029
|
elif bos.path.exists(abspath):
|
3010
3030
|
try:
|
3011
3031
|
wunlink(self.log, abspath, vfs.flags)
|
3012
|
-
t = "overwriting file with new upload: %
|
3032
|
+
t = "overwriting file with new upload: %r"
|
3013
3033
|
except:
|
3014
|
-
t = "toctou while deleting for ?replace: %
|
3034
|
+
t = "toctou while deleting for ?replace: %r"
|
3015
3035
|
self.log(t % (abspath,))
|
3016
3036
|
else:
|
3017
3037
|
open_args = {}
|
@@ -3094,7 +3114,7 @@ class HttpCli(object):
|
|
3094
3114
|
f, tnam = ren_open(tnam, "wb", self.args.iobuf, **open_args)
|
3095
3115
|
try:
|
3096
3116
|
tabspath = os.path.join(fdir, tnam)
|
3097
|
-
self.log("writing to
|
3117
|
+
self.log("writing to %r" % (tabspath,))
|
3098
3118
|
sz, sha_hex, sha_b64 = copier(
|
3099
3119
|
p_data, f, hasher, max_sz, self.args.s_wr_slp
|
3100
3120
|
)
|
@@ -3279,7 +3299,7 @@ class HttpCli(object):
|
|
3279
3299
|
jmsg["files"].append(jpart)
|
3280
3300
|
|
3281
3301
|
vspd = self._spd(sz_total, False)
|
3282
|
-
self.log("
|
3302
|
+
self.log("%s %r" % (vspd, msg))
|
3283
3303
|
|
3284
3304
|
suf = ""
|
3285
3305
|
if not nullwrite and self.args.write_uplog:
|
@@ -3540,7 +3560,7 @@ class HttpCli(object):
|
|
3540
3560
|
if req == zs:
|
3541
3561
|
return True
|
3542
3562
|
|
3543
|
-
t = "wrong dirkey, want %s, got %s\n vp: %
|
3563
|
+
t = "wrong dirkey, want %s, got %s\n vp: %r\n ap: %r"
|
3544
3564
|
self.log(t % (zs, req, self.req, ap), 6)
|
3545
3565
|
return False
|
3546
3566
|
|
@@ -3568,7 +3588,7 @@ class HttpCli(object):
|
|
3568
3588
|
if req == zs:
|
3569
3589
|
return True
|
3570
3590
|
|
3571
|
-
t = "wrong filekey, want %s, got %s\n vp: %
|
3591
|
+
t = "wrong filekey, want %s, got %s\n vp: %r\n ap: %r"
|
3572
3592
|
self.log(t % (zs, req, self.req, ap), 6)
|
3573
3593
|
return False
|
3574
3594
|
|
@@ -3630,7 +3650,7 @@ class HttpCli(object):
|
|
3630
3650
|
elif ph == "srv.htime":
|
3631
3651
|
sv = datetime.now(UTC).strftime("%Y-%m-%d, %H:%M:%S")
|
3632
3652
|
else:
|
3633
|
-
self.log("unknown placeholder in server config: [%s]" % (ph), 3)
|
3653
|
+
self.log("unknown placeholder in server config: [%s]" % (ph,), 3)
|
3634
3654
|
continue
|
3635
3655
|
|
3636
3656
|
sv = self.conn.hsrv.ptn_hsafe.sub("_", sv)
|
@@ -3787,7 +3807,7 @@ class HttpCli(object):
|
|
3787
3807
|
self.pipes.set(req_path, job)
|
3788
3808
|
except Exception as ex:
|
3789
3809
|
if getattr(ex, "errno", 0) != errno.ENOENT:
|
3790
|
-
self.log("will not pipe
|
3810
|
+
self.log("will not pipe %r; %s" % (ap_data, ex), 6)
|
3791
3811
|
ptop = None
|
3792
3812
|
|
3793
3813
|
#
|
@@ -4075,7 +4095,7 @@ class HttpCli(object):
|
|
4075
4095
|
if lower >= data_end:
|
4076
4096
|
if data_end:
|
4077
4097
|
t = "pipe: uploader is too slow; aborting download at %.2f MiB"
|
4078
|
-
self.log(t % (data_end / M))
|
4098
|
+
self.log(t % (data_end / M,))
|
4079
4099
|
raise Pebkac(416, "uploader is too slow")
|
4080
4100
|
|
4081
4101
|
raise Pebkac(416, "no data available yet; please retry in a bit")
|
@@ -4219,7 +4239,7 @@ class HttpCli(object):
|
|
4219
4239
|
|
4220
4240
|
cdis = "attachment; filename=\"{}.{}\"; filename*=UTF-8''{}.{}"
|
4221
4241
|
cdis = cdis.format(afn, ext, ufn, ext)
|
4222
|
-
self.log(cdis)
|
4242
|
+
self.log(repr(cdis))
|
4223
4243
|
self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis})
|
4224
4244
|
|
4225
4245
|
fgen = vn.zipgen(
|
@@ -4881,9 +4901,9 @@ class HttpCli(object):
|
|
4881
4901
|
raise Pebkac(500, "sqlite3 not found on server; unpost is disabled")
|
4882
4902
|
raise Pebkac(500, "server busy, cannot unpost; please retry in a bit")
|
4883
4903
|
|
4884
|
-
|
4885
|
-
|
4886
|
-
|
4904
|
+
zs = self.uparam.get("filter") or ""
|
4905
|
+
filt = re.compile(zs, re.I) if zs else None
|
4906
|
+
lm = "ups %r" % (zs,)
|
4887
4907
|
|
4888
4908
|
if self.args.shr and self.vpath.startswith(self.args.shr1):
|
4889
4909
|
shr_dbv, shr_vrem = self.vn.get_dbv(self.rem)
|
@@ -4924,13 +4944,18 @@ class HttpCli(object):
|
|
4924
4944
|
|
4925
4945
|
nfk, fk_alg = fk_vols.get(vol) or (0, 0)
|
4926
4946
|
|
4927
|
-
|
4947
|
+
n = 2000
|
4948
|
+
q = "select sz, rd, fn, at from up where ip=? and at>? order by at desc"
|
4928
4949
|
for sz, rd, fn, at in cur.execute(q, (self.ip, lim)):
|
4929
4950
|
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
4930
|
-
if filt and
|
4951
|
+
if filt and not filt.search(vp):
|
4931
4952
|
continue
|
4932
4953
|
|
4933
|
-
|
4954
|
+
n -= 1
|
4955
|
+
if not n:
|
4956
|
+
break
|
4957
|
+
|
4958
|
+
rv = {"vp": vp, "sz": sz, "at": at, "nfk": nfk}
|
4934
4959
|
if nfk:
|
4935
4960
|
rv["ap"] = vol.canonical(vjoin(rd, fn))
|
4936
4961
|
rv["fk_alg"] = fk_alg
|
@@ -4940,9 +4965,13 @@ class HttpCli(object):
|
|
4940
4965
|
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
4941
4966
|
ret = ret[:2000]
|
4942
4967
|
|
4968
|
+
if len(ret) > 2000:
|
4969
|
+
ret = ret[:2000]
|
4970
|
+
|
4943
4971
|
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
4944
|
-
|
4945
|
-
for rv in ret
|
4972
|
+
|
4973
|
+
for rv in ret:
|
4974
|
+
rv["vp"] = quotep(rv["vp"])
|
4946
4975
|
nfk = rv.pop("nfk")
|
4947
4976
|
if not nfk:
|
4948
4977
|
continue
|
@@ -4959,12 +4988,6 @@ class HttpCli(object):
|
|
4959
4988
|
)
|
4960
4989
|
rv["vp"] += "?k=" + fk[:nfk]
|
4961
4990
|
|
4962
|
-
n += 1
|
4963
|
-
if n > 2000:
|
4964
|
-
break
|
4965
|
-
|
4966
|
-
ret = ret[:2000]
|
4967
|
-
|
4968
4991
|
if shr_dbv:
|
4969
4992
|
# translate vpaths from share-target to share-url
|
4970
4993
|
# to satisfy access checks
|
@@ -4987,6 +5010,125 @@ class HttpCli(object):
|
|
4987
5010
|
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
4988
5011
|
return True
|
4989
5012
|
|
5013
|
+
def tx_rups(self) :
|
5014
|
+
if self.args.no_ups_page:
|
5015
|
+
raise Pebkac(500, "listing of recent uploads is disabled in server config")
|
5016
|
+
|
5017
|
+
idx = self.conn.get_u2idx()
|
5018
|
+
if not idx or not hasattr(idx, "p_end"):
|
5019
|
+
if not HAVE_SQLITE3:
|
5020
|
+
raise Pebkac(500, "sqlite3 not found on server; recent-uploads n/a")
|
5021
|
+
raise Pebkac(500, "server busy, cannot list recent uploads; please retry")
|
5022
|
+
|
5023
|
+
sfilt = self.uparam.get("filter") or ""
|
5024
|
+
filt = re.compile(sfilt, re.I) if sfilt else None
|
5025
|
+
lm = "ru %r" % (sfilt,)
|
5026
|
+
self.log(lm)
|
5027
|
+
|
5028
|
+
ret = []
|
5029
|
+
t0 = time.time()
|
5030
|
+
allvols = [
|
5031
|
+
x
|
5032
|
+
for x in self.asrv.vfs.all_vols.values()
|
5033
|
+
if "e2d" in x.flags and ("*" in x.axs.uread or self.uname in x.axs.uread)
|
5034
|
+
]
|
5035
|
+
fk_vols = {
|
5036
|
+
vol: (vol.flags["fk"], 2 if "fka" in vol.flags else 1)
|
5037
|
+
for vol in allvols
|
5038
|
+
if "fk" in vol.flags and "*" not in vol.axs.uread
|
5039
|
+
}
|
5040
|
+
|
5041
|
+
for vol in allvols:
|
5042
|
+
cur = idx.get_cur(vol)
|
5043
|
+
if not cur:
|
5044
|
+
continue
|
5045
|
+
|
5046
|
+
nfk, fk_alg = fk_vols.get(vol) or (0, 0)
|
5047
|
+
adm = "*" in vol.axs.uadmin or self.uname in vol.axs.uadmin
|
5048
|
+
dots = "*" in vol.axs.udot or self.uname in vol.axs.udot
|
5049
|
+
|
5050
|
+
n = 1000
|
5051
|
+
q = "select sz, rd, fn, ip, at from up where at>0 order by at desc"
|
5052
|
+
for sz, rd, fn, ip, at in cur.execute(q):
|
5053
|
+
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
5054
|
+
if filt and not filt.search(vp):
|
5055
|
+
continue
|
5056
|
+
|
5057
|
+
if not dots and "/." in vp:
|
5058
|
+
continue
|
5059
|
+
|
5060
|
+
n -= 1
|
5061
|
+
if not n:
|
5062
|
+
break
|
5063
|
+
|
5064
|
+
rv = {
|
5065
|
+
"vp": vp,
|
5066
|
+
"sz": sz,
|
5067
|
+
"ip": ip,
|
5068
|
+
"at": at,
|
5069
|
+
"nfk": nfk,
|
5070
|
+
"adm": adm,
|
5071
|
+
}
|
5072
|
+
if nfk:
|
5073
|
+
rv["ap"] = vol.canonical(vjoin(rd, fn))
|
5074
|
+
rv["fk_alg"] = fk_alg
|
5075
|
+
|
5076
|
+
ret.append(rv)
|
5077
|
+
if len(ret) > 2000:
|
5078
|
+
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
5079
|
+
ret = ret[:1000]
|
5080
|
+
|
5081
|
+
if len(ret) > 1000:
|
5082
|
+
ret = ret[:1000]
|
5083
|
+
|
5084
|
+
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
5085
|
+
|
5086
|
+
for rv in ret:
|
5087
|
+
rv["evp"] = quotep(rv["vp"])
|
5088
|
+
nfk = rv.pop("nfk")
|
5089
|
+
if not nfk:
|
5090
|
+
continue
|
5091
|
+
|
5092
|
+
alg = rv.pop("fk_alg")
|
5093
|
+
ap = rv.pop("ap")
|
5094
|
+
try:
|
5095
|
+
st = bos.stat(ap)
|
5096
|
+
except:
|
5097
|
+
continue
|
5098
|
+
|
5099
|
+
fk = self.gen_fk(
|
5100
|
+
alg, self.args.fk_salt, ap, st.st_size, 0 if ANYWIN else st.st_ino
|
5101
|
+
)
|
5102
|
+
rv["vp"] += "?k=" + fk[:nfk]
|
5103
|
+
|
5104
|
+
if self.args.ups_when:
|
5105
|
+
for rv in ret:
|
5106
|
+
adm = rv.pop("adm")
|
5107
|
+
if not adm:
|
5108
|
+
rv["ip"] = "(You)" if rv["ip"] == self.ip else "(?)"
|
5109
|
+
else:
|
5110
|
+
for rv in ret:
|
5111
|
+
adm = rv.pop("adm")
|
5112
|
+
if not adm:
|
5113
|
+
rv["ip"] = "(You)" if rv["ip"] == self.ip else "(?)"
|
5114
|
+
rv["at"] = 0
|
5115
|
+
|
5116
|
+
if self.is_vproxied:
|
5117
|
+
for v in ret:
|
5118
|
+
v["vp"] = self.args.SR + v["vp"]
|
5119
|
+
|
5120
|
+
self.log("%s #%d %.2fsec" % (lm, len(ret), time.time() - t0))
|
5121
|
+
|
5122
|
+
if "j" in self.ouparam:
|
5123
|
+
jtxt = json.dumps(ret, separators=(",\n", ": "))
|
5124
|
+
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
5125
|
+
return True
|
5126
|
+
|
5127
|
+
rows = [[x["vp"], x["evp"], x["sz"], x["ip"], x["at"]] for x in ret]
|
5128
|
+
html = self.j2s("rups", this=self, rows=rows, filt=sfilt, now=int(time.time()))
|
5129
|
+
self.reply(html.encode("utf-8"), status=200)
|
5130
|
+
return True
|
5131
|
+
|
4990
5132
|
def tx_shares(self) :
|
4991
5133
|
if self.uname == "*":
|
4992
5134
|
self.loud_reply("you're not logged in")
|
@@ -5692,7 +5834,7 @@ class HttpCli(object):
|
|
5692
5834
|
linf = stats.get(fn) or bos.lstat(fspath)
|
5693
5835
|
inf = bos.stat(fspath) if stat.S_ISLNK(linf.st_mode) else linf
|
5694
5836
|
except:
|
5695
|
-
self.log("broken symlink:
|
5837
|
+
self.log("broken symlink: %r" % (fspath,))
|
5696
5838
|
continue
|
5697
5839
|
|
5698
5840
|
is_dir = stat.S_ISDIR(inf.st_mode)
|
@@ -5806,8 +5948,7 @@ class HttpCli(object):
|
|
5806
5948
|
erd_efn = s3enc(idx.mem_cur, rd, fn)
|
5807
5949
|
r = icur.execute(q, erd_efn)
|
5808
5950
|
except:
|
5809
|
-
|
5810
|
-
self.log(t.format(rd, fn, min_ex()))
|
5951
|
+
self.log("tag read error, %r / %r\n%s" % (rd, fn, min_ex()))
|
5811
5952
|
break
|
5812
5953
|
|
5813
5954
|
tags = {k: v for k, v in r}
|
@@ -5927,10 +6068,10 @@ class HttpCli(object):
|
|
5927
6068
|
if doc.lower().endswith(".md") and "exp" in vn.flags:
|
5928
6069
|
doctxt = self._expand(doctxt, vn.flags.get("exp_md") or [])
|
5929
6070
|
else:
|
5930
|
-
self.log("doc 2big:
|
6071
|
+
self.log("doc 2big: %r" % (doc,), 6)
|
5931
6072
|
doctxt = "( size of textfile exceeds serverside limit )"
|
5932
6073
|
else:
|
5933
|
-
self.log("doc 404:
|
6074
|
+
self.log("doc 404: %r" % (doc,), 6)
|
5934
6075
|
doctxt = "( textfile not found )"
|
5935
6076
|
|
5936
6077
|
if doctxt is not None:
|