copyparty 1.16.5__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 +4 -1
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +31 -18
- copyparty/fsutil.py +4 -4
- copyparty/httpcli.py +212 -68
- copyparty/httpsrv.py +6 -5
- copyparty/mtag.py +4 -4
- copyparty/smbd.py +1 -1
- copyparty/sutil.py +1 -1
- 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/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/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.16.5.dist-info → copyparty-1.16.6.dist-info}/METADATA +14 -2
- {copyparty-1.16.5.dist-info → copyparty-1.16.6.dist-info}/RECORD +31 -28
- {copyparty-1.16.5.dist-info → copyparty-1.16.6.dist-info}/LICENSE +0 -0
- {copyparty-1.16.5.dist-info → copyparty-1.16.6.dist-info}/WHEEL +0 -0
- {copyparty-1.16.5.dist-info → copyparty-1.16.6.dist-info}/entry_points.txt +0 -0
- {copyparty-1.16.5.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
|
|
@@ -593,7 +593,7 @@ class HttpCli(object):
|
|
593
593
|
self.uname = idp_usr
|
594
594
|
self.html_head += "<script>var is_idp=1</script>\n"
|
595
595
|
else:
|
596
|
-
self.log("unknown username:
|
596
|
+
self.log("unknown username: %r" % (idp_usr,), 1)
|
597
597
|
|
598
598
|
if self.args.ipu and self.uname == "*":
|
599
599
|
self.uname = self.conn.ipu_iu[self.conn.ipu_nm.map(self.ip)]
|
@@ -658,7 +658,7 @@ class HttpCli(object):
|
|
658
658
|
origin = self.headers.get("origin", "<?>")
|
659
659
|
proto = "https://" if self.is_https else "http://"
|
660
660
|
guess = "modifying" if (origin and host) else "stripping"
|
661
|
-
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)"
|
662
662
|
self.log(t % (self.mode, origin, proto, self.host, host, guess), 3)
|
663
663
|
raise Pebkac(403, "rejected by cors-check")
|
664
664
|
|
@@ -704,7 +704,7 @@ class HttpCli(object):
|
|
704
704
|
|
705
705
|
if pex.code != 404 or self.do_log:
|
706
706
|
self.log(
|
707
|
-
"http%d: %s\033[0m, %
|
707
|
+
"http%d: %s\033[0m, %r" % (pex.code, msg, "/" + self.vpath),
|
708
708
|
6 if em.startswith("client d/c ") else 3,
|
709
709
|
)
|
710
710
|
|
@@ -1113,6 +1113,8 @@ class HttpCli(object):
|
|
1113
1113
|
logmsg += " [\033[36m" + rval + "\033[0m]"
|
1114
1114
|
|
1115
1115
|
self.log(logmsg)
|
1116
|
+
if "%" in self.req:
|
1117
|
+
self.log(" `-- %r" % (self.vpath,))
|
1116
1118
|
|
1117
1119
|
# "embedded" resources
|
1118
1120
|
if self.vpath.startswith(".cpr"):
|
@@ -1147,8 +1149,8 @@ class HttpCli(object):
|
|
1147
1149
|
return self.tx_res(res_path)
|
1148
1150
|
|
1149
1151
|
if res_path != undot(res_path):
|
1150
|
-
t = "malicious user; attempted path traversal
|
1151
|
-
self.log(t
|
1152
|
+
t = "malicious user; attempted path traversal %r => %r"
|
1153
|
+
self.log(t % ("/" + self.vpath, res_path), 1)
|
1152
1154
|
self.cbonk(self.conn.hsrv.gmal, self.req, "trav", "path traversal")
|
1153
1155
|
|
1154
1156
|
self.tx_404()
|
@@ -1159,11 +1161,11 @@ class HttpCli(object):
|
|
1159
1161
|
return True
|
1160
1162
|
|
1161
1163
|
if not self.can_read and not self.can_write and not self.can_get:
|
1162
|
-
t = "
|
1164
|
+
t = "@%s has no access to %r"
|
1163
1165
|
|
1164
1166
|
if "on403" in self.vn.flags:
|
1165
1167
|
t += " (on403)"
|
1166
|
-
self.log(t
|
1168
|
+
self.log(t % (self.uname, "/" + self.vpath))
|
1167
1169
|
ret = self.on40x(self.vn.flags["on403"], self.vn, self.rem)
|
1168
1170
|
if ret == "true":
|
1169
1171
|
return True
|
@@ -1182,7 +1184,7 @@ class HttpCli(object):
|
|
1182
1184
|
if self.vpath:
|
1183
1185
|
ptn = self.args.nonsus_urls
|
1184
1186
|
if not ptn or not ptn.search(self.vpath):
|
1185
|
-
self.log(t
|
1187
|
+
self.log(t % (self.uname, "/" + self.vpath))
|
1186
1188
|
|
1187
1189
|
return self.tx_404(True)
|
1188
1190
|
|
@@ -1226,6 +1228,9 @@ class HttpCli(object):
|
|
1226
1228
|
if "dls" in self.uparam:
|
1227
1229
|
return self.tx_dls()
|
1228
1230
|
|
1231
|
+
if "ru" in self.uparam:
|
1232
|
+
return self.tx_rups()
|
1233
|
+
|
1229
1234
|
if "h" in self.uparam:
|
1230
1235
|
return self.tx_mounts()
|
1231
1236
|
|
@@ -1376,6 +1381,8 @@ class HttpCli(object):
|
|
1376
1381
|
def handle_propfind(self) :
|
1377
1382
|
if self.do_log:
|
1378
1383
|
self.log("PFIND %s @%s" % (self.req, self.uname))
|
1384
|
+
if "%" in self.req:
|
1385
|
+
self.log(" `-- %r" % (self.vpath,))
|
1379
1386
|
|
1380
1387
|
if self.args.no_dav:
|
1381
1388
|
raise Pebkac(405, "WebDAV is disabled in server config")
|
@@ -1426,14 +1433,14 @@ class HttpCli(object):
|
|
1426
1433
|
if depth == "infinity":
|
1427
1434
|
# allow depth:0 from unmapped root, but require read-axs otherwise
|
1428
1435
|
if not self.can_read and (self.vpath or self.asrv.vfs.realpath):
|
1429
|
-
t = "depth:infinity requires read-access in
|
1430
|
-
t = t % (self.vpath,)
|
1436
|
+
t = "depth:infinity requires read-access in %r"
|
1437
|
+
t = t % ("/" + self.vpath,)
|
1431
1438
|
self.log(t, 3)
|
1432
1439
|
raise Pebkac(401, t)
|
1433
1440
|
|
1434
1441
|
if not stat.S_ISDIR(topdir["st"].st_mode):
|
1435
|
-
t = "depth:infinity can only be used on folders;
|
1436
|
-
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"])
|
1437
1444
|
self.log(t, 3)
|
1438
1445
|
raise Pebkac(400, t)
|
1439
1446
|
|
@@ -1459,7 +1466,7 @@ class HttpCli(object):
|
|
1459
1466
|
elif depth == "0" or not stat.S_ISDIR(st.st_mode):
|
1460
1467
|
# propfind on a file; return as topdir
|
1461
1468
|
if not self.can_read and not self.can_get:
|
1462
|
-
self.log("inaccessible:
|
1469
|
+
self.log("inaccessible: %r" % ("/" + self.vpath,))
|
1463
1470
|
raise Pebkac(401, "authenticate")
|
1464
1471
|
|
1465
1472
|
elif depth == "1":
|
@@ -1486,7 +1493,7 @@ class HttpCli(object):
|
|
1486
1493
|
raise Pebkac(412, t.format(depth, t2))
|
1487
1494
|
|
1488
1495
|
if not self.can_read and not self.can_write and not fgen:
|
1489
|
-
self.log("inaccessible:
|
1496
|
+
self.log("inaccessible: %r" % ("/" + self.vpath,))
|
1490
1497
|
raise Pebkac(401, "authenticate")
|
1491
1498
|
|
1492
1499
|
fgen = itertools.chain([topdir], fgen)
|
@@ -1557,12 +1564,14 @@ class HttpCli(object):
|
|
1557
1564
|
def handle_proppatch(self) :
|
1558
1565
|
if self.do_log:
|
1559
1566
|
self.log("PPATCH %s @%s" % (self.req, self.uname))
|
1567
|
+
if "%" in self.req:
|
1568
|
+
self.log(" `-- %r" % (self.vpath,))
|
1560
1569
|
|
1561
1570
|
if self.args.no_dav:
|
1562
1571
|
raise Pebkac(405, "WebDAV is disabled in server config")
|
1563
1572
|
|
1564
1573
|
if not self.can_write:
|
1565
|
-
self.log("
|
1574
|
+
self.log("%s tried to proppatch %r" % (self.uname, "/" + self.vpath))
|
1566
1575
|
raise Pebkac(401, "authenticate")
|
1567
1576
|
|
1568
1577
|
from xml.etree import ElementTree as ET
|
@@ -1609,13 +1618,15 @@ class HttpCli(object):
|
|
1609
1618
|
def handle_lock(self) :
|
1610
1619
|
if self.do_log:
|
1611
1620
|
self.log("LOCK %s @%s" % (self.req, self.uname))
|
1621
|
+
if "%" in self.req:
|
1622
|
+
self.log(" `-- %r" % (self.vpath,))
|
1612
1623
|
|
1613
1624
|
if self.args.no_dav:
|
1614
1625
|
raise Pebkac(405, "WebDAV is disabled in server config")
|
1615
1626
|
|
1616
1627
|
# win7+ deadlocks if we say no; just smile and nod
|
1617
1628
|
if not self.can_write and "Microsoft-WebDAV" not in self.ua:
|
1618
|
-
self.log("
|
1629
|
+
self.log("%s tried to lock %r" % (self.uname, "/" + self.vpath))
|
1619
1630
|
raise Pebkac(401, "authenticate")
|
1620
1631
|
|
1621
1632
|
from xml.etree import ElementTree as ET
|
@@ -1674,12 +1685,14 @@ class HttpCli(object):
|
|
1674
1685
|
def handle_unlock(self) :
|
1675
1686
|
if self.do_log:
|
1676
1687
|
self.log("UNLOCK %s @%s" % (self.req, self.uname))
|
1688
|
+
if "%" in self.req:
|
1689
|
+
self.log(" `-- %r" % (self.vpath,))
|
1677
1690
|
|
1678
1691
|
if self.args.no_dav:
|
1679
1692
|
raise Pebkac(405, "WebDAV is disabled in server config")
|
1680
1693
|
|
1681
1694
|
if not self.can_write and "Microsoft-WebDAV" not in self.ua:
|
1682
|
-
self.log("
|
1695
|
+
self.log("%s tried to lock %r" % (self.uname, "/" + self.vpath))
|
1683
1696
|
raise Pebkac(401, "authenticate")
|
1684
1697
|
|
1685
1698
|
self.send_headers(None, 204)
|
@@ -1691,6 +1704,8 @@ class HttpCli(object):
|
|
1691
1704
|
|
1692
1705
|
if self.do_log:
|
1693
1706
|
self.log("MKCOL %s @%s" % (self.req, self.uname))
|
1707
|
+
if "%" in self.req:
|
1708
|
+
self.log(" `-- %r" % (self.vpath,))
|
1694
1709
|
|
1695
1710
|
try:
|
1696
1711
|
return self._mkdir(self.vpath, True)
|
@@ -1742,6 +1757,8 @@ class HttpCli(object):
|
|
1742
1757
|
def handle_options(self) :
|
1743
1758
|
if self.do_log:
|
1744
1759
|
self.log("OPTIONS %s @%s" % (self.req, self.uname))
|
1760
|
+
if "%" in self.req:
|
1761
|
+
self.log(" `-- %r" % (self.vpath,))
|
1745
1762
|
|
1746
1763
|
oh = self.out_headers
|
1747
1764
|
oh["Allow"] = ", ".join(self.conn.hsrv.mallow)
|
@@ -1757,10 +1774,14 @@ class HttpCli(object):
|
|
1757
1774
|
|
1758
1775
|
def handle_delete(self) :
|
1759
1776
|
self.log("DELETE %s @%s" % (self.req, self.uname))
|
1777
|
+
if "%" in self.req:
|
1778
|
+
self.log(" `-- %r" % (self.vpath,))
|
1760
1779
|
return self.handle_rm([])
|
1761
1780
|
|
1762
1781
|
def handle_put(self) :
|
1763
|
-
self.log("PUT
|
1782
|
+
self.log("PUT %s @%s" % (self.req, self.uname))
|
1783
|
+
if "%" in self.req:
|
1784
|
+
self.log(" `-- %r" % (self.vpath,))
|
1764
1785
|
|
1765
1786
|
if not self.can_write:
|
1766
1787
|
t = "user %s does not have write-access under /%s"
|
@@ -1779,6 +1800,8 @@ class HttpCli(object):
|
|
1779
1800
|
|
1780
1801
|
def handle_post(self) :
|
1781
1802
|
self.log("POST %s @%s" % (self.req, self.uname))
|
1803
|
+
if "%" in self.req:
|
1804
|
+
self.log(" `-- %r" % (self.vpath,))
|
1782
1805
|
|
1783
1806
|
if self.headers.get("expect", "").lower() == "100-continue":
|
1784
1807
|
try:
|
@@ -1823,7 +1846,7 @@ class HttpCli(object):
|
|
1823
1846
|
|
1824
1847
|
if "save" in opt:
|
1825
1848
|
post_sz, _, _, _, path, _ = self.dump_to_file(False)
|
1826
|
-
self.log("urlform:
|
1849
|
+
self.log("urlform: %d bytes, %r" % (post_sz, path))
|
1827
1850
|
elif "print" in opt:
|
1828
1851
|
reader, _ = self.get_body_reader()
|
1829
1852
|
buf = b""
|
@@ -1834,8 +1857,8 @@ class HttpCli(object):
|
|
1834
1857
|
|
1835
1858
|
if buf:
|
1836
1859
|
orig = buf.decode("utf-8", "replace")
|
1837
|
-
t = "urlform_raw
|
1838
|
-
self.log(t
|
1860
|
+
t = "urlform_raw %d @ %r\n %r\n"
|
1861
|
+
self.log(t % (len(orig), "/" + self.vpath, orig))
|
1839
1862
|
try:
|
1840
1863
|
zb = unquote(buf.replace(b"+", b" "))
|
1841
1864
|
plain = zb.decode("utf-8", "replace")
|
@@ -1861,8 +1884,8 @@ class HttpCli(object):
|
|
1861
1884
|
plain,
|
1862
1885
|
)
|
1863
1886
|
|
1864
|
-
t = "urlform_dec
|
1865
|
-
self.log(t
|
1887
|
+
t = "urlform_dec %d @ %r\n %r\n"
|
1888
|
+
self.log(t % (len(plain), "/" + self.vpath, plain))
|
1866
1889
|
|
1867
1890
|
except Exception as ex:
|
1868
1891
|
self.log(repr(ex))
|
@@ -2110,7 +2133,7 @@ class HttpCli(object):
|
|
2110
2133
|
try:
|
2111
2134
|
ext = self.conn.hsrv.magician.ext(path)
|
2112
2135
|
except Exception as ex:
|
2113
|
-
self.log("filetype detection failed for
|
2136
|
+
self.log("filetype detection failed for %r: %s" % (path, ex), 6)
|
2114
2137
|
ext = None
|
2115
2138
|
|
2116
2139
|
if ext:
|
@@ -2210,8 +2233,8 @@ class HttpCli(object):
|
|
2210
2233
|
def handle_stash(self, is_put ) :
|
2211
2234
|
post_sz, sha_hex, sha_b64, remains, path, url = self.dump_to_file(is_put)
|
2212
2235
|
spd = self._spd(post_sz)
|
2213
|
-
t = "
|
2214
|
-
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
|
2215
2238
|
|
2216
2239
|
ac = self.uparam.get(
|
2217
2240
|
"want", self.headers.get("accept", "").lower().split(";")[-1]
|
@@ -2241,7 +2264,7 @@ class HttpCli(object):
|
|
2241
2264
|
flags ,
|
2242
2265
|
) :
|
2243
2266
|
now = time.time()
|
2244
|
-
t = "bad-chunk: %.3f %s %s %d %s %s %
|
2267
|
+
t = "bad-chunk: %.3f %s %s %d %s %s %r"
|
2245
2268
|
t = t % (now, bad_sha, good_sha, ofs, self.ip, self.uname, ap)
|
2246
2269
|
self.log(t, 5)
|
2247
2270
|
|
@@ -2380,7 +2403,7 @@ class HttpCli(object):
|
|
2380
2403
|
body = json.loads(json_buf.decode(enc, "replace"))
|
2381
2404
|
try:
|
2382
2405
|
zds = {k: v for k, v in body.items()}
|
2383
|
-
zds["hash"] = "%d chunks" % (len(body["hash"]))
|
2406
|
+
zds["hash"] = "%d chunks" % (len(body["hash"]),)
|
2384
2407
|
except:
|
2385
2408
|
zds = body
|
2386
2409
|
t = "POST len=%d type=%s ip=%s user=%s req=%r json=%s"
|
@@ -2424,7 +2447,7 @@ class HttpCli(object):
|
|
2424
2447
|
if not bos.path.isdir(dst):
|
2425
2448
|
bos.makedirs(dst)
|
2426
2449
|
except OSError as ex:
|
2427
|
-
self.log("makedirs failed
|
2450
|
+
self.log("makedirs failed %r" % (dst,))
|
2428
2451
|
if not bos.path.isdir(dst):
|
2429
2452
|
if ex.errno == errno.EACCES:
|
2430
2453
|
raise Pebkac(500, "the server OS denied write-access")
|
@@ -2449,7 +2472,7 @@ class HttpCli(object):
|
|
2449
2472
|
# strip common suffix (uploader's folder structure)
|
2450
2473
|
vp_req, vp_vfs = vroots(self.vpath, vjoin(dbv.vpath, vrem))
|
2451
2474
|
if not ret["purl"].startswith(vp_vfs):
|
2452
|
-
t = "share-mapping failed; req
|
2475
|
+
t = "share-mapping failed; req=%r dbv=%r vrem=%r n1=%r n2=%r purl=%r"
|
2453
2476
|
zt = (self.vpath, dbv.vpath, vrem, vp_req, vp_vfs, ret["purl"])
|
2454
2477
|
raise Pebkac(500, t % zt)
|
2455
2478
|
ret["purl"] = vp_req + ret["purl"][len(vp_vfs) :]
|
@@ -2498,13 +2521,13 @@ class HttpCli(object):
|
|
2498
2521
|
# search by query params
|
2499
2522
|
q = body["q"]
|
2500
2523
|
n = body.get("n", self.args.srch_hits)
|
2501
|
-
self.log("qj:
|
2524
|
+
self.log("qj: %r |%d|" % (q, n))
|
2502
2525
|
hits, taglist, trunc = idx.search(self.uname, vols, q, n)
|
2503
2526
|
msg = len(hits)
|
2504
2527
|
|
2505
2528
|
idx.p_end = time.time()
|
2506
2529
|
idx.p_dur = idx.p_end - t0
|
2507
|
-
self.log("q#:
|
2530
|
+
self.log("q#: %r (%.2fs)" % (msg, idx.p_dur))
|
2508
2531
|
|
2509
2532
|
order = []
|
2510
2533
|
for t in self.args.mte:
|
@@ -2615,7 +2638,7 @@ class HttpCli(object):
|
|
2615
2638
|
t = "your client is sending %d bytes which is too much (server expected %d bytes at most)"
|
2616
2639
|
raise Pebkac(400, t % (remains, maxsize))
|
2617
2640
|
|
2618
|
-
t = "writing %
|
2641
|
+
t = "writing %r %s+%d #%d+%d %s"
|
2619
2642
|
chunkno = cstart0[0] // chunksize
|
2620
2643
|
zs = " ".join([chashes[0][:15]] + [x[:9] for x in chashes[1:]])
|
2621
2644
|
self.log(t % (path, cstart0, remains, chunkno, len(chashes), zs))
|
@@ -2727,7 +2750,7 @@ class HttpCli(object):
|
|
2727
2750
|
cinf = self.headers.get("x-up2k-stat", "")
|
2728
2751
|
|
2729
2752
|
spd = self._spd(postsize)
|
2730
|
-
self.log("
|
2753
|
+
self.log("%70s thank %r" % (spd, cinf))
|
2731
2754
|
self.reply(b"thank")
|
2732
2755
|
return True
|
2733
2756
|
|
@@ -2806,7 +2829,7 @@ class HttpCli(object):
|
|
2806
2829
|
logpwd = "%" + ub64enc(zb[:12]).decode("ascii")
|
2807
2830
|
|
2808
2831
|
if pwd != "x":
|
2809
|
-
self.log("invalid password:
|
2832
|
+
self.log("invalid password: %r" % (logpwd,), 3)
|
2810
2833
|
self.cbonk(self.conn.hsrv.gpwd, pwd, "pw", "invalid passwords")
|
2811
2834
|
|
2812
2835
|
msg = "naw dude"
|
@@ -2841,7 +2864,7 @@ class HttpCli(object):
|
|
2841
2864
|
rem = sanitize_vpath(rem, "/")
|
2842
2865
|
fn = vfs.canonical(rem)
|
2843
2866
|
if not fn.startswith(vfs.realpath):
|
2844
|
-
self.log("invalid mkdir
|
2867
|
+
self.log("invalid mkdir %r %r" % (self.gctx, vpath), 1)
|
2845
2868
|
raise Pebkac(422)
|
2846
2869
|
|
2847
2870
|
if not nullwrite:
|
@@ -3006,9 +3029,9 @@ class HttpCli(object):
|
|
3006
3029
|
elif bos.path.exists(abspath):
|
3007
3030
|
try:
|
3008
3031
|
wunlink(self.log, abspath, vfs.flags)
|
3009
|
-
t = "overwriting file with new upload: %
|
3032
|
+
t = "overwriting file with new upload: %r"
|
3010
3033
|
except:
|
3011
|
-
t = "toctou while deleting for ?replace: %
|
3034
|
+
t = "toctou while deleting for ?replace: %r"
|
3012
3035
|
self.log(t % (abspath,))
|
3013
3036
|
else:
|
3014
3037
|
open_args = {}
|
@@ -3091,7 +3114,7 @@ class HttpCli(object):
|
|
3091
3114
|
f, tnam = ren_open(tnam, "wb", self.args.iobuf, **open_args)
|
3092
3115
|
try:
|
3093
3116
|
tabspath = os.path.join(fdir, tnam)
|
3094
|
-
self.log("writing to
|
3117
|
+
self.log("writing to %r" % (tabspath,))
|
3095
3118
|
sz, sha_hex, sha_b64 = copier(
|
3096
3119
|
p_data, f, hasher, max_sz, self.args.s_wr_slp
|
3097
3120
|
)
|
@@ -3276,7 +3299,7 @@ class HttpCli(object):
|
|
3276
3299
|
jmsg["files"].append(jpart)
|
3277
3300
|
|
3278
3301
|
vspd = self._spd(sz_total, False)
|
3279
|
-
self.log("
|
3302
|
+
self.log("%s %r" % (vspd, msg))
|
3280
3303
|
|
3281
3304
|
suf = ""
|
3282
3305
|
if not nullwrite and self.args.write_uplog:
|
@@ -3537,7 +3560,7 @@ class HttpCli(object):
|
|
3537
3560
|
if req == zs:
|
3538
3561
|
return True
|
3539
3562
|
|
3540
|
-
t = "wrong dirkey, want %s, got %s\n vp: %
|
3563
|
+
t = "wrong dirkey, want %s, got %s\n vp: %r\n ap: %r"
|
3541
3564
|
self.log(t % (zs, req, self.req, ap), 6)
|
3542
3565
|
return False
|
3543
3566
|
|
@@ -3565,7 +3588,7 @@ class HttpCli(object):
|
|
3565
3588
|
if req == zs:
|
3566
3589
|
return True
|
3567
3590
|
|
3568
|
-
t = "wrong filekey, want %s, got %s\n vp: %
|
3591
|
+
t = "wrong filekey, want %s, got %s\n vp: %r\n ap: %r"
|
3569
3592
|
self.log(t % (zs, req, self.req, ap), 6)
|
3570
3593
|
return False
|
3571
3594
|
|
@@ -3627,7 +3650,7 @@ class HttpCli(object):
|
|
3627
3650
|
elif ph == "srv.htime":
|
3628
3651
|
sv = datetime.now(UTC).strftime("%Y-%m-%d, %H:%M:%S")
|
3629
3652
|
else:
|
3630
|
-
self.log("unknown placeholder in server config: [%s]" % (ph), 3)
|
3653
|
+
self.log("unknown placeholder in server config: [%s]" % (ph,), 3)
|
3631
3654
|
continue
|
3632
3655
|
|
3633
3656
|
sv = self.conn.hsrv.ptn_hsafe.sub("_", sv)
|
@@ -3784,7 +3807,7 @@ class HttpCli(object):
|
|
3784
3807
|
self.pipes.set(req_path, job)
|
3785
3808
|
except Exception as ex:
|
3786
3809
|
if getattr(ex, "errno", 0) != errno.ENOENT:
|
3787
|
-
self.log("will not pipe
|
3810
|
+
self.log("will not pipe %r; %s" % (ap_data, ex), 6)
|
3788
3811
|
ptop = None
|
3789
3812
|
|
3790
3813
|
#
|
@@ -4072,7 +4095,7 @@ class HttpCli(object):
|
|
4072
4095
|
if lower >= data_end:
|
4073
4096
|
if data_end:
|
4074
4097
|
t = "pipe: uploader is too slow; aborting download at %.2f MiB"
|
4075
|
-
self.log(t % (data_end / M))
|
4098
|
+
self.log(t % (data_end / M,))
|
4076
4099
|
raise Pebkac(416, "uploader is too slow")
|
4077
4100
|
|
4078
4101
|
raise Pebkac(416, "no data available yet; please retry in a bit")
|
@@ -4216,7 +4239,7 @@ class HttpCli(object):
|
|
4216
4239
|
|
4217
4240
|
cdis = "attachment; filename=\"{}.{}\"; filename*=UTF-8''{}.{}"
|
4218
4241
|
cdis = cdis.format(afn, ext, ufn, ext)
|
4219
|
-
self.log(cdis)
|
4242
|
+
self.log(repr(cdis))
|
4220
4243
|
self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis})
|
4221
4244
|
|
4222
4245
|
fgen = vn.zipgen(
|
@@ -4878,9 +4901,9 @@ class HttpCli(object):
|
|
4878
4901
|
raise Pebkac(500, "sqlite3 not found on server; unpost is disabled")
|
4879
4902
|
raise Pebkac(500, "server busy, cannot unpost; please retry in a bit")
|
4880
4903
|
|
4881
|
-
|
4882
|
-
|
4883
|
-
|
4904
|
+
zs = self.uparam.get("filter") or ""
|
4905
|
+
filt = re.compile(zs, re.I) if zs else None
|
4906
|
+
lm = "ups %r" % (zs,)
|
4884
4907
|
|
4885
4908
|
if self.args.shr and self.vpath.startswith(self.args.shr1):
|
4886
4909
|
shr_dbv, shr_vrem = self.vn.get_dbv(self.rem)
|
@@ -4921,13 +4944,18 @@ class HttpCli(object):
|
|
4921
4944
|
|
4922
4945
|
nfk, fk_alg = fk_vols.get(vol) or (0, 0)
|
4923
4946
|
|
4924
|
-
|
4947
|
+
n = 2000
|
4948
|
+
q = "select sz, rd, fn, at from up where ip=? and at>? order by at desc"
|
4925
4949
|
for sz, rd, fn, at in cur.execute(q, (self.ip, lim)):
|
4926
4950
|
vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
|
4927
|
-
if filt and
|
4951
|
+
if filt and not filt.search(vp):
|
4928
4952
|
continue
|
4929
4953
|
|
4930
|
-
|
4954
|
+
n -= 1
|
4955
|
+
if not n:
|
4956
|
+
break
|
4957
|
+
|
4958
|
+
rv = {"vp": vp, "sz": sz, "at": at, "nfk": nfk}
|
4931
4959
|
if nfk:
|
4932
4960
|
rv["ap"] = vol.canonical(vjoin(rd, fn))
|
4933
4961
|
rv["fk_alg"] = fk_alg
|
@@ -4937,9 +4965,13 @@ class HttpCli(object):
|
|
4937
4965
|
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
4938
4966
|
ret = ret[:2000]
|
4939
4967
|
|
4968
|
+
if len(ret) > 2000:
|
4969
|
+
ret = ret[:2000]
|
4970
|
+
|
4940
4971
|
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
4941
|
-
|
4942
|
-
for rv in ret
|
4972
|
+
|
4973
|
+
for rv in ret:
|
4974
|
+
rv["vp"] = quotep(rv["vp"])
|
4943
4975
|
nfk = rv.pop("nfk")
|
4944
4976
|
if not nfk:
|
4945
4977
|
continue
|
@@ -4956,12 +4988,6 @@ class HttpCli(object):
|
|
4956
4988
|
)
|
4957
4989
|
rv["vp"] += "?k=" + fk[:nfk]
|
4958
4990
|
|
4959
|
-
n += 1
|
4960
|
-
if n > 2000:
|
4961
|
-
break
|
4962
|
-
|
4963
|
-
ret = ret[:2000]
|
4964
|
-
|
4965
4991
|
if shr_dbv:
|
4966
4992
|
# translate vpaths from share-target to share-url
|
4967
4993
|
# to satisfy access checks
|
@@ -4984,6 +5010,125 @@ class HttpCli(object):
|
|
4984
5010
|
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
4985
5011
|
return True
|
4986
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
|
+
|
4987
5132
|
def tx_shares(self) :
|
4988
5133
|
if self.uname == "*":
|
4989
5134
|
self.loud_reply("you're not logged in")
|
@@ -5689,7 +5834,7 @@ class HttpCli(object):
|
|
5689
5834
|
linf = stats.get(fn) or bos.lstat(fspath)
|
5690
5835
|
inf = bos.stat(fspath) if stat.S_ISLNK(linf.st_mode) else linf
|
5691
5836
|
except:
|
5692
|
-
self.log("broken symlink:
|
5837
|
+
self.log("broken symlink: %r" % (fspath,))
|
5693
5838
|
continue
|
5694
5839
|
|
5695
5840
|
is_dir = stat.S_ISDIR(inf.st_mode)
|
@@ -5803,8 +5948,7 @@ class HttpCli(object):
|
|
5803
5948
|
erd_efn = s3enc(idx.mem_cur, rd, fn)
|
5804
5949
|
r = icur.execute(q, erd_efn)
|
5805
5950
|
except:
|
5806
|
-
|
5807
|
-
self.log(t.format(rd, fn, min_ex()))
|
5951
|
+
self.log("tag read error, %r / %r\n%s" % (rd, fn, min_ex()))
|
5808
5952
|
break
|
5809
5953
|
|
5810
5954
|
tags = {k: v for k, v in r}
|
@@ -5924,10 +6068,10 @@ class HttpCli(object):
|
|
5924
6068
|
if doc.lower().endswith(".md") and "exp" in vn.flags:
|
5925
6069
|
doctxt = self._expand(doctxt, vn.flags.get("exp_md") or [])
|
5926
6070
|
else:
|
5927
|
-
self.log("doc 2big:
|
6071
|
+
self.log("doc 2big: %r" % (doc,), 6)
|
5928
6072
|
doctxt = "( size of textfile exceeds serverside limit )"
|
5929
6073
|
else:
|
5930
|
-
self.log("doc 404:
|
6074
|
+
self.log("doc 404: %r" % (doc,), 6)
|
5931
6075
|
doctxt = "( textfile not found )"
|
5932
6076
|
|
5933
6077
|
if doctxt is not None:
|