copyparty 1.16.6__py3-none-any.whl → 1.16.8__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/__version__.py +2 -2
- copyparty/authsrv.py +2 -2
- copyparty/httpcli.py +116 -53
- copyparty/httpsrv.py +0 -4
- copyparty/tcpsrv.py +3 -3
- copyparty/up2k.py +21 -7
- copyparty/web/a/u2c.py +1 -1
- copyparty/web/browser.html +1 -1
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/md.html +2 -2
- copyparty/web/mde.html +2 -2
- copyparty/web/rups.css.gz +0 -0
- copyparty/web/rups.html +8 -25
- copyparty/web/rups.js.gz +0 -0
- copyparty/web/shares.html +4 -3
- copyparty/web/splash.css.gz +0 -0
- copyparty/web/splash.html +1 -1
- copyparty/web/svcs.html +65 -4
- copyparty/web/svcs.js.gz +0 -0
- copyparty/web/ui.css.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.16.6.dist-info → copyparty-1.16.8.dist-info}/METADATA +50 -7
- {copyparty-1.16.6.dist-info → copyparty-1.16.8.dist-info}/RECORD +28 -28
- {copyparty-1.16.6.dist-info → copyparty-1.16.8.dist-info}/WHEEL +1 -1
- {copyparty-1.16.6.dist-info → copyparty-1.16.8.dist-info}/LICENSE +0 -0
- {copyparty-1.16.6.dist-info → copyparty-1.16.8.dist-info}/entry_points.txt +0 -0
- {copyparty-1.16.6.dist-info → copyparty-1.16.8.dist-info}/top_level.txt +0 -0
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -2174,11 +2174,11 @@ class AuthSrv(object):
|
|
2174
2174
|
if not self.args.no_voldump:
|
2175
2175
|
self.log(t)
|
2176
2176
|
|
2177
|
-
if have_e2d:
|
2177
|
+
if have_e2d or self.args.idp_h_usr:
|
2178
2178
|
t = self.chk_sqlite_threadsafe()
|
2179
2179
|
if t:
|
2180
2180
|
self.log("\n\033[{}\033[0m\n".format(t))
|
2181
|
-
|
2181
|
+
if have_e2d:
|
2182
2182
|
if not have_e2t:
|
2183
2183
|
t = "hint: enable multimedia indexing (artist/title/...) with argument -e2ts"
|
2184
2184
|
self.log(t, 6)
|
copyparty/httpcli.py
CHANGED
@@ -141,6 +141,14 @@ A_FILE = os.stat_result(
|
|
141
141
|
(0o644, -1, -1, 1, 1000, 1000, 8, 0x39230101, 0x39230101, 0x39230101)
|
142
142
|
)
|
143
143
|
|
144
|
+
RE_CC = re.compile(r"[\x00-\x1f]") # search always faster
|
145
|
+
RE_HSAFE = re.compile(r"[\x00-\x1f<>\"'&]") # search always much faster
|
146
|
+
RE_HOST = re.compile(r"[^][0-9a-zA-Z.:_-]") # search faster <=17ch
|
147
|
+
RE_MHOST = re.compile(r"^[][0-9a-zA-Z.:_-]+$") # match faster >=18ch
|
148
|
+
RE_K = re.compile(r"[^0-9a-zA-Z_-]") # search faster <=17ch
|
149
|
+
|
150
|
+
UPARAM_CC_OK = set("doc move tree".split())
|
151
|
+
|
144
152
|
|
145
153
|
class HttpCli(object):
|
146
154
|
"""
|
@@ -384,6 +392,15 @@ class HttpCli(object):
|
|
384
392
|
self.host = self.headers.get("x-forwarded-host") or self.host
|
385
393
|
trusted_xff = True
|
386
394
|
|
395
|
+
m = RE_HOST.search(self.host)
|
396
|
+
if m and self.host != self.args.name:
|
397
|
+
zs = self.host
|
398
|
+
t = "malicious user; illegal Host header; req(%r) host(%r) => %r"
|
399
|
+
self.log(t % (self.req, zs, zs[m.span()[0] :]), 1)
|
400
|
+
self.cbonk(self.conn.hsrv.gmal, zs, "bad_host", "illegal Host header")
|
401
|
+
self.terse_reply(b"illegal Host header", 400)
|
402
|
+
return False
|
403
|
+
|
387
404
|
if self.is_banned():
|
388
405
|
return False
|
389
406
|
|
@@ -429,6 +446,16 @@ class HttpCli(object):
|
|
429
446
|
self.loud_reply(t, status=400)
|
430
447
|
return False
|
431
448
|
|
449
|
+
ptn_cc = RE_CC
|
450
|
+
m = ptn_cc.search(self.req)
|
451
|
+
if m:
|
452
|
+
zs = self.req
|
453
|
+
t = "malicious user; Cc in req0 %r => %r"
|
454
|
+
self.log(t % (zs, zs[m.span()[0] :]), 1)
|
455
|
+
self.cbonk(self.conn.hsrv.gmal, zs, "cc_r0", "Cc in req0")
|
456
|
+
self.terse_reply(b"", 500)
|
457
|
+
return False
|
458
|
+
|
432
459
|
# split req into vpath + uparam
|
433
460
|
uparam = {}
|
434
461
|
if "?" not in self.req:
|
@@ -441,8 +468,8 @@ class HttpCli(object):
|
|
441
468
|
self.trailing_slash = vpath.endswith("/")
|
442
469
|
vpath = undot(vpath)
|
443
470
|
|
444
|
-
|
445
|
-
k_safe =
|
471
|
+
re_k = RE_K
|
472
|
+
k_safe = UPARAM_CC_OK
|
446
473
|
for k in arglist.split("&"):
|
447
474
|
if "=" in k:
|
448
475
|
k, zs = k.split("=", 1)
|
@@ -452,6 +479,14 @@ class HttpCli(object):
|
|
452
479
|
else:
|
453
480
|
sv = ""
|
454
481
|
|
482
|
+
m = re_k.search(k)
|
483
|
+
if m:
|
484
|
+
t = "malicious user; bad char in query key; req(%r) qk(%r) => %r"
|
485
|
+
self.log(t % (self.req, k, k[m.span()[0] :]), 1)
|
486
|
+
self.cbonk(self.conn.hsrv.gmal, self.req, "bc_q", "illegal qkey")
|
487
|
+
self.terse_reply(b"", 500)
|
488
|
+
return False
|
489
|
+
|
455
490
|
k = k.lower()
|
456
491
|
uparam[k] = sv
|
457
492
|
|
@@ -459,17 +494,26 @@ class HttpCli(object):
|
|
459
494
|
continue
|
460
495
|
|
461
496
|
zs = "%s=%s" % (k, sv)
|
462
|
-
m =
|
497
|
+
m = ptn_cc.search(zs)
|
463
498
|
if not m:
|
464
499
|
continue
|
465
500
|
|
466
|
-
|
467
|
-
t
|
468
|
-
self.log(t.format(self.req, hit), 1)
|
501
|
+
t = "malicious user; Cc in query; req(%r) qp(%r) => %r"
|
502
|
+
self.log(t % (self.req, zs, zs[m.span()[0] :]), 1)
|
469
503
|
self.cbonk(self.conn.hsrv.gmal, self.req, "cc_q", "Cc in query")
|
470
504
|
self.terse_reply(b"", 500)
|
471
505
|
return False
|
472
506
|
|
507
|
+
if "k" in uparam:
|
508
|
+
m = RE_K.search(uparam["k"])
|
509
|
+
if m:
|
510
|
+
zs = uparam["k"]
|
511
|
+
t = "malicious user; illegal filekey; req(%r) k(%r) => %r"
|
512
|
+
self.log(t % (self.req, zs, zs[m.span()[0] :]), 1)
|
513
|
+
self.cbonk(self.conn.hsrv.gmal, zs, "bad_k", "illegal filekey")
|
514
|
+
self.terse_reply(b"illegal filekey", 400)
|
515
|
+
return False
|
516
|
+
|
473
517
|
if self.is_vproxied:
|
474
518
|
if vpath.startswith(self.args.R):
|
475
519
|
vpath = vpath[len(self.args.R) + 1 :]
|
@@ -513,7 +557,7 @@ class HttpCli(object):
|
|
513
557
|
return self.tx_qr()
|
514
558
|
|
515
559
|
if relchk(self.vpath) and (self.vpath != "*" or self.mode != "OPTIONS"):
|
516
|
-
self.log("
|
560
|
+
self.log("illegal relpath; req(%r) => %r" % (self.req, "/" + self.vpath))
|
517
561
|
self.cbonk(self.conn.hsrv.gmal, self.req, "bad_vp", "invalid relpaths")
|
518
562
|
return self.tx_404() and self.keepalive
|
519
563
|
|
@@ -866,12 +910,12 @@ class HttpCli(object):
|
|
866
910
|
for k, zs in list(self.out_headers.items()) + self.out_headerlist:
|
867
911
|
response.append("%s: %s" % (k, zs))
|
868
912
|
|
913
|
+
ptn_cc = RE_CC
|
869
914
|
for zs in response:
|
870
|
-
m =
|
915
|
+
m = ptn_cc.search(zs)
|
871
916
|
if m:
|
872
|
-
|
873
|
-
t
|
874
|
-
self.log(t.format(zs, hit), 1)
|
917
|
+
t = "malicious user; Cc in out-hdr; req(%r) hdr(%r) => %r"
|
918
|
+
self.log(t % (self.req, zs, zs[m.span()[0] :]), 1)
|
875
919
|
self.cbonk(self.conn.hsrv.gmal, zs, "cc_hdr", "Cc in out-hdr")
|
876
920
|
raise Pebkac(999)
|
877
921
|
|
@@ -997,7 +1041,7 @@ class HttpCli(object):
|
|
997
1041
|
if not kv:
|
998
1042
|
return ""
|
999
1043
|
|
1000
|
-
r = ["%s=%s" % (k, quotep(zs)) if zs else k for k, zs in kv.items()]
|
1044
|
+
r = ["%s=%s" % (quotep(k), quotep(zs)) if zs else k for k, zs in kv.items()]
|
1001
1045
|
return "?" + "&".join(r)
|
1002
1046
|
|
1003
1047
|
def ourlq(self) :
|
@@ -1149,8 +1193,8 @@ class HttpCli(object):
|
|
1149
1193
|
return self.tx_res(res_path)
|
1150
1194
|
|
1151
1195
|
if res_path != undot(res_path):
|
1152
|
-
t = "malicious user; attempted path traversal %r => %r"
|
1153
|
-
self.log(t % ("/" + self.vpath, res_path), 1)
|
1196
|
+
t = "malicious user; attempted path traversal; req(%r) vp(%r) => %r"
|
1197
|
+
self.log(t % (self.req, "/" + self.vpath, res_path), 1)
|
1154
1198
|
self.cbonk(self.conn.hsrv.gmal, self.req, "trav", "path traversal")
|
1155
1199
|
|
1156
1200
|
self.tx_404()
|
@@ -1293,8 +1337,8 @@ class HttpCli(object):
|
|
1293
1337
|
|
1294
1338
|
pw = self.ouparam.get("pw")
|
1295
1339
|
if pw:
|
1296
|
-
q_pw = "?pw=%s" % (pw,)
|
1297
|
-
a_pw = "&pw=%s" % (pw,)
|
1340
|
+
q_pw = "?pw=%s" % (html_escape(pw, True, True),)
|
1341
|
+
a_pw = "&pw=%s" % (html_escape(pw, True, True),)
|
1298
1342
|
for i in hits:
|
1299
1343
|
i["rp"] += a_pw if "?" in i["rp"] else q_pw
|
1300
1344
|
else:
|
@@ -1655,7 +1699,7 @@ class HttpCli(object):
|
|
1655
1699
|
|
1656
1700
|
token = str(uuid.uuid4())
|
1657
1701
|
|
1658
|
-
if
|
1702
|
+
if lk.find(r"./{DAV:}depth") is None:
|
1659
1703
|
depth = self.headers.get("depth", "infinity")
|
1660
1704
|
lk.append(mktnod("D:depth", depth))
|
1661
1705
|
|
@@ -1845,7 +1889,7 @@ class HttpCli(object):
|
|
1845
1889
|
return self.handle_stash(False)
|
1846
1890
|
|
1847
1891
|
if "save" in opt:
|
1848
|
-
post_sz, _, _, _, path, _ = self.dump_to_file(False)
|
1892
|
+
post_sz, _, _, _, _, path, _ = self.dump_to_file(False)
|
1849
1893
|
self.log("urlform: %d bytes, %r" % (post_sz, path))
|
1850
1894
|
elif "print" in opt:
|
1851
1895
|
reader, _ = self.get_body_reader()
|
@@ -1926,11 +1970,11 @@ class HttpCli(object):
|
|
1926
1970
|
else:
|
1927
1971
|
return read_socket(self.sr, bufsz, remains), remains
|
1928
1972
|
|
1929
|
-
def dump_to_file(self, is_put )
|
1930
|
-
# post_sz, sha_hex, sha_b64, remains, path, url
|
1973
|
+
def dump_to_file(self, is_put ) :
|
1974
|
+
# post_sz, halg, sha_hex, sha_b64, remains, path, url
|
1931
1975
|
reader, remains = self.get_body_reader()
|
1932
1976
|
vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True)
|
1933
|
-
rnd,
|
1977
|
+
rnd, lifetime, xbu, xau = self.upload_flags(vfs)
|
1934
1978
|
lim = vfs.get_dbv(rem)[0].lim
|
1935
1979
|
fdir = vfs.canonical(rem)
|
1936
1980
|
if lim:
|
@@ -2078,12 +2122,14 @@ class HttpCli(object):
|
|
2078
2122
|
# small toctou, but better than clobbering a hardlink
|
2079
2123
|
wunlink(self.log, path, vfs.flags)
|
2080
2124
|
|
2125
|
+
halg = "sha512"
|
2081
2126
|
hasher = None
|
2082
2127
|
copier = hashcopy
|
2083
2128
|
if "ck" in self.ouparam or "ck" in self.headers:
|
2084
|
-
zs = self.ouparam.get("ck") or self.headers.get("ck") or ""
|
2129
|
+
halg = zs = self.ouparam.get("ck") or self.headers.get("ck") or ""
|
2085
2130
|
if not zs or zs == "no":
|
2086
2131
|
copier = justcopy
|
2132
|
+
halg = ""
|
2087
2133
|
elif zs == "md5":
|
2088
2134
|
hasher = hashlib.md5(**USED4SEC)
|
2089
2135
|
elif zs == "sha1":
|
@@ -2117,7 +2163,7 @@ class HttpCli(object):
|
|
2117
2163
|
raise
|
2118
2164
|
|
2119
2165
|
if self.args.nw:
|
2120
|
-
return post_sz, sha_hex, sha_b64, remains, path, ""
|
2166
|
+
return post_sz, halg, sha_hex, sha_b64, remains, path, ""
|
2121
2167
|
|
2122
2168
|
at = mt = time.time() - lifetime
|
2123
2169
|
cli_mt = self.headers.get("x-oc-mtime")
|
@@ -2228,19 +2274,30 @@ class HttpCli(object):
|
|
2228
2274
|
self.args.RS + vpath + vsuf,
|
2229
2275
|
)
|
2230
2276
|
|
2231
|
-
return post_sz, sha_hex, sha_b64, remains, path, url
|
2277
|
+
return post_sz, halg, sha_hex, sha_b64, remains, path, url
|
2232
2278
|
|
2233
2279
|
def handle_stash(self, is_put ) :
|
2234
|
-
post_sz, sha_hex, sha_b64, remains, path, url = self.dump_to_file(is_put)
|
2280
|
+
post_sz, halg, sha_hex, sha_b64, remains, path, url = self.dump_to_file(is_put)
|
2235
2281
|
spd = self._spd(post_sz)
|
2236
2282
|
t = "%s wrote %d/%d bytes to %r # %s"
|
2237
2283
|
self.log(t % (spd, post_sz, remains, path, sha_b64[:28])) # 21
|
2238
2284
|
|
2239
|
-
|
2240
|
-
|
2241
|
-
|
2285
|
+
mime = "text/plain; charset=utf-8"
|
2286
|
+
ac = self.uparam.get("want") or self.headers.get("accept") or ""
|
2287
|
+
if ac:
|
2288
|
+
ac = ac.split(";", 1)[0].lower()
|
2289
|
+
if ac == "application/json":
|
2290
|
+
ac = "json"
|
2242
2291
|
if ac == "url":
|
2243
2292
|
t = url
|
2293
|
+
elif ac == "json" or "j" in self.uparam:
|
2294
|
+
jmsg = {"fileurl": url, "filesz": post_sz}
|
2295
|
+
if halg:
|
2296
|
+
jmsg[halg] = sha_hex[:56]
|
2297
|
+
jmsg["sha_b64"] = sha_b64
|
2298
|
+
|
2299
|
+
mime = "application/json"
|
2300
|
+
t = json.dumps(jmsg, indent=2, sort_keys=True)
|
2244
2301
|
else:
|
2245
2302
|
t = "{}\n{}\n{}\n{}\n".format(post_sz, sha_b64, sha_hex[:56], url)
|
2246
2303
|
|
@@ -2250,7 +2307,7 @@ class HttpCli(object):
|
|
2250
2307
|
h["X-OC-MTime"] = "accepted"
|
2251
2308
|
t = "" # some webdav clients expect/prefer this
|
2252
2309
|
|
2253
|
-
self.reply(t.encode("utf-8"), 201, headers=h)
|
2310
|
+
self.reply(t.encode("utf-8", "replace"), 201, mime=mime, headers=h)
|
2254
2311
|
return True
|
2255
2312
|
|
2256
2313
|
def bakflip(
|
@@ -2923,7 +2980,7 @@ class HttpCli(object):
|
|
2923
2980
|
self.redirect(vpath, "?edit")
|
2924
2981
|
return True
|
2925
2982
|
|
2926
|
-
def upload_flags(self, vfs )
|
2983
|
+
def upload_flags(self, vfs ) :
|
2927
2984
|
if self.args.nw:
|
2928
2985
|
rnd = 0
|
2929
2986
|
else:
|
@@ -2931,10 +2988,6 @@ class HttpCli(object):
|
|
2931
2988
|
if vfs.flags.get("rand"): # force-enable
|
2932
2989
|
rnd = max(rnd, vfs.flags["nrand"])
|
2933
2990
|
|
2934
|
-
ac = self.uparam.get(
|
2935
|
-
"want", self.headers.get("accept", "").lower().split(";")[-1]
|
2936
|
-
)
|
2937
|
-
want_url = ac == "url"
|
2938
2991
|
zs = self.uparam.get("life", self.headers.get("life", ""))
|
2939
2992
|
if zs:
|
2940
2993
|
vlife = vfs.flags.get("lifetime") or 0
|
@@ -2944,7 +2997,6 @@ class HttpCli(object):
|
|
2944
2997
|
|
2945
2998
|
return (
|
2946
2999
|
rnd,
|
2947
|
-
want_url,
|
2948
3000
|
lifetime,
|
2949
3001
|
vfs.flags.get("xbu") or [],
|
2950
3002
|
vfs.flags.get("xau") or [],
|
@@ -2997,7 +3049,14 @@ class HttpCli(object):
|
|
2997
3049
|
if not nullwrite:
|
2998
3050
|
bos.makedirs(fdir_base)
|
2999
3051
|
|
3000
|
-
rnd,
|
3052
|
+
rnd, lifetime, xbu, xau = self.upload_flags(vfs)
|
3053
|
+
zs = self.uparam.get("want") or self.headers.get("accept") or ""
|
3054
|
+
if zs:
|
3055
|
+
zs = zs.split(";", 1)[0].lower()
|
3056
|
+
if zs == "application/json":
|
3057
|
+
zs = "json"
|
3058
|
+
want_url = zs == "url"
|
3059
|
+
want_json = zs == "json" or "j" in self.uparam
|
3001
3060
|
|
3002
3061
|
files = []
|
3003
3062
|
# sz, sha_hex, sha_b64, p_file, fname, abspath
|
@@ -3319,7 +3378,9 @@ class HttpCli(object):
|
|
3319
3378
|
msg += "\n" + errmsg
|
3320
3379
|
|
3321
3380
|
self.reply(msg.encode("utf-8", "replace"), status=sc)
|
3322
|
-
elif
|
3381
|
+
elif want_json:
|
3382
|
+
if len(jmsg["files"]) == 1:
|
3383
|
+
jmsg["fileurl"] = jmsg["files"][0]["url"]
|
3323
3384
|
jtxt = json.dumps(jmsg, indent=2, sort_keys=True).encode("utf-8", "replace")
|
3324
3385
|
self.reply(jtxt, mime="application/json", status=sc)
|
3325
3386
|
else:
|
@@ -3636,6 +3697,7 @@ class HttpCli(object):
|
|
3636
3697
|
return logues, readmes
|
3637
3698
|
|
3638
3699
|
def _expand(self, txt , phs ) :
|
3700
|
+
ptn_hsafe = RE_HSAFE
|
3639
3701
|
for ph in phs:
|
3640
3702
|
if ph.startswith("hdr."):
|
3641
3703
|
sv = str(self.headers.get(ph[4:], ""))
|
@@ -3653,7 +3715,7 @@ class HttpCli(object):
|
|
3653
3715
|
self.log("unknown placeholder in server config: [%s]" % (ph,), 3)
|
3654
3716
|
continue
|
3655
3717
|
|
3656
|
-
sv =
|
3718
|
+
sv = ptn_hsafe.sub("_", sv)
|
3657
3719
|
txt = txt.replace("{{%s}}" % (ph,), sv)
|
3658
3720
|
|
3659
3721
|
return txt
|
@@ -4486,12 +4548,12 @@ class HttpCli(object):
|
|
4486
4548
|
else self.conn.hsrv.nm.map(self.ip) or host
|
4487
4549
|
)
|
4488
4550
|
# safer than html_escape/quotep since this avoids both XSS and shell-stuff
|
4489
|
-
pw = re.sub(r"[<>&$?`\"']", "_", self.pw or "
|
4551
|
+
pw = re.sub(r"[<>&$?`\"']", "_", self.pw or "hunter2")
|
4490
4552
|
vp = re.sub(r"[<>&$?`\"']", "_", self.uparam["hc"] or "").lstrip("/")
|
4491
4553
|
pw = pw.replace(" ", "%20")
|
4492
4554
|
vp = vp.replace(" ", "%20")
|
4493
4555
|
if pw in self.asrv.sesa:
|
4494
|
-
pw = "
|
4556
|
+
pw = "hunter2"
|
4495
4557
|
|
4496
4558
|
html = self.j2s(
|
4497
4559
|
"svcs",
|
@@ -4965,11 +5027,11 @@ class HttpCli(object):
|
|
4965
5027
|
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
4966
5028
|
ret = ret[:2000]
|
4967
5029
|
|
5030
|
+
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
5031
|
+
|
4968
5032
|
if len(ret) > 2000:
|
4969
5033
|
ret = ret[:2000]
|
4970
5034
|
|
4971
|
-
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
4972
|
-
|
4973
5035
|
for rv in ret:
|
4974
5036
|
rv["vp"] = quotep(rv["vp"])
|
4975
5037
|
nfk = rv.pop("nfk")
|
@@ -5057,10 +5119,6 @@ class HttpCli(object):
|
|
5057
5119
|
if not dots and "/." in vp:
|
5058
5120
|
continue
|
5059
5121
|
|
5060
|
-
n -= 1
|
5061
|
-
if not n:
|
5062
|
-
break
|
5063
|
-
|
5064
5122
|
rv = {
|
5065
5123
|
"vp": vp,
|
5066
5124
|
"sz": sz,
|
@@ -5078,13 +5136,17 @@ class HttpCli(object):
|
|
5078
5136
|
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
5079
5137
|
ret = ret[:1000]
|
5080
5138
|
|
5081
|
-
|
5082
|
-
|
5139
|
+
n -= 1
|
5140
|
+
if not n:
|
5141
|
+
break
|
5083
5142
|
|
5084
5143
|
ret.sort(key=lambda x: x["at"], reverse=True) # type: ignore
|
5085
5144
|
|
5145
|
+
if len(ret) > 1000:
|
5146
|
+
ret = ret[:1000]
|
5147
|
+
|
5086
5148
|
for rv in ret:
|
5087
|
-
rv["
|
5149
|
+
rv["vp"] = quotep(rv["vp"])
|
5088
5150
|
nfk = rv.pop("nfk")
|
5089
5151
|
if not nfk:
|
5090
5152
|
continue
|
@@ -5117,15 +5179,16 @@ class HttpCli(object):
|
|
5117
5179
|
for v in ret:
|
5118
5180
|
v["vp"] = self.args.SR + v["vp"]
|
5119
5181
|
|
5120
|
-
|
5182
|
+
now = time.time()
|
5183
|
+
self.log("%s #%d %.2fsec" % (lm, len(ret), now - t0))
|
5121
5184
|
|
5185
|
+
ret2 = {"now": int(now), "filter": sfilt, "ups": ret}
|
5186
|
+
jtxt = json.dumps(ret2, separators=(",\n", ": "))
|
5122
5187
|
if "j" in self.ouparam:
|
5123
|
-
jtxt = json.dumps(ret, separators=(",\n", ": "))
|
5124
5188
|
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
5125
5189
|
return True
|
5126
5190
|
|
5127
|
-
|
5128
|
-
html = self.j2s("rups", this=self, rows=rows, filt=sfilt, now=int(time.time()))
|
5191
|
+
html = self.j2s("rups", this=self, v=jtxt)
|
5129
5192
|
self.reply(html.encode("utf-8"), status=200)
|
5130
5193
|
return True
|
5131
5194
|
|
copyparty/httpsrv.py
CHANGED
@@ -191,10 +191,6 @@ class HttpSrv(object):
|
|
191
191
|
self.xff_nm = build_netmap(self.args.xff_src)
|
192
192
|
self.xff_lan = build_netmap("lan")
|
193
193
|
|
194
|
-
self.ptn_cc = re.compile(r"[\x00-\x1f]")
|
195
|
-
self.ptn_hsafe = re.compile(r"[\x00-\x1f<>\"'&]")
|
196
|
-
self.uparam_cc_ok = set("doc move tree".split())
|
197
|
-
|
198
194
|
self.mallow = "GET HEAD POST PUT DELETE OPTIONS".split()
|
199
195
|
if not self.args.no_dav:
|
200
196
|
zs = "PROPFIND PROPPATCH LOCK UNLOCK MKCOL COPY MOVE"
|
copyparty/tcpsrv.py
CHANGED
@@ -403,12 +403,12 @@ class TcpSrv(object):
|
|
403
403
|
rem = []
|
404
404
|
for k, v in netdevs.items():
|
405
405
|
if k not in self.netdevs:
|
406
|
-
add.append("\n added %s = %s" % (k, v))
|
406
|
+
add.append("\n\033[32m added %s = %s" % (k, v))
|
407
407
|
for k, v in self.netdevs.items():
|
408
408
|
if k not in netdevs:
|
409
|
-
rem.append("\
|
409
|
+
rem.append("\n\033[33mremoved %s = %s" % (k, v))
|
410
410
|
|
411
|
-
t = "network change detected
|
411
|
+
t = "network change detected:%s%s"
|
412
412
|
self.log("tcpsrv", t % ("".join(add), "".join(rem)), 3)
|
413
413
|
self.netdevs = netdevs
|
414
414
|
self._distribute_netdevs()
|
copyparty/up2k.py
CHANGED
@@ -851,9 +851,9 @@ class Up2k(object):
|
|
851
851
|
self.iacct = self.asrv.iacct
|
852
852
|
self.grps = self.asrv.grps
|
853
853
|
|
854
|
+
have_e2d = self.args.idp_h_usr or self.args.chpw or self.args.shr
|
854
855
|
vols = list(all_vols.values())
|
855
856
|
t0 = time.time()
|
856
|
-
have_e2d = False
|
857
857
|
|
858
858
|
if self.no_expr_idx:
|
859
859
|
modified = False
|
@@ -1112,6 +1112,7 @@ class Up2k(object):
|
|
1112
1112
|
reg = {}
|
1113
1113
|
drp = None
|
1114
1114
|
emptylist = []
|
1115
|
+
dotpart = "." if self.args.dotpart else ""
|
1115
1116
|
snap = os.path.join(histpath, "up2k.snap")
|
1116
1117
|
if bos.path.exists(snap):
|
1117
1118
|
with gzip.GzipFile(snap, "rb") as f:
|
@@ -1124,6 +1125,8 @@ class Up2k(object):
|
|
1124
1125
|
except:
|
1125
1126
|
pass
|
1126
1127
|
|
1128
|
+
reg = reg2 # diff-golf
|
1129
|
+
|
1127
1130
|
if reg2 and "dwrk" not in reg2[next(iter(reg2))]:
|
1128
1131
|
for job in reg2.values():
|
1129
1132
|
job["dwrk"] = job["wark"]
|
@@ -1131,7 +1134,8 @@ class Up2k(object):
|
|
1131
1134
|
rm = []
|
1132
1135
|
for k, job in reg2.items():
|
1133
1136
|
job["ptop"] = ptop
|
1134
|
-
|
1137
|
+
is_done = "done" in job
|
1138
|
+
if is_done:
|
1135
1139
|
job["need"] = job["hash"] = emptylist
|
1136
1140
|
else:
|
1137
1141
|
if "need" not in job:
|
@@ -1139,10 +1143,13 @@ class Up2k(object):
|
|
1139
1143
|
if "hash" not in job:
|
1140
1144
|
job["hash"] = []
|
1141
1145
|
|
1142
|
-
|
1146
|
+
if is_done:
|
1147
|
+
fp = djoin(ptop, job["prel"], job["name"])
|
1148
|
+
else:
|
1149
|
+
fp = djoin(ptop, job["prel"], dotpart + job["name"] + ".PARTIAL")
|
1150
|
+
|
1143
1151
|
if bos.path.exists(fp):
|
1144
|
-
|
1145
|
-
if "done" in job:
|
1152
|
+
if is_done:
|
1146
1153
|
continue
|
1147
1154
|
job["poke"] = time.time()
|
1148
1155
|
job["busy"] = {}
|
@@ -1150,11 +1157,18 @@ class Up2k(object):
|
|
1150
1157
|
self.log("ign deleted file in snap: %r" % (fp,))
|
1151
1158
|
if not n4g:
|
1152
1159
|
rm.append(k)
|
1153
|
-
continue
|
1154
1160
|
|
1155
1161
|
for x in rm:
|
1156
1162
|
del reg2[x]
|
1157
1163
|
|
1164
|
+
# optimize pre-1.15.4 entries
|
1165
|
+
if next((x for x in reg.values() if "done" in x and "poke" in x), None):
|
1166
|
+
zsl = "host tnam busy sprs poke t0c".split()
|
1167
|
+
for job in reg.values():
|
1168
|
+
if "done" in job:
|
1169
|
+
for k in zsl:
|
1170
|
+
job.pop(k, None)
|
1171
|
+
|
1158
1172
|
if drp is None:
|
1159
1173
|
drp = [k for k, v in reg.items() if not v["need"]]
|
1160
1174
|
else:
|
@@ -2989,7 +3003,7 @@ class Up2k(object):
|
|
2989
3003
|
if wark in reg:
|
2990
3004
|
del reg[wark]
|
2991
3005
|
job["hash"] = job["need"] = []
|
2992
|
-
job["done"] =
|
3006
|
+
job["done"] = 1
|
2993
3007
|
job["busy"] = {}
|
2994
3008
|
|
2995
3009
|
if lost:
|
copyparty/web/a/u2c.py
CHANGED
@@ -1101,7 +1101,7 @@ class Ctl(object):
|
|
1101
1101
|
nleft = self.nfiles - self.up_f
|
1102
1102
|
tail = "\033[K\033[u" if VT100 and not self.ar.ns else "\r"
|
1103
1103
|
|
1104
|
-
t = "%s eta @ %s/s, %s, %d# left
|
1104
|
+
t = "%s eta @ %s/s, %s, %d# left" % (self.eta, spd, sleft, nleft)
|
1105
1105
|
if not self.hash_b:
|
1106
1106
|
t = " now hashing..."
|
1107
1107
|
eprint(txt + "\033]0;{0}\033\\\r{0}{1}".format(t, tail))
|
copyparty/web/browser.html
CHANGED
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/md.html
CHANGED
@@ -128,9 +128,9 @@ write markdown (most html is 🙆 too)
|
|
128
128
|
|
129
129
|
<script>
|
130
130
|
|
131
|
-
var SR = {{ r
|
131
|
+
var SR = "{{ r }}",
|
132
132
|
last_modified = {{ lastmod }},
|
133
|
-
have_emp = {{ have_emp
|
133
|
+
have_emp = {{ "true" if have_emp else "false" }},
|
134
134
|
dfavico = "{{ favico }}";
|
135
135
|
|
136
136
|
var md_opt = {
|
copyparty/web/mde.html
CHANGED
@@ -26,9 +26,9 @@
|
|
26
26
|
<a href="#" id="repl">π</a>
|
27
27
|
<script>
|
28
28
|
|
29
|
-
var SR = {{ r
|
29
|
+
var SR = "{{ r }}",
|
30
30
|
last_modified = {{ lastmod }},
|
31
|
-
have_emp = {{ have_emp
|
31
|
+
have_emp = {{ "true" if have_emp else "false" }},
|
32
32
|
dfavico = "{{ favico }}";
|
33
33
|
|
34
34
|
var md_opt = {
|
copyparty/web/rups.css.gz
CHANGED
Binary file
|
copyparty/web/rups.html
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
<title>{{ s_doctitle }}</title>
|
7
7
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
9
|
+
<meta name="robots" content="noindex, nofollow">
|
9
10
|
<meta name="theme-color" content="#{{ tcolor }}">
|
10
11
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/rups.css?_={{ ts }}">
|
11
12
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
@@ -14,14 +15,10 @@
|
|
14
15
|
|
15
16
|
<body>
|
16
17
|
<div id="wrap">
|
17
|
-
<a
|
18
|
-
<a
|
19
|
-
<
|
20
|
-
|
21
|
-
Filter: <input type="text" name="filter" size="20" placeholder="documents/passwords" value="{{ filt }}" />
|
22
|
-
<input type="submit" />
|
23
|
-
</form>
|
24
|
-
<span id="hits"></span>
|
18
|
+
<a href="#" id="re">refresh</a>
|
19
|
+
<a href="{{ r }}/?h">control-panel</a>
|
20
|
+
Filter: <input type="text" id="filter" size="20" placeholder="documents/passwords" />
|
21
|
+
<span id="hits"></span>
|
25
22
|
<table id="tab"><thead><tr>
|
26
23
|
<th>size</th>
|
27
24
|
<th>who</th>
|
@@ -29,27 +26,12 @@
|
|
29
26
|
<th>age</th>
|
30
27
|
<th>dir</th>
|
31
28
|
<th>file</th>
|
32
|
-
</tr></thead><tbody>
|
33
|
-
{% for vp, evp, sz, ip, at in rows %}
|
34
|
-
<tr>
|
35
|
-
<td>{{ sz }}</td>
|
36
|
-
<td>{{ ip }}</td>
|
37
|
-
<td>{{ at }}</td>
|
38
|
-
<td>{{ (now-at) }}</td>
|
39
|
-
<td></td>
|
40
|
-
<td><a href="{{ r }}{{ evp }}">{{ vp|e }}</a></td>
|
41
|
-
</tr>
|
42
|
-
{% endfor %}
|
43
|
-
</tbody></table>
|
44
|
-
{% if not rows %}
|
45
|
-
(the database is not aware of any uploads)
|
46
|
-
{% endif %}
|
29
|
+
</tr></thead><tbody id="tb"></tbody></table>
|
47
30
|
</div>
|
48
31
|
<a href="#" id="repl">π</a>
|
49
32
|
<script>
|
50
33
|
|
51
|
-
var SR
|
52
|
-
NOW = {{ now }},
|
34
|
+
var SR="{{ r }}",
|
53
35
|
lang="{{ lang }}",
|
54
36
|
dfavico="{{ favico }}";
|
55
37
|
|
@@ -58,6 +40,7 @@ document.documentElement.className = (STG && STG.cpp_thm) || "{{ this.args.theme
|
|
58
40
|
|
59
41
|
</script>
|
60
42
|
<script src="{{ r }}/.cpr/util.js?_={{ ts }}"></script>
|
43
|
+
<script>var V={{ v }};</script>
|
61
44
|
<script src="{{ r }}/.cpr/rups.js?_={{ ts }}"></script>
|
62
45
|
{%- if js %}
|
63
46
|
<script src="{{ js }}_={{ ts }}"></script>
|
copyparty/web/rups.js.gz
CHANGED
Binary file
|
copyparty/web/shares.html
CHANGED
@@ -6,6 +6,7 @@
|
|
6
6
|
<title>{{ s_doctitle }}</title>
|
7
7
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
8
8
|
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
9
|
+
<meta name="robots" content="noindex, nofollow">
|
9
10
|
<meta name="theme-color" content="#{{ tcolor }}">
|
10
11
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/shares.css?_={{ ts }}">
|
11
12
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
@@ -14,8 +15,8 @@
|
|
14
15
|
|
15
16
|
<body>
|
16
17
|
<div id="wrap">
|
17
|
-
<a
|
18
|
-
<a
|
18
|
+
<a href="{{ r }}/?shares">refresh</a>
|
19
|
+
<a href="{{ r }}/?h">control-panel</a>
|
19
20
|
|
20
21
|
<span>axs = perms (read,write,move,delet)</span>
|
21
22
|
<span>nf = numFiles (0=dir)</span>
|
@@ -62,7 +63,7 @@
|
|
62
63
|
<a href="#" id="repl">π</a>
|
63
64
|
<script>
|
64
65
|
|
65
|
-
var SR
|
66
|
+
var SR="{{ r }}",
|
66
67
|
shr="{{ shr }}",
|
67
68
|
lang="{{ lang }}",
|
68
69
|
dfavico="{{ favico }}";
|
copyparty/web/splash.css.gz
CHANGED
Binary file
|
copyparty/web/splash.html
CHANGED
copyparty/web/svcs.html
CHANGED
@@ -9,7 +9,7 @@
|
|
9
9
|
<meta name="theme-color" content="#{{ tcolor }}">
|
10
10
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/splash.css?_={{ ts }}">
|
11
11
|
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}">
|
12
|
-
<style>ul{padding-left:1.3em}li{margin:.4em 0}</style>
|
12
|
+
<style>ul{padding-left:1.3em}li{margin:.4em 0}.txa{float:right;margin:0 0 0 1em}</style>
|
13
13
|
{{ html_head }}
|
14
14
|
</head>
|
15
15
|
|
@@ -31,15 +31,22 @@
|
|
31
31
|
<br />
|
32
32
|
<span class="os win lin mac">placeholders:</span>
|
33
33
|
<span class="os win">
|
34
|
-
{% if accs %}<code><b>{{ pw }}</b></code>=password, {% endif %}<code><b>W:</b></code>=mountpoint
|
34
|
+
{% if accs %}<code><b id="pw0">{{ pw }}</b></code>=password, {% endif %}<code><b>W:</b></code>=mountpoint
|
35
35
|
</span>
|
36
36
|
<span class="os lin mac">
|
37
|
-
{% if accs %}<code><b>{{ pw }}</b></code>=password, {% endif %}<code><b>mp</b></code>=mountpoint
|
37
|
+
{% if accs %}<code><b id="pw0">{{ pw }}</b></code>=password, {% endif %}<code><b>mp</b></code>=mountpoint
|
38
38
|
</span>
|
39
|
+
<a href="#" id="setpw">use real password</a>
|
39
40
|
</p>
|
40
41
|
|
41
42
|
|
42
43
|
|
44
|
+
{% if args.idp_h_usr %}
|
45
|
+
<p style="line-height:2em"><b>WARNING:</b> this server is using IdP-based authentication, so this stuff may not work as advertised. Depending on server config, these commands can probably only be used to access areas which don't require authentication, unless you auth using any non-IdP accounts defined in the copyparty config. Please see <a href="https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#connecting-webdav-clients">the IdP docs</a></p>
|
46
|
+
{% endif %}
|
47
|
+
|
48
|
+
|
49
|
+
|
43
50
|
{% if not args.no_dav %}
|
44
51
|
<h1>WebDAV</h1>
|
45
52
|
|
@@ -229,11 +236,65 @@
|
|
229
236
|
|
230
237
|
|
231
238
|
|
239
|
+
<div class="os win">
|
240
|
+
<h1>ShareX</h1>
|
241
|
+
|
242
|
+
<p>to upload screenshots using ShareX <a href="https://github.com/ShareX/ShareX/releases/tag/v12.4.1">v12</a> or <a href="https://getsharex.com/">v15+</a>, save this as <code>copyparty.sxcu</code> and run it:</p>
|
243
|
+
|
244
|
+
<pre class="dl" name="copyparty.sxcu">
|
245
|
+
{ "Name": "copyparty",
|
246
|
+
"RequestURL": "http{{ s }}://{{ ep }}/{{ rvp }}",
|
247
|
+
"Headers": {
|
248
|
+
{% if accs %}"pw": "<b>{{ pw }}</b>",{% endif %}
|
249
|
+
"accept": "url"
|
250
|
+
},
|
251
|
+
"DestinationType": "ImageUploader, TextUploader, FileUploader",
|
252
|
+
"FileFormName": "f" }
|
253
|
+
</pre>
|
254
|
+
</div>
|
255
|
+
|
256
|
+
|
257
|
+
|
258
|
+
<div class="os mac">
|
259
|
+
<h1>ishare</h1>
|
260
|
+
|
261
|
+
<p>to upload screenshots using <a href="https://isharemac.app/">ishare</a>, save this as <code>copyparty.iscu</code> and run it:</p>
|
262
|
+
|
263
|
+
<pre class="dl" name="copyparty.iscu">
|
264
|
+
{ "Name": "copyparty",
|
265
|
+
"RequestURL": "http{{ s }}://{{ ep }}/{{ rvp }}",
|
266
|
+
"Headers": {
|
267
|
+
{% if accs %}"pw": "<b>{{ pw }}</b>",{% endif %}
|
268
|
+
"accept": "json"
|
269
|
+
},
|
270
|
+
"ResponseURL": "{{ '{{fileurl}}' }}",
|
271
|
+
"FileFormName": "f" }
|
272
|
+
</pre>
|
273
|
+
</div>
|
274
|
+
|
275
|
+
|
276
|
+
|
277
|
+
<div class="os lin">
|
278
|
+
<h1>flameshot</h1>
|
279
|
+
|
280
|
+
<p>to upload screenshots using <a href="https://flameshot.org/">flameshot</a>, save this as <code>flameshot.sh</code> and run it:</p>
|
281
|
+
|
282
|
+
<pre class="dl" name="flameshot.sh">
|
283
|
+
#!/bin/bash
|
284
|
+
pw="<b>{{ pw }}</b>"
|
285
|
+
url="http{{ s }}://{{ ep }}/{{ rvp }}"
|
286
|
+
filename="$(date +%Y-%m%d-%H%M%S).png"
|
287
|
+
flameshot gui -s -r | curl -sT- "$url$filename?want=url&pw=$pw" | xsel -ib
|
288
|
+
</pre>
|
289
|
+
</div>
|
290
|
+
|
291
|
+
|
292
|
+
|
232
293
|
</div>
|
233
294
|
<a href="#" id="repl">π</a>
|
234
295
|
<script>
|
235
296
|
|
236
|
-
var SR
|
297
|
+
var SR="{{ r }}",
|
237
298
|
lang="{{ lang }}",
|
238
299
|
dfavico="{{ favico }}";
|
239
300
|
|
copyparty/web/svcs.js.gz
CHANGED
Binary file
|
copyparty/web/ui.css.gz
CHANGED
Binary file
|
copyparty/web/up2k.js.gz
CHANGED
Binary file
|
copyparty/web/util.js.gz
CHANGED
Binary file
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.2
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.16.
|
3
|
+
Version: 1.16.8
|
4
4
|
Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
|
5
5
|
Author-email: ed <copyparty@ocv.me>
|
6
6
|
License: MIT
|
@@ -147,6 +147,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
147
147
|
* [listen on port 80 and 443](#listen-on-port-80-and-443) - become a *real* webserver
|
148
148
|
* [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
|
149
149
|
* [real-ip](#real-ip) - teaching copyparty how to see client IPs
|
150
|
+
* [reverse-proxy performance](#reverse-proxy-performance)
|
150
151
|
* [prometheus](#prometheus) - metrics/stats can be enabled
|
151
152
|
* [other extremely specific features](#other-extremely-specific-features) - you'll never find a use for these
|
152
153
|
* [custom mimetypes](#custom-mimetypes) - change the association of a file extension
|
@@ -195,6 +196,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
|
|
195
196
|
* or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
|
196
197
|
* or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
|
197
198
|
* or if you are on android, [install copyparty in termux](#install-on-android)
|
199
|
+
* or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
|
198
200
|
* or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
|
199
201
|
* or if you prefer to [use docker](./scripts/docker/) 🐋 you can do that too
|
200
202
|
* docker has all deps built-in, so skip this step:
|
@@ -701,7 +703,7 @@ dragdrop is the recommended way, but you may also:
|
|
701
703
|
|
702
704
|
* select some files (not folders) in your file explorer and press CTRL-V inside the browser window
|
703
705
|
* use the [command-line uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy)
|
704
|
-
* upload using [curl
|
706
|
+
* upload using [curl, sharex, ishare, ...](#client-examples)
|
705
707
|
|
706
708
|
when uploading files through dragdrop or CTRL-V, this initiates an upload using `up2k`; there are two browser-based uploaders available:
|
707
709
|
* `[🎈] bup`, the basic uploader, supports almost every browser since netscape 4.0
|
@@ -1159,6 +1161,8 @@ on macos, connect from finder:
|
|
1159
1161
|
|
1160
1162
|
in order to grant full write-access to webdav clients, the volflag `daw` must be set and the account must also have delete-access (otherwise the client won't be allowed to replace the contents of existing files, which is how webdav works)
|
1161
1163
|
|
1164
|
+
> note: if you have enabled [IdP authentication](#identity-providers) then that may cause issues for some/most webdav clients; see [the webdav section in the IdP docs](https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#connecting-webdav-clients)
|
1165
|
+
|
1162
1166
|
|
1163
1167
|
### connecting to webdav from windows
|
1164
1168
|
|
@@ -1724,10 +1728,16 @@ some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatical
|
|
1724
1728
|
|
1725
1729
|
for improved security (and a 10% performance boost) consider listening on a unix-socket with `-i unix:770:www:/tmp/party.sock` (permission `770` means only members of group `www` can access it)
|
1726
1730
|
|
1727
|
-
example webserver configs:
|
1731
|
+
example webserver / reverse-proxy configs:
|
1728
1732
|
|
1729
|
-
* [
|
1730
|
-
*
|
1733
|
+
* [apache config](contrib/apache/copyparty.conf)
|
1734
|
+
* caddy uds: `caddy reverse-proxy --from :8080 --to unix///dev/shm/party.sock`
|
1735
|
+
* caddy tcp: `caddy reverse-proxy --from :8081 --to http://127.0.0.1:3923`
|
1736
|
+
* [haproxy config](contrib/haproxy/copyparty.conf)
|
1737
|
+
* [lighttpd subdomain](contrib/lighttpd/subdomain.conf) -- entire domain/subdomain
|
1738
|
+
* [lighttpd subpath](contrib/lighttpd/subpath.conf) -- location-based (not optimal, but in case you need it)
|
1739
|
+
* [nginx config](contrib/nginx/copyparty.conf) -- recommended
|
1740
|
+
* [traefik config](contrib/traefik/copyparty.yaml)
|
1731
1741
|
|
1732
1742
|
|
1733
1743
|
### real-ip
|
@@ -1739,6 +1749,38 @@ if you (and maybe everybody else) keep getting a message that says `thank you fo
|
|
1739
1749
|
for most common setups, there should be a helpful message in the server-log explaining what to do, but see [docs/xff.md](docs/xff.md) if you want to learn more, including a quick hack to **just make it work** (which is **not** recommended, but hey...)
|
1740
1750
|
|
1741
1751
|
|
1752
|
+
### reverse-proxy performance
|
1753
|
+
|
1754
|
+
most reverse-proxies support connecting to copyparty either using uds/unix-sockets (`/dev/shm/party.sock`, faster/recommended) or using tcp (`127.0.0.1`)
|
1755
|
+
|
1756
|
+
with copyparty listening on a uds / unix-socket / unix-domain-socket and the reverse-proxy connecting to that:
|
1757
|
+
|
1758
|
+
| index.html | upload | download | software |
|
1759
|
+
| ------------ | ----------- | ----------- | -------- |
|
1760
|
+
| 28'900 req/s | 6'900 MiB/s | 7'400 MiB/s | no-proxy |
|
1761
|
+
| 18'750 req/s | 3'500 MiB/s | 2'370 MiB/s | haproxy |
|
1762
|
+
| 9'900 req/s | 3'750 MiB/s | 2'200 MiB/s | caddy |
|
1763
|
+
| 18'700 req/s | 2'200 MiB/s | 1'570 MiB/s | nginx |
|
1764
|
+
| 9'700 req/s | 1'750 MiB/s | 1'830 MiB/s | apache |
|
1765
|
+
| 9'900 req/s | 1'300 MiB/s | 1'470 MiB/s | lighttpd |
|
1766
|
+
|
1767
|
+
when connecting the reverse-proxy to `127.0.0.1` instead (the basic and/or old-fasioned way), speeds are a bit worse:
|
1768
|
+
|
1769
|
+
| index.html | upload | download | software |
|
1770
|
+
| ------------ | ----------- | ----------- | -------- |
|
1771
|
+
| 21'200 req/s | 5'700 MiB/s | 6'700 MiB/s | no-proxy |
|
1772
|
+
| 14'500 req/s | 1'700 MiB/s | 2'170 MiB/s | haproxy |
|
1773
|
+
| 11'100 req/s | 2'750 MiB/s | 2'000 MiB/s | traefik |
|
1774
|
+
| 8'400 req/s | 2'300 MiB/s | 1'950 MiB/s | caddy |
|
1775
|
+
| 13'400 req/s | 1'100 MiB/s | 1'480 MiB/s | nginx |
|
1776
|
+
| 8'400 req/s | 1'000 MiB/s | 1'000 MiB/s | apache |
|
1777
|
+
| 6'500 req/s | 1'270 MiB/s | 1'500 MiB/s | lighttpd |
|
1778
|
+
|
1779
|
+
in summary, `haproxy > caddy > traefik > nginx > apache > lighttpd`, and use uds when possible (traefik does not support it yet)
|
1780
|
+
|
1781
|
+
* if these results are bullshit because my config exampels are bad, please submit corrections!
|
1782
|
+
|
1783
|
+
|
1742
1784
|
## prometheus
|
1743
1785
|
|
1744
1786
|
metrics/stats can be enabled at URL `/.cpr/metrics` for grafana / prometheus / etc (openmetrics 1.0.0)
|
@@ -2052,7 +2094,8 @@ interact with copyparty using non-browser clients
|
|
2052
2094
|
* can be downloaded from copyparty: controlpanel -> connect -> [partyfuse.py](http://127.0.0.1:3923/.cpr/a/partyfuse.py)
|
2053
2095
|
* [rclone](https://rclone.org/) as client can give ~5x performance, see [./docs/rclone.md](docs/rclone.md)
|
2054
2096
|
|
2055
|
-
* sharex (screenshot utility): see [./contrib/sharex.sxcu](contrib/#sharexsxcu)
|
2097
|
+
* sharex (screenshot utility): see [./contrib/sharex.sxcu](./contrib/#sharexsxcu)
|
2098
|
+
* and for screenshots on macos, see [./contrib/ishare.iscu](./contrib/#ishareiscu)
|
2056
2099
|
* and for screenshots on linux, see [./contrib/flameshot.sh](./contrib/flameshot.sh)
|
2057
2100
|
|
2058
2101
|
* contextlet (web browser integration); see [contrib contextlet](contrib/#send-to-cppcontextletjson)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
copyparty/__init__.py,sha256=VR6ZZhB9IxaK5TDXDTBM_OIP5ydkrdbaEnstktLM__s,2649
|
2
2
|
copyparty/__main__.py,sha256=acjihZUEvEwFp6d6BnT35oFqminMZBruDUfVU76YijA,113564
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256=
|
3
|
+
copyparty/__version__.py,sha256=O9UOPs_HcmEufkcuaes_ZA7U8kK_sVtXCGmVqE69BnA,251
|
4
|
+
copyparty/authsrv.py,sha256=h1OhdMR_hrqZA9NH2pMRZ-Axc_GSoUajioCZhimVVsQ,104109
|
5
5
|
copyparty/broker_mp.py,sha256=QdOXXvV2Xn6J0CysEqyY3GZbqxQMyWnTpnba-a5lMc0,4987
|
6
6
|
copyparty/broker_mpw.py,sha256=PpSS4SK3pItlpfD8OwVr3QmJEPKlUgaf2nuMOozixgU,3347
|
7
7
|
copyparty/broker_thr.py,sha256=fjoYtpSscUA7-nMl4r1n2R7UK3J9lrvLS3rUZ-iJzKQ,1721
|
@@ -11,9 +11,9 @@ copyparty/cfg.py,sha256=UUmFpFbTm750Nv9RnofS80-FTpWT37EggEtmkE1wbxE,10374
|
|
11
11
|
copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
|
12
12
|
copyparty/fsutil.py,sha256=IVOFG8zBQPMQDDv7RIStSJHwHiAnVNROZS37O5k465A,4524
|
13
13
|
copyparty/ftpd.py,sha256=T97SFS7JFtvRLbJX8C4fJSYwe13vhN3-E6emtlVmqLA,17608
|
14
|
-
copyparty/httpcli.py,sha256=
|
14
|
+
copyparty/httpcli.py,sha256=jiR7zHGGyVp-ZwK-j0I2eNkwbEOYDTc7xGrGzrSb0iQ,215225
|
15
15
|
copyparty/httpconn.py,sha256=mQSgljh0Q-jyWjF4tQLrHbRKRe9WKl19kGqsGMsJpWo,6880
|
16
|
-
copyparty/httpsrv.py,sha256=
|
16
|
+
copyparty/httpsrv.py,sha256=pxH_Eh8ElBLvOEDejgpP9Bvk65HNEou-03aYIcgXhrs,18090
|
17
17
|
copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
|
18
18
|
copyparty/mdns.py,sha256=G73OWWg1copda47LgayCRK7qjVrk6cnUGpMR5ugmi7o,18315
|
19
19
|
copyparty/metrics.py,sha256=EOIiPOItEQmdK9YgNb75l0kCzanWb6RtJGwMI7ufifY,8966
|
@@ -26,12 +26,12 @@ copyparty/star.py,sha256=tV5BbX6AiQ7N4UU8DYtSTckNYeoeey4DBqq4LjfymbY,3818
|
|
26
26
|
copyparty/sutil.py,sha256=6zEEGl4hRe6bTB83Y_RtnBGxr2JcUa__GdiAMqNJZnY,3208
|
27
27
|
copyparty/svchub.py,sha256=sAHkiPGzzKACLqKlem2V-bps9Xh-wHlcfwaNywxcd5A,40877
|
28
28
|
copyparty/szip.py,sha256=HFtnwOiBgx0HMLUf-h_T84zSlRijPxmhRo5PM613kRA,8602
|
29
|
-
copyparty/tcpsrv.py,sha256=
|
29
|
+
copyparty/tcpsrv.py,sha256=2q18dGR8jnezA4SMfUXa-wrGRGX3nHIwkxkWvkTzF2A,19889
|
30
30
|
copyparty/tftpd.py,sha256=PXgG4rTmiaU_TavSyZWD5cFphdfChs9YvNY21qfExt8,13611
|
31
31
|
copyparty/th_cli.py,sha256=1B8el4NNs5cNyJyjOPiAdvLOX2HQXaebsHn6VLdZ_gU,4630
|
32
32
|
copyparty/th_srv.py,sha256=uAcz-wZJQEG5KavcZDkwToONZujyoOeC4oCxxKTD5us,29575
|
33
33
|
copyparty/u2idx.py,sha256=G6MDbD4I_sJSOwaNFZ6XLTQhnEDrB12pVKuKhzQ_leE,13676
|
34
|
-
copyparty/up2k.py,sha256=
|
34
|
+
copyparty/up2k.py,sha256=0YPdP2UHoJr0c3QhS6EsOIUPSq2JztCD43Y3D7lN6Po,175144
|
35
35
|
copyparty/util.py,sha256=qhNakLtygJveVbZUuPvDY7Tm1E48E67CS9XBmosu5i4,95101
|
36
36
|
copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
37
|
copyparty/bos/bos.py,sha256=Wb7eWsXJgR5AFlBR9ZOyKrLTwy-Kct9RrGiOu4Jo37Y,1622
|
@@ -56,39 +56,39 @@ copyparty/stolen/ifaddr/_shared.py,sha256=uNC4SdEIgdSLKvuUzsf1aM-H1Xrc_9mpLoOT43
|
|
56
56
|
copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8QtZM,4026
|
57
57
|
copyparty/web/baguettebox.js.gz,sha256=_amC3ipOrXKEFz8DsVP-JEl49VjMQYiKyF78eWfG-uk,7965
|
58
58
|
copyparty/web/browser.css.gz,sha256=kurx_iA-KxLYx8PqJsn0bJVjkAxP-0YTOHSV9l_oouo,11645
|
59
|
-
copyparty/web/browser.html,sha256=
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
59
|
+
copyparty/web/browser.html,sha256=dekrZQ6w8ciB-QPlp-mjcuzUVKlsCYcvvi6efmXRfQE,4822
|
60
|
+
copyparty/web/browser.js.gz,sha256=HejO4Brwmw-NmLVmqbK1I31LPP9arf7G9JaHOUpNcfY,89876
|
61
61
|
copyparty/web/browser2.html,sha256=NRUZ08GH-e2YcGXcoz0UjYg6JIVF42u4IMX4HHwWTmg,1587
|
62
62
|
copyparty/web/cf.html,sha256=lJThtNFNAQT1ClCHHlivAkDGE0LutedwopXD62Z8Nys,589
|
63
63
|
copyparty/web/dbg-audio.js.gz,sha256=Ma-KZtK8LnmiwNvNKFKXMPYl_Nn_3U7GsJ6-DRWC2HE,688
|
64
64
|
copyparty/web/md.css.gz,sha256=UZpN0J7ubVM05CZkbZYkQRJeGgJt_GNDEzKTGSQd8h4,2032
|
65
|
-
copyparty/web/md.html,sha256=
|
65
|
+
copyparty/web/md.html,sha256=hz-xJVfKtaeTUQn3tGh7ebIMvLbOjVKkrMhsCTr3lGM,4200
|
66
66
|
copyparty/web/md.js.gz,sha256=tw9vS9yZzyVW4FNOTv0MMHBdBEedfahZRZ8WhVJDu4A,4180
|
67
67
|
copyparty/web/md2.css.gz,sha256=uIVHKScThdbcfhXNSHgKZnALYpxbnXC-WuEzOJ20Lpc,699
|
68
68
|
copyparty/web/md2.js.gz,sha256=w0Ve06BUcyZfcWR8DAPpCj-kI-uL_wTL2aX_0PsNc-I,8363
|
69
69
|
copyparty/web/mde.css.gz,sha256=2SkAEDKIRPqywNJ8t_heQaeBQ_R73Rf-pQI_bDoKF6o,942
|
70
|
-
copyparty/web/mde.html,sha256=
|
70
|
+
copyparty/web/mde.html,sha256=fRGUlnNhK6ra8P4jskQLUw6Aaef6g6Heh4EEgYhJkxU,1770
|
71
71
|
copyparty/web/mde.js.gz,sha256=kN2eUSvr4mFuksfK4-4LimJmWdwsao39Sea2lWtu8L0,2224
|
72
72
|
copyparty/web/msg.css.gz,sha256=u90fXYAVrMD-jqwf5XFVC1ptSpSHZUe8Mez6PX101P8,300
|
73
73
|
copyparty/web/msg.html,sha256=w9CM3hkLLGJX9fWEaG4gSbTOPe2GcPqW8BpSCDiFzOI,977
|
74
|
-
copyparty/web/rups.css.gz,sha256=
|
75
|
-
copyparty/web/rups.html,sha256=
|
76
|
-
copyparty/web/rups.js.gz,sha256=
|
74
|
+
copyparty/web/rups.css.gz,sha256=pWklsym27oGGr-8tYQR7WnZvGZElAgCwLzlwTDErNAM,647
|
75
|
+
copyparty/web/rups.html,sha256=iPuz53jBT_mIWIfl1yrjjg5-P7oO2ada6fTFq8PgjGk,1479
|
76
|
+
copyparty/web/rups.js.gz,sha256=nvvcG8L-fkm7zkhjnlTGhBp_KD0j08mtHEW0sB7zy-Y,854
|
77
77
|
copyparty/web/shares.css.gz,sha256=SdPlZCBwz9tkPkgEo5pSPDOZSI079njxEfkJ64-iW3c,547
|
78
|
-
copyparty/web/shares.html,sha256=
|
78
|
+
copyparty/web/shares.html,sha256=YctvUrKuBYu42kxVylyW2_DEHm7Ik6uHqzfzVZ4N0ac,2545
|
79
79
|
copyparty/web/shares.js.gz,sha256=emeY2-wjkh8x1JgaW6ny5fcC7XpZzZzfE1f-sEyntQ4,940
|
80
|
-
copyparty/web/splash.css.gz,sha256=
|
81
|
-
copyparty/web/splash.html,sha256=
|
80
|
+
copyparty/web/splash.css.gz,sha256=S8_A7JJl71xACRBYGzafeaD82OacW6Fa7oKPiNyrhAs,1087
|
81
|
+
copyparty/web/splash.html,sha256=QEnTH9QZXFmAuyVtgqOuuHKBtIdi7uclpRqe0ZMewj4,6249
|
82
82
|
copyparty/web/splash.js.gz,sha256=4VqNznN10-bT33IJm3VWzBEJ1s08XZyxFB1TYPUkuAo,2739
|
83
|
-
copyparty/web/svcs.html,sha256=
|
84
|
-
copyparty/web/svcs.js.gz,sha256=
|
85
|
-
copyparty/web/ui.css.gz,sha256=
|
86
|
-
copyparty/web/up2k.js.gz,sha256=
|
87
|
-
copyparty/web/util.js.gz,sha256=
|
83
|
+
copyparty/web/svcs.html,sha256=s2uMblxDpYo8l-M--KB1BAF1ZiQrWf5p4v1sDjSs1MQ,14140
|
84
|
+
copyparty/web/svcs.js.gz,sha256=rcc75HMmoc3KA7Ld2j8X9AKX_elZgwUD6Vnm2F-yj_U,805
|
85
|
+
copyparty/web/ui.css.gz,sha256=0sHIwGsL3_xH8Uu6N0Ag3bKBTjf-e_yfFbKynEZXAnk,2800
|
86
|
+
copyparty/web/up2k.js.gz,sha256=f00wmaThk1CoFYzFBG_6KLG0acq60cAn3p12tngqprk,23843
|
87
|
+
copyparty/web/util.js.gz,sha256=uCCnKWT_FeG-ShKPoj0MtXnBwtdAbyDSM9AP9KiBDjw,15098
|
88
88
|
copyparty/web/w.hash.js.gz,sha256=l3GpSJD6mcU-1CRWkIj7PybgbjlfSr8oeO3vortIrQk,1105
|
89
89
|
copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
90
90
|
copyparty/web/a/partyfuse.py,sha256=9p5Hpg_IBiSimv7j9kmPhCGpy-FLXSRUOYnLjJ5JifU,28049
|
91
|
-
copyparty/web/a/u2c.py,sha256=
|
91
|
+
copyparty/web/a/u2c.py,sha256=OuA0UMtPDSrMHgd4ebANJXoLeIa1RKxzbBXUV3H8jIw,51606
|
92
92
|
copyparty/web/a/webdav-cfg.bat,sha256=Y4NoGZlksAIg4cBMb7KdJrpKC6Nx97onaTl6yMjaimk,1449
|
93
93
|
copyparty/web/dd/2.png,sha256=gJ14XFPzaw95L6z92fSq9eMPikSQyu-03P1lgiGe0_I,258
|
94
94
|
copyparty/web/dd/3.png,sha256=4lho8Koz5tV7jJ4ODo6GMTScZfkqsT05yp48EDFIlyg,252
|
@@ -109,9 +109,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
|
|
109
109
|
copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
|
110
110
|
copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
|
111
111
|
copyparty/web/deps/sha512.hw.js.gz,sha256=UAed2_ocklZCnIzcSYz2h4P1ycztlCLj-ewsRTud2lU,7939
|
112
|
-
copyparty-1.16.
|
113
|
-
copyparty-1.16.
|
114
|
-
copyparty-1.16.
|
115
|
-
copyparty-1.16.
|
116
|
-
copyparty-1.16.
|
117
|
-
copyparty-1.16.
|
112
|
+
copyparty-1.16.8.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
113
|
+
copyparty-1.16.8.dist-info/METADATA,sha256=-4246pvuZxdG_MtxGM7euDMxpPVVGiXgIzBEdvlRGLk,144011
|
114
|
+
copyparty-1.16.8.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
115
|
+
copyparty-1.16.8.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
116
|
+
copyparty-1.16.8.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
117
|
+
copyparty-1.16.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|