copyparty 1.15.8__py3-none-any.whl → 1.15.10__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/__main__.py +15 -1
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +17 -7
- copyparty/cfg.py +1 -0
- copyparty/httpcli.py +274 -75
- copyparty/metrics.py +1 -1
- copyparty/svchub.py +31 -3
- copyparty/u2idx.py +5 -3
- copyparty/up2k.py +7 -5
- copyparty/util.py +49 -13
- copyparty/web/a/partyfuse.py +1 -1
- copyparty/web/a/u2c.py +13 -9
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/splash.html +12 -3
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/w.hash.js.gz +0 -0
- {copyparty-1.15.8.dist-info → copyparty-1.15.10.dist-info}/METADATA +30 -1
- {copyparty-1.15.8.dist-info → copyparty-1.15.10.dist-info}/RECORD +23 -23
- {copyparty-1.15.8.dist-info → copyparty-1.15.10.dist-info}/LICENSE +0 -0
- {copyparty-1.15.8.dist-info → copyparty-1.15.10.dist-info}/WHEEL +0 -0
- {copyparty-1.15.8.dist-info → copyparty-1.15.10.dist-info}/entry_points.txt +0 -0
- {copyparty-1.15.8.dist-info → copyparty-1.15.10.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -1029,7 +1029,7 @@ def add_network(ap):
|
|
1029
1029
|
else:
|
1030
1030
|
ap2.add_argument("--freebind", action="store_true", help="allow listening on IPs which do not yet exist, for example if the network interfaces haven't finished going up. Only makes sense for IPs other than '0.0.0.0', '127.0.0.1', '::', and '::1'. May require running as root (unless net.ipv6.ip_nonlocal_bind)")
|
1031
1031
|
ap2.add_argument("--s-thead", metavar="SEC", type=int, default=120, help="socket timeout (read request header)")
|
1032
|
-
ap2.add_argument("--s-tbody", metavar="SEC", type=float, default=
|
1032
|
+
ap2.add_argument("--s-tbody", metavar="SEC", type=float, default=128.0, help="socket timeout (read/write request/response bodies). Use 60 on fast servers (default is extremely safe). Disable with 0 if reverse-proxied for a 2%% speed boost")
|
1033
1033
|
ap2.add_argument("--s-rd-sz", metavar="B", type=int, default=256*1024, help="socket read size in bytes (indirectly affects filesystem writes; recommendation: keep equal-to or lower-than \033[33m--iobuf\033[0m)")
|
1034
1034
|
ap2.add_argument("--s-wr-sz", metavar="B", type=int, default=256*1024, help="socket write size in bytes")
|
1035
1035
|
ap2.add_argument("--s-wr-slp", metavar="SEC", type=float, default=0.0, help="debug: socket write delay in seconds")
|
@@ -1299,6 +1299,7 @@ def add_logging(ap):
|
|
1299
1299
|
ap2.add_argument("--log-conn", action="store_true", help="debug: print tcp-server msgs")
|
1300
1300
|
ap2.add_argument("--log-htp", action="store_true", help="debug: print http-server threadpool scaling")
|
1301
1301
|
ap2.add_argument("--ihead", metavar="HEADER", type=u, action='append', help="print request \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
|
1302
|
+
ap2.add_argument("--ohead", metavar="HEADER", type=u, action='append', help="print response \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
|
1302
1303
|
ap2.add_argument("--lf-url", metavar="RE", type=u, default=r"^/\.cpr/|\?th=[wj]$|/\.(_|ql_|DS_Store$|localized$)", help="dont log URLs matching regex \033[33mRE\033[0m")
|
1303
1304
|
|
1304
1305
|
|
@@ -1349,6 +1350,14 @@ def add_transcoding(ap):
|
|
1349
1350
|
ap2.add_argument("--ac-maxage", metavar="SEC", type=int, default=86400, help="delete cached transcode output after \033[33mSEC\033[0m seconds")
|
1350
1351
|
|
1351
1352
|
|
1353
|
+
def add_rss(ap):
|
1354
|
+
ap2 = ap.add_argument_group('RSS options')
|
1355
|
+
ap2.add_argument("--rss", action="store_true", help="enable RSS output (experimental)")
|
1356
|
+
ap2.add_argument("--rss-nf", metavar="HITS", type=int, default=250, help="default number of files to return (url-param 'nf')")
|
1357
|
+
ap2.add_argument("--rss-fext", metavar="E,E", type=u, default="", help="default list of file extensions to include (url-param 'fext'); blank=all")
|
1358
|
+
ap2.add_argument("--rss-sort", metavar="ORD", type=u, default="m", help="default sort order (url-param 'sort'); [\033[32mm\033[0m]=last-modified [\033[32mu\033[0m]=upload-time [\033[32mn\033[0m]=filename [\033[32ms\033[0m]=filesize; Uppercase=oldest-first. Note that upload-time is 0 for non-uploaded files")
|
1359
|
+
|
1360
|
+
|
1352
1361
|
def add_db_general(ap, hcores):
|
1353
1362
|
noidx = APPLESAN_TXT if MACOS else ""
|
1354
1363
|
ap2 = ap.add_argument_group('general db options')
|
@@ -1444,6 +1453,7 @@ def add_ui(ap, retry):
|
|
1444
1453
|
ap2.add_argument("--pb-url", metavar="URL", type=u, default="https://github.com/9001/copyparty", help="powered-by link; disable with \033[33m-np\033[0m")
|
1445
1454
|
ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m)")
|
1446
1455
|
ap2.add_argument("--k304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable k304 on the controlpanel (workaround for buggy reverse-proxies); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
|
1456
|
+
ap2.add_argument("--no304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable no304 on the controlpanel (workaround for buggy caching in browsers); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
|
1447
1457
|
ap2.add_argument("--md-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to ALLOW for README.md docs (volflag=md_sbf); see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox")
|
1448
1458
|
ap2.add_argument("--lg-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to ALLOW for prologue/epilogue docs (volflag=lg_sbf)")
|
1449
1459
|
ap2.add_argument("--no-sb-md", action="store_true", help="don't sandbox README/PREADME.md documents (volflags: no_sb_md | sb_md)")
|
@@ -1518,6 +1528,7 @@ def run_argparse(
|
|
1518
1528
|
add_db_metadata(ap)
|
1519
1529
|
add_thumbnail(ap)
|
1520
1530
|
add_transcoding(ap)
|
1531
|
+
add_rss(ap)
|
1521
1532
|
add_ftp(ap)
|
1522
1533
|
add_webdav(ap)
|
1523
1534
|
add_tftp(ap)
|
@@ -1740,6 +1751,9 @@ def main(argv = None) :
|
|
1740
1751
|
if al.ihead:
|
1741
1752
|
al.ihead = [x.lower() for x in al.ihead]
|
1742
1753
|
|
1754
|
+
if al.ohead:
|
1755
|
+
al.ohead = [x.lower() for x in al.ohead]
|
1756
|
+
|
1743
1757
|
if HAVE_SSL:
|
1744
1758
|
if al.ssl_ver:
|
1745
1759
|
configure_ssl_ver(al)
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -158,8 +158,11 @@ class Lim(object):
|
|
158
158
|
self.chk_rem(rem)
|
159
159
|
if sz != -1:
|
160
160
|
self.chk_sz(sz)
|
161
|
-
|
162
|
-
|
161
|
+
else:
|
162
|
+
sz = 0
|
163
|
+
|
164
|
+
self.chk_vsz(broker, ptop, sz, volgetter)
|
165
|
+
self.chk_df(abspath, sz) # side effects; keep last-ish
|
163
166
|
|
164
167
|
ap2, vp2 = self.rot(abspath)
|
165
168
|
if abspath == ap2:
|
@@ -199,7 +202,15 @@ class Lim(object):
|
|
199
202
|
|
200
203
|
if self.dft < time.time():
|
201
204
|
self.dft = int(time.time()) + 300
|
202
|
-
|
205
|
+
|
206
|
+
df, du, err = get_df(abspath, True)
|
207
|
+
if err:
|
208
|
+
t = "failed to read disk space usage for [%s]: %s"
|
209
|
+
self.log(t % (abspath, err), 3)
|
210
|
+
self.dfv = 0xAAAAAAAAA # 42.6 GiB
|
211
|
+
else:
|
212
|
+
self.dfv = df or 0
|
213
|
+
|
203
214
|
for j in list(self.reg.values()) if self.reg else []:
|
204
215
|
self.dfv -= int(j["size"] / (len(j["hash"]) or 999) * len(j["need"]))
|
205
216
|
|
@@ -534,15 +545,14 @@ class VFS(object):
|
|
534
545
|
return self._get_dbv(vrem)
|
535
546
|
|
536
547
|
shv, srem = src
|
537
|
-
return shv
|
548
|
+
return shv._get_dbv(vjoin(srem, vrem))
|
538
549
|
|
539
550
|
def _get_dbv(self, vrem ) :
|
540
551
|
dbv = self.dbv
|
541
552
|
if not dbv:
|
542
553
|
return self, vrem
|
543
554
|
|
544
|
-
|
545
|
-
vrem = "/".join([x for x in tv if x])
|
555
|
+
vrem = vjoin(self.vpath[len(dbv.vpath) :].lstrip("/"), vrem)
|
546
556
|
return dbv, vrem
|
547
557
|
|
548
558
|
def canonical(self, rem , resolve = True) :
|
@@ -2622,7 +2632,7 @@ class AuthSrv(object):
|
|
2622
2632
|
]
|
2623
2633
|
|
2624
2634
|
csv = set("i p th_covers zm_on zm_off zs_on zs_off".split())
|
2625
|
-
zs = "c ihead mtm mtp on403 on404 xad xar xau xiu xban xbd xbr xbu xm"
|
2635
|
+
zs = "c ihead ohead mtm mtp on403 on404 xad xar xau xiu xban xbd xbr xbu xm"
|
2626
2636
|
lst = set(zs.split())
|
2627
2637
|
askip = set("a v c vc cgen exp_lg exp_md theme".split())
|
2628
2638
|
fskip = set("exp_lg exp_md mv_re_r mv_re_t rm_re_r rm_re_t".split())
|
copyparty/cfg.py
CHANGED
copyparty/httpcli.py
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
4
|
import argparse # typechk
|
5
|
-
import calendar
|
6
5
|
import copy
|
7
6
|
import errno
|
8
7
|
import gzip
|
@@ -19,7 +18,6 @@ import threading # typechk
|
|
19
18
|
import time
|
20
19
|
import uuid
|
21
20
|
from datetime import datetime
|
22
|
-
from email.utils import parsedate
|
23
21
|
from operator import itemgetter
|
24
22
|
|
25
23
|
import jinja2 # typechk
|
@@ -107,6 +105,7 @@ from .util import (
|
|
107
105
|
unquotep,
|
108
106
|
vjoin,
|
109
107
|
vol_san,
|
108
|
+
vroots,
|
110
109
|
vsplit,
|
111
110
|
wrename,
|
112
111
|
wunlink,
|
@@ -123,10 +122,17 @@ _ = (argparse, threading)
|
|
123
122
|
|
124
123
|
NO_CACHE = {"Cache-Control": "no-cache"}
|
125
124
|
|
125
|
+
ALL_COOKIES = "k304 no304 js idxh dots cppwd cppws".split()
|
126
|
+
|
127
|
+
H_CONN_KEEPALIVE = "Connection: Keep-Alive"
|
128
|
+
H_CONN_CLOSE = "Connection: Close"
|
129
|
+
|
126
130
|
LOGUES = [[0, ".prologue.html"], [1, ".epilogue.html"]]
|
127
131
|
|
128
132
|
READMES = [[0, ["preadme.md", "PREADME.md"]], [1, ["readme.md", "README.md"]]]
|
129
133
|
|
134
|
+
RSS_SORT = {"m": "mt", "u": "at", "n": "fn", "s": "sz"}
|
135
|
+
|
130
136
|
|
131
137
|
class HttpCli(object):
|
132
138
|
"""
|
@@ -785,11 +791,11 @@ class HttpCli(object):
|
|
785
791
|
|
786
792
|
def k304(self) :
|
787
793
|
k304 = self.cookies.get("k304")
|
788
|
-
return (
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
)
|
794
|
+
return k304 == "y" or (self.args.k304 == 2 and k304 != "n")
|
795
|
+
|
796
|
+
def no304(self) :
|
797
|
+
no304 = self.cookies.get("no304")
|
798
|
+
return no304 == "y" or (self.args.no304 == 2 and no304 != "n")
|
793
799
|
|
794
800
|
def _build_html_head(self, maybe_html , kv ) :
|
795
801
|
html = str(maybe_html)
|
@@ -824,25 +830,28 @@ class HttpCli(object):
|
|
824
830
|
) :
|
825
831
|
response = ["%s %s %s" % (self.http_ver, status, HTTPCODE[status])]
|
826
832
|
|
827
|
-
if length is not None:
|
828
|
-
response.append("Content-Length: " + unicode(length))
|
829
|
-
|
830
|
-
if status == 304 and self.k304():
|
831
|
-
self.keepalive = False
|
832
|
-
|
833
|
-
# close if unknown length, otherwise take client's preference
|
834
|
-
response.append("Connection: " + ("Keep-Alive" if self.keepalive else "Close"))
|
835
|
-
response.append("Date: " + formatdate())
|
836
|
-
|
837
833
|
# headers{} overrides anything set previously
|
838
834
|
if headers:
|
839
835
|
self.out_headers.update(headers)
|
840
836
|
|
841
|
-
|
842
|
-
|
843
|
-
|
837
|
+
if status == 304:
|
838
|
+
self.out_headers.pop("Content-Length", None)
|
839
|
+
self.out_headers.pop("Content-Type", None)
|
840
|
+
self.out_headerlist.clear()
|
841
|
+
if self.k304():
|
842
|
+
self.keepalive = False
|
843
|
+
else:
|
844
|
+
if length is not None:
|
845
|
+
response.append("Content-Length: " + unicode(length))
|
846
|
+
|
847
|
+
if mime:
|
848
|
+
self.out_headers["Content-Type"] = mime
|
849
|
+
elif "Content-Type" not in self.out_headers:
|
850
|
+
self.out_headers["Content-Type"] = "text/html; charset=utf-8"
|
844
851
|
|
845
|
-
|
852
|
+
# close if unknown length, otherwise take client's preference
|
853
|
+
response.append(H_CONN_KEEPALIVE if self.keepalive else H_CONN_CLOSE)
|
854
|
+
response.append("Date: " + formatdate())
|
846
855
|
|
847
856
|
for k, zs in list(self.out_headers.items()) + self.out_headerlist:
|
848
857
|
response.append("%s: %s" % (k, zs))
|
@@ -856,6 +865,19 @@ class HttpCli(object):
|
|
856
865
|
self.cbonk(self.conn.hsrv.gmal, zs, "cc_hdr", "Cc in out-hdr")
|
857
866
|
raise Pebkac(999)
|
858
867
|
|
868
|
+
if self.args.ohead and self.do_log:
|
869
|
+
keys = self.args.ohead
|
870
|
+
if "*" in keys:
|
871
|
+
lines = response[1:]
|
872
|
+
else:
|
873
|
+
lines = []
|
874
|
+
for zs in response[1:]:
|
875
|
+
if zs.split(":")[0].lower() in keys:
|
876
|
+
lines.append(zs)
|
877
|
+
for zs in lines:
|
878
|
+
hk, hv = zs.split(": ")
|
879
|
+
self.log("[O] {}: \033[33m[{}]".format(hk, hv), 5)
|
880
|
+
|
859
881
|
response.append("\r\n")
|
860
882
|
try:
|
861
883
|
self.s.sendall("\r\n".join(response).encode("utf-8"))
|
@@ -935,13 +957,14 @@ class HttpCli(object):
|
|
935
957
|
|
936
958
|
lines = [
|
937
959
|
"%s %s %s" % (self.http_ver or "HTTP/1.1", status, HTTPCODE[status]),
|
938
|
-
|
960
|
+
H_CONN_CLOSE,
|
939
961
|
]
|
940
962
|
|
941
963
|
if body:
|
942
964
|
lines.append("Content-Length: " + unicode(len(body)))
|
943
965
|
|
944
|
-
|
966
|
+
lines.append("\r\n")
|
967
|
+
self.s.sendall("\r\n".join(lines).encode("utf-8") + body)
|
945
968
|
|
946
969
|
def urlq(self, add , rm ) :
|
947
970
|
"""
|
@@ -1175,12 +1198,6 @@ class HttpCli(object):
|
|
1175
1198
|
if "stack" in self.uparam:
|
1176
1199
|
return self.tx_stack()
|
1177
1200
|
|
1178
|
-
if "ups" in self.uparam:
|
1179
|
-
return self.tx_ups()
|
1180
|
-
|
1181
|
-
if "k304" in self.uparam:
|
1182
|
-
return self.set_k304()
|
1183
|
-
|
1184
1201
|
if "setck" in self.uparam:
|
1185
1202
|
return self.setck()
|
1186
1203
|
|
@@ -1196,8 +1213,150 @@ class HttpCli(object):
|
|
1196
1213
|
if "h" in self.uparam:
|
1197
1214
|
return self.tx_mounts()
|
1198
1215
|
|
1216
|
+
if "ups" in self.uparam:
|
1217
|
+
# vpath is used for share translation
|
1218
|
+
return self.tx_ups()
|
1219
|
+
|
1220
|
+
if "rss" in self.uparam:
|
1221
|
+
return self.tx_rss()
|
1222
|
+
|
1199
1223
|
return self.tx_browser()
|
1200
1224
|
|
1225
|
+
def tx_rss(self) :
|
1226
|
+
if self.do_log:
|
1227
|
+
self.log("RSS %s @%s" % (self.req, self.uname))
|
1228
|
+
|
1229
|
+
if not self.can_read:
|
1230
|
+
return self.tx_404()
|
1231
|
+
|
1232
|
+
vn = self.vn
|
1233
|
+
if not vn.flags.get("rss"):
|
1234
|
+
raise Pebkac(405, "RSS is disabled in server config")
|
1235
|
+
|
1236
|
+
rem = self.rem
|
1237
|
+
idx = self.conn.get_u2idx()
|
1238
|
+
if not idx or not hasattr(idx, "p_end"):
|
1239
|
+
if not HAVE_SQLITE3:
|
1240
|
+
raise Pebkac(500, "sqlite3 not found on server; rss is disabled")
|
1241
|
+
raise Pebkac(500, "server busy, cannot generate rss; please retry in a bit")
|
1242
|
+
|
1243
|
+
uv = [rem]
|
1244
|
+
if "recursive" in self.uparam:
|
1245
|
+
uq = "up.rd like ?||'%'"
|
1246
|
+
else:
|
1247
|
+
uq = "up.rd == ?"
|
1248
|
+
|
1249
|
+
zs = str(self.uparam.get("fext", self.args.rss_fext))
|
1250
|
+
if zs in ("True", "False"):
|
1251
|
+
zs = ""
|
1252
|
+
if zs:
|
1253
|
+
zsl = []
|
1254
|
+
for ext in zs.split(","):
|
1255
|
+
zsl.append("+up.fn like '%.'||?")
|
1256
|
+
uv.append(ext)
|
1257
|
+
uq += " and ( %s )" % (" or ".join(zsl),)
|
1258
|
+
|
1259
|
+
zs1 = self.uparam.get("sort", self.args.rss_sort)
|
1260
|
+
zs2 = zs1.lower()
|
1261
|
+
zs = RSS_SORT.get(zs2)
|
1262
|
+
if not zs:
|
1263
|
+
raise Pebkac(400, "invalid sort key; must be m/u/n/s")
|
1264
|
+
|
1265
|
+
uq += " order by up." + zs
|
1266
|
+
if zs1 == zs2:
|
1267
|
+
uq += " desc"
|
1268
|
+
|
1269
|
+
nmax = int(self.uparam.get("nf") or self.args.rss_nf)
|
1270
|
+
|
1271
|
+
hits = idx.run_query(self.uname, [self.vn], uq, uv, False, False, nmax)[0]
|
1272
|
+
|
1273
|
+
pw = self.ouparam.get("pw")
|
1274
|
+
if pw:
|
1275
|
+
q_pw = "?pw=%s" % (pw,)
|
1276
|
+
a_pw = "&pw=%s" % (pw,)
|
1277
|
+
for i in hits:
|
1278
|
+
i["rp"] += a_pw if "?" in i["rp"] else q_pw
|
1279
|
+
else:
|
1280
|
+
q_pw = a_pw = ""
|
1281
|
+
|
1282
|
+
title = self.uparam.get("title") or self.vpath.split("/")[-1]
|
1283
|
+
etitle = html_escape(title, True, True)
|
1284
|
+
|
1285
|
+
baseurl = "%s://%s%s" % (
|
1286
|
+
"https" if self.is_https else "http",
|
1287
|
+
self.host,
|
1288
|
+
self.args.SRS,
|
1289
|
+
)
|
1290
|
+
feed = "%s%s" % (baseurl, self.req[1:])
|
1291
|
+
efeed = html_escape(feed, True, True)
|
1292
|
+
edirlink = efeed.split("?")[0] + q_pw
|
1293
|
+
|
1294
|
+
ret = [
|
1295
|
+
"""\
|
1296
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
1297
|
+
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:content="http://purl.org/rss/1.0/modules/content/">
|
1298
|
+
\t<channel>
|
1299
|
+
\t\t<atom:link href="%s" rel="self" type="application/rss+xml" />
|
1300
|
+
\t\t<title>%s</title>
|
1301
|
+
\t\t<description></description>
|
1302
|
+
\t\t<link>%s</link>
|
1303
|
+
\t\t<generator>copyparty-1</generator>
|
1304
|
+
"""
|
1305
|
+
% (efeed, etitle, edirlink)
|
1306
|
+
]
|
1307
|
+
|
1308
|
+
q = "select fn from cv where rd=? and dn=?"
|
1309
|
+
crd, cdn = rem.rsplit("/", 1) if "/" in rem else ("", rem)
|
1310
|
+
try:
|
1311
|
+
cfn = idx.cur[self.vn.realpath].execute(q, (crd, cdn)).fetchone()[0]
|
1312
|
+
bos.stat(os.path.join(vn.canonical(rem), cfn))
|
1313
|
+
cv_url = "%s%s?th=jf%s" % (baseurl, vjoin(self.vpath, cfn), a_pw)
|
1314
|
+
cv_url = html_escape(cv_url, True, True)
|
1315
|
+
zs = """\
|
1316
|
+
\t\t<image>
|
1317
|
+
\t\t\t<url>%s</url>
|
1318
|
+
\t\t\t<title>%s</title>
|
1319
|
+
\t\t\t<link>%s</link>
|
1320
|
+
\t\t</image>
|
1321
|
+
"""
|
1322
|
+
ret.append(zs % (cv_url, etitle, edirlink))
|
1323
|
+
except:
|
1324
|
+
pass
|
1325
|
+
|
1326
|
+
for i in hits:
|
1327
|
+
iurl = html_escape("%s%s" % (baseurl, i["rp"]), True, True)
|
1328
|
+
title = unquotep(i["rp"].split("?")[0].split("/")[-1])
|
1329
|
+
title = html_escape(title, True, True)
|
1330
|
+
tag_t = str(i["tags"].get("title") or "")
|
1331
|
+
tag_a = str(i["tags"].get("artist") or "")
|
1332
|
+
desc = "%s - %s" % (tag_a, tag_t) if tag_t and tag_a else (tag_t or tag_a)
|
1333
|
+
desc = html_escape(desc, True, True) if desc else title
|
1334
|
+
mime = html_escape(guess_mime(title))
|
1335
|
+
lmod = formatdate(i["ts"])
|
1336
|
+
zsa = (iurl, iurl, title, desc, lmod, iurl, mime, i["sz"])
|
1337
|
+
zs = (
|
1338
|
+
"""\
|
1339
|
+
\t\t<item>
|
1340
|
+
\t\t\t<guid>%s</guid>
|
1341
|
+
\t\t\t<link>%s</link>
|
1342
|
+
\t\t\t<title>%s</title>
|
1343
|
+
\t\t\t<description>%s</description>
|
1344
|
+
\t\t\t<pubDate>%s</pubDate>
|
1345
|
+
\t\t\t<enclosure url="%s" type="%s" length="%d"/>
|
1346
|
+
"""
|
1347
|
+
% zsa
|
1348
|
+
)
|
1349
|
+
dur = i["tags"].get(".dur")
|
1350
|
+
if dur:
|
1351
|
+
zs += "\t\t\t<itunes:duration>%d</itunes:duration>\n" % (dur,)
|
1352
|
+
ret.append(zs + "\t\t</item>\n")
|
1353
|
+
|
1354
|
+
ret.append("\t</channel>\n</rss>\n")
|
1355
|
+
bret = "".join(ret).encode("utf-8", "replace")
|
1356
|
+
self.reply(bret, 200, "text/xml; charset=utf-8")
|
1357
|
+
self.log("rss: %d hits, %d bytes" % (len(hits), len(bret)))
|
1358
|
+
return True
|
1359
|
+
|
1201
1360
|
def handle_propfind(self) :
|
1202
1361
|
if self.do_log:
|
1203
1362
|
self.log("PFIND %s @%s" % (self.req, self.uname))
|
@@ -2240,6 +2399,15 @@ class HttpCli(object):
|
|
2240
2399
|
if "purl" in ret:
|
2241
2400
|
ret["purl"] = self.args.SR + ret["purl"]
|
2242
2401
|
|
2402
|
+
if self.args.shr and self.vpath.startswith(self.args.shr1):
|
2403
|
+
# strip common suffix (uploader's folder structure)
|
2404
|
+
vp_req, vp_vfs = vroots(self.vpath, vjoin(dbv.vpath, vrem))
|
2405
|
+
if not ret["purl"].startswith(vp_vfs):
|
2406
|
+
t = "share-mapping failed; req=[%s] dbv=[%s] vrem=[%s] n1=[%s] n2=[%s] purl=[%s]"
|
2407
|
+
zt = (self.vpath, dbv.vpath, vrem, vp_req, vp_vfs, ret["purl"])
|
2408
|
+
raise Pebkac(500, t % zt)
|
2409
|
+
ret["purl"] = vp_req + ret["purl"][len(vp_vfs) :]
|
2410
|
+
|
2243
2411
|
ret = json.dumps(ret)
|
2244
2412
|
self.log(ret)
|
2245
2413
|
self.reply(ret.encode("utf-8"), mime="application/json")
|
@@ -2332,7 +2500,11 @@ class HttpCli(object):
|
|
2332
2500
|
chashes.append(siblings[n : n + clen])
|
2333
2501
|
|
2334
2502
|
vfs, _ = self.asrv.vfs.get(self.vpath, self.uname, False, True)
|
2335
|
-
ptop =
|
2503
|
+
ptop = vfs.get_dbv("")[0].realpath
|
2504
|
+
# if this is a share, then get_dbv has been overridden to return
|
2505
|
+
# the dbv (which does not exist as a property). And its realpath
|
2506
|
+
# could point into the middle of its origin vfs node, meaning it
|
2507
|
+
# is not necessarily registered with up2k, so get_dbv is crucial
|
2336
2508
|
|
2337
2509
|
broker = self.conn.hsrv.broker
|
2338
2510
|
x = broker.ask("up2k.handle_chunks", ptop, wark, chashes)
|
@@ -2481,6 +2653,7 @@ class HttpCli(object):
|
|
2481
2653
|
except:
|
2482
2654
|
# maybe busted handle (eg. disk went full)
|
2483
2655
|
f.close()
|
2656
|
+
chashes = [] # exception flag
|
2484
2657
|
raise
|
2485
2658
|
finally:
|
2486
2659
|
if locked:
|
@@ -2489,9 +2662,11 @@ class HttpCli(object):
|
|
2489
2662
|
num_left, t = x.get()
|
2490
2663
|
if num_left < 0:
|
2491
2664
|
self.loud_reply(t, status=500)
|
2492
|
-
|
2493
|
-
|
2494
|
-
|
2665
|
+
if chashes: # kills exception bubbling otherwise
|
2666
|
+
return False
|
2667
|
+
else:
|
2668
|
+
t = "got %d more chunks, %d left"
|
2669
|
+
self.log(t % (len(written), num_left), 6)
|
2495
2670
|
|
2496
2671
|
if num_left < 0:
|
2497
2672
|
raise Pebkac(500, "unconfirmed; see serverlog")
|
@@ -3235,25 +3410,29 @@ class HttpCli(object):
|
|
3235
3410
|
self.reply(response.encode("utf-8"))
|
3236
3411
|
return True
|
3237
3412
|
|
3238
|
-
def _chk_lastmod(self, file_ts )
|
3413
|
+
def _chk_lastmod(self, file_ts ) :
|
3414
|
+
# ret: lastmod, do_send, can_range
|
3239
3415
|
file_lastmod = formatdate(file_ts)
|
3240
|
-
|
3241
|
-
if
|
3242
|
-
|
3243
|
-
|
3244
|
-
|
3245
|
-
|
3246
|
-
|
3247
|
-
|
3248
|
-
|
3249
|
-
|
3250
|
-
|
3251
|
-
|
3252
|
-
|
3253
|
-
|
3254
|
-
|
3416
|
+
c_ifrange = self.headers.get("if-range")
|
3417
|
+
c_lastmod = self.headers.get("if-modified-since")
|
3418
|
+
|
3419
|
+
if not c_ifrange and not c_lastmod:
|
3420
|
+
return file_lastmod, True, True
|
3421
|
+
|
3422
|
+
if c_ifrange and c_ifrange != file_lastmod:
|
3423
|
+
t = "sending entire file due to If-Range; cli(%s) file(%s)"
|
3424
|
+
self.log(t % (c_ifrange, file_lastmod), 6)
|
3425
|
+
return file_lastmod, True, False
|
3426
|
+
|
3427
|
+
do_send = c_lastmod != file_lastmod
|
3428
|
+
if do_send and c_lastmod:
|
3429
|
+
t = "sending body due to If-Modified-Since cli(%s) file(%s)"
|
3430
|
+
self.log(t % (c_lastmod, file_lastmod), 6)
|
3431
|
+
elif not do_send and self.no304():
|
3432
|
+
do_send = True
|
3433
|
+
self.log("sending body due to no304")
|
3255
3434
|
|
3256
|
-
return file_lastmod, True
|
3435
|
+
return file_lastmod, do_send, True
|
3257
3436
|
|
3258
3437
|
def _use_dirkey(self, vn , ap ) :
|
3259
3438
|
if self.can_read or not self.can_get:
|
@@ -3403,7 +3582,7 @@ class HttpCli(object):
|
|
3403
3582
|
# if-modified
|
3404
3583
|
|
3405
3584
|
if file_ts > 0:
|
3406
|
-
file_lastmod, do_send = self._chk_lastmod(int(file_ts))
|
3585
|
+
file_lastmod, do_send, _ = self._chk_lastmod(int(file_ts))
|
3407
3586
|
self.out_headers["Last-Modified"] = file_lastmod
|
3408
3587
|
if not do_send:
|
3409
3588
|
status = 304
|
@@ -3565,7 +3744,7 @@ class HttpCli(object):
|
|
3565
3744
|
#
|
3566
3745
|
# if-modified
|
3567
3746
|
|
3568
|
-
file_lastmod, do_send = self._chk_lastmod(int(file_ts))
|
3747
|
+
file_lastmod, do_send, can_range = self._chk_lastmod(int(file_ts))
|
3569
3748
|
self.out_headers["Last-Modified"] = file_lastmod
|
3570
3749
|
if not do_send:
|
3571
3750
|
status = 304
|
@@ -3609,7 +3788,14 @@ class HttpCli(object):
|
|
3609
3788
|
|
3610
3789
|
# let's not support 206 with compression
|
3611
3790
|
# and multirange / multipart is also not-impl (mostly because calculating contentlength is a pain)
|
3612
|
-
if
|
3791
|
+
if (
|
3792
|
+
do_send
|
3793
|
+
and not is_compressed
|
3794
|
+
and hrange
|
3795
|
+
and can_range
|
3796
|
+
and file_sz
|
3797
|
+
and "," not in hrange
|
3798
|
+
):
|
3613
3799
|
try:
|
3614
3800
|
if not hrange.lower().startswith("bytes"):
|
3615
3801
|
raise Exception()
|
@@ -4080,7 +4266,7 @@ class HttpCli(object):
|
|
4080
4266
|
sz_md = len(lead) + len(fullfile)
|
4081
4267
|
|
4082
4268
|
file_ts = int(max(ts_md, self.E.t0))
|
4083
|
-
file_lastmod, do_send = self._chk_lastmod(file_ts)
|
4269
|
+
file_lastmod, do_send, _ = self._chk_lastmod(file_ts)
|
4084
4270
|
self.out_headers["Last-Modified"] = file_lastmod
|
4085
4271
|
self.out_headers.update(NO_CACHE)
|
4086
4272
|
status = 200 if do_send else 304
|
@@ -4266,7 +4452,7 @@ class HttpCli(object):
|
|
4266
4452
|
rvol=rvol,
|
4267
4453
|
wvol=wvol,
|
4268
4454
|
avol=avol,
|
4269
|
-
in_shr=self.args.shr and self.vpath.startswith(self.args.
|
4455
|
+
in_shr=self.args.shr and self.vpath.startswith(self.args.shr1),
|
4270
4456
|
vstate=vstate,
|
4271
4457
|
ups=ups,
|
4272
4458
|
scanning=vs["scanning"],
|
@@ -4276,7 +4462,9 @@ class HttpCli(object):
|
|
4276
4462
|
dbwt=vs["dbwt"],
|
4277
4463
|
url_suf=suf,
|
4278
4464
|
k304=self.k304(),
|
4465
|
+
no304=self.no304(),
|
4279
4466
|
k304vis=self.args.k304 > 0,
|
4467
|
+
no304vis=self.args.no304 > 0,
|
4280
4468
|
ver=S_VERSION if self.args.ver else "",
|
4281
4469
|
chpw=self.args.chpw and self.uname != "*",
|
4282
4470
|
ahttps="" if self.is_https else "https://" + self.host + self.req,
|
@@ -4284,29 +4472,21 @@ class HttpCli(object):
|
|
4284
4472
|
self.reply(html.encode("utf-8"))
|
4285
4473
|
return True
|
4286
4474
|
|
4287
|
-
def set_k304(self) :
|
4288
|
-
v = self.uparam["k304"].lower()
|
4289
|
-
if v in "yn":
|
4290
|
-
dur = 86400 * 299
|
4291
|
-
else:
|
4292
|
-
dur = 0
|
4293
|
-
v = "x"
|
4294
|
-
|
4295
|
-
ck = gencookie("k304", v, self.args.R, False, dur)
|
4296
|
-
self.out_headerlist.append(("Set-Cookie", ck))
|
4297
|
-
self.redirect("", "?h#cc")
|
4298
|
-
return True
|
4299
|
-
|
4300
4475
|
def setck(self) :
|
4301
4476
|
k, v = self.uparam["setck"].split("=", 1)
|
4302
|
-
t = 0 if v
|
4477
|
+
t = 0 if v in ("", "x") else 86400 * 299
|
4303
4478
|
ck = gencookie(k, v, self.args.R, False, t)
|
4304
4479
|
self.out_headerlist.append(("Set-Cookie", ck))
|
4305
|
-
self.
|
4480
|
+
if "cc" in self.ouparam:
|
4481
|
+
self.redirect("", "?h#cc")
|
4482
|
+
else:
|
4483
|
+
self.reply(b"o7\n")
|
4306
4484
|
return True
|
4307
4485
|
|
4308
4486
|
def set_cfg_reset(self) :
|
4309
|
-
for k in
|
4487
|
+
for k in ALL_COOKIES:
|
4488
|
+
if k not in self.cookies:
|
4489
|
+
continue
|
4310
4490
|
cookie = gencookie(k, "x", self.args.R, False)
|
4311
4491
|
self.out_headerlist.append(("Set-Cookie", cookie))
|
4312
4492
|
|
@@ -4336,7 +4516,7 @@ class HttpCli(object):
|
|
4336
4516
|
|
4337
4517
|
t = t.format(self.args.SR)
|
4338
4518
|
qv = quotep(self.vpaths) + self.ourlq()
|
4339
|
-
in_shr = self.args.shr and self.vpath.startswith(self.args.
|
4519
|
+
in_shr = self.args.shr and self.vpath.startswith(self.args.shr1)
|
4340
4520
|
html = self.j2s("splash", this=self, qvpath=qv, in_shr=in_shr, msg=t)
|
4341
4521
|
self.reply(html.encode("utf-8"), status=rc)
|
4342
4522
|
return True
|
@@ -4499,6 +4679,11 @@ class HttpCli(object):
|
|
4499
4679
|
lm = "ups [{}]".format(filt)
|
4500
4680
|
self.log(lm)
|
4501
4681
|
|
4682
|
+
if self.args.shr and self.vpath.startswith(self.args.shr1):
|
4683
|
+
shr_dbv, shr_vrem = self.vn.get_dbv(self.rem)
|
4684
|
+
else:
|
4685
|
+
shr_dbv = None
|
4686
|
+
|
4502
4687
|
ret = []
|
4503
4688
|
t0 = time.time()
|
4504
4689
|
lim = time.time() - self.args.unpost
|
@@ -4519,7 +4704,12 @@ class HttpCli(object):
|
|
4519
4704
|
else:
|
4520
4705
|
allvols = list(self.asrv.vfs.all_vols.values())
|
4521
4706
|
|
4522
|
-
allvols = [
|
4707
|
+
allvols = [
|
4708
|
+
x
|
4709
|
+
for x in allvols
|
4710
|
+
if "e2d" in x.flags
|
4711
|
+
and ("*" in x.axs.uwrite or self.uname in x.axs.uwrite or x == shr_dbv)
|
4712
|
+
]
|
4523
4713
|
|
4524
4714
|
for vol in allvols:
|
4525
4715
|
cur = idx.get_cur(vol)
|
@@ -4569,6 +4759,15 @@ class HttpCli(object):
|
|
4569
4759
|
|
4570
4760
|
ret = ret[:2000]
|
4571
4761
|
|
4762
|
+
if shr_dbv:
|
4763
|
+
# translate vpaths from share-target to share-url
|
4764
|
+
# to satisfy access checks
|
4765
|
+
vp_shr, vp_vfs = vroots(self.vpath, vjoin(shr_dbv.vpath, shr_vrem))
|
4766
|
+
for v in ret:
|
4767
|
+
vp = v["vp"]
|
4768
|
+
if vp.startswith(vp_vfs):
|
4769
|
+
v["vp"] = vp_shr + vp[len(vp_vfs) :]
|
4770
|
+
|
4572
4771
|
if self.is_vproxied:
|
4573
4772
|
for v in ret:
|
4574
4773
|
v["vp"] = self.args.SR + v["vp"]
|
@@ -4698,7 +4897,7 @@ class HttpCli(object):
|
|
4698
4897
|
if m:
|
4699
4898
|
raise Pebkac(400, "sharekey has illegal character [%s]" % (m[1],))
|
4700
4899
|
|
4701
|
-
if vp.startswith(self.args.
|
4900
|
+
if vp.startswith(self.args.shr1):
|
4702
4901
|
raise Pebkac(400, "yo dawg...")
|
4703
4902
|
|
4704
4903
|
cur = idx.get_shr()
|
@@ -5082,7 +5281,7 @@ class HttpCli(object):
|
|
5082
5281
|
self.log("#wow #whoa")
|
5083
5282
|
|
5084
5283
|
if not self.args.nid:
|
5085
|
-
free, total = get_df(abspath)
|
5284
|
+
free, total, _ = get_df(abspath, False)
|
5086
5285
|
if total is not None:
|
5087
5286
|
h1 = humansize(free or 0)
|
5088
5287
|
h2 = humansize(total)
|
copyparty/metrics.py
CHANGED
@@ -128,7 +128,7 @@ class Metrics(object):
|
|
128
128
|
addbh("cpp_disk_size_bytes", "total HDD size of volume")
|
129
129
|
addbh("cpp_disk_free_bytes", "free HDD space in volume")
|
130
130
|
for vpath, vol in allvols:
|
131
|
-
free, total = get_df(vol.realpath)
|
131
|
+
free, total, _ = get_df(vol.realpath, False)
|
132
132
|
if free is None or total is None:
|
133
133
|
continue
|
134
134
|
|
copyparty/svchub.py
CHANGED
@@ -217,13 +217,14 @@ class SvcHub(object):
|
|
217
217
|
args.chpw_no = noch
|
218
218
|
|
219
219
|
if args.ipu:
|
220
|
-
iu, nm = load_ipu(self.log, args.ipu)
|
220
|
+
iu, nm = load_ipu(self.log, args.ipu, True)
|
221
221
|
setattr(args, "ipu_iu", iu)
|
222
222
|
setattr(args, "ipu_nm", nm)
|
223
223
|
|
224
224
|
if not self.args.no_ses:
|
225
225
|
self.setup_session_db()
|
226
226
|
|
227
|
+
args.shr1 = ""
|
227
228
|
if args.shr:
|
228
229
|
self.setup_share_db()
|
229
230
|
|
@@ -372,6 +373,14 @@ class SvcHub(object):
|
|
372
373
|
|
373
374
|
self.broker = Broker(self)
|
374
375
|
|
376
|
+
# create netmaps early to avoid firewall gaps,
|
377
|
+
# but the mutex blocks multiprocessing startup
|
378
|
+
for zs in "ipu_iu ftp_ipa_nm tftp_ipa_nm".split():
|
379
|
+
try:
|
380
|
+
getattr(args, zs).mutex = threading.Lock()
|
381
|
+
except:
|
382
|
+
pass
|
383
|
+
|
375
384
|
def setup_session_db(self) :
|
376
385
|
if not HAVE_SQLITE3:
|
377
386
|
self.args.no_ses = True
|
@@ -444,6 +453,7 @@ class SvcHub(object):
|
|
444
453
|
raise Exception(t)
|
445
454
|
|
446
455
|
al.shr = "/%s/" % (al.shr,)
|
456
|
+
al.shr1 = al.shr[1:]
|
447
457
|
|
448
458
|
create = True
|
449
459
|
modified = False
|
@@ -751,8 +761,8 @@ class SvcHub(object):
|
|
751
761
|
al.idp_h_grp = al.idp_h_grp.lower()
|
752
762
|
al.idp_h_key = al.idp_h_key.lower()
|
753
763
|
|
754
|
-
al.ftp_ipa_nm = build_netmap(al.ftp_ipa or al.ipa)
|
755
|
-
al.tftp_ipa_nm = build_netmap(al.tftp_ipa or al.ipa)
|
764
|
+
al.ftp_ipa_nm = build_netmap(al.ftp_ipa or al.ipa, True)
|
765
|
+
al.tftp_ipa_nm = build_netmap(al.tftp_ipa or al.ipa, True)
|
756
766
|
|
757
767
|
mte = ODict.fromkeys(DEF_MTE.split(","), True)
|
758
768
|
al.mte = odfusion(mte, al.mte)
|
@@ -799,6 +809,24 @@ class SvcHub(object):
|
|
799
809
|
if len(al.tcolor) == 3: # fc5 => ffcc55
|
800
810
|
al.tcolor = "".join([x * 2 for x in al.tcolor])
|
801
811
|
|
812
|
+
zs = al.u2sz
|
813
|
+
zsl = zs.split(",")
|
814
|
+
if len(zsl) not in (1, 3):
|
815
|
+
t = "invalid --u2sz; must be either one number, or a comma-separated list of three numbers (min,default,max)"
|
816
|
+
raise Exception(t)
|
817
|
+
if len(zsl) < 3:
|
818
|
+
zsl = ["1", zs, zs]
|
819
|
+
zi2 = 1
|
820
|
+
for zs in zsl:
|
821
|
+
zi = int(zs)
|
822
|
+
# arbitrary constraint (anything above 2 GiB is probably unintended)
|
823
|
+
if zi < 1 or zi > 2047:
|
824
|
+
raise Exception("invalid --u2sz; minimum is 1, max is 2047")
|
825
|
+
if zi < zi2:
|
826
|
+
raise Exception("invalid --u2sz; values must be equal or ascending")
|
827
|
+
zi2 = zi
|
828
|
+
al.u2sz = ",".join(zsl)
|
829
|
+
|
802
830
|
return True
|
803
831
|
|
804
832
|
def _ipa2re(self, txt) :
|
copyparty/u2idx.py
CHANGED
@@ -91,7 +91,7 @@ class U2idx(object):
|
|
91
91
|
uv = [wark[:16], wark]
|
92
92
|
|
93
93
|
try:
|
94
|
-
return self.run_query(uname, vols, uq, uv, False, 99999)[0]
|
94
|
+
return self.run_query(uname, vols, uq, uv, False, True, 99999)[0]
|
95
95
|
except:
|
96
96
|
raise Pebkac(500, min_ex())
|
97
97
|
|
@@ -295,7 +295,7 @@ class U2idx(object):
|
|
295
295
|
q += " lower({}) {} ? ) ".format(field, oper)
|
296
296
|
|
297
297
|
try:
|
298
|
-
return self.run_query(uname, vols, q, va, have_mt, lim)
|
298
|
+
return self.run_query(uname, vols, q, va, have_mt, True, lim)
|
299
299
|
except Exception as ex:
|
300
300
|
raise Pebkac(500, repr(ex))
|
301
301
|
|
@@ -306,6 +306,7 @@ class U2idx(object):
|
|
306
306
|
uq ,
|
307
307
|
uv ,
|
308
308
|
have_mt ,
|
309
|
+
sort ,
|
309
310
|
lim ,
|
310
311
|
) :
|
311
312
|
if self.args.srch_dbg:
|
@@ -452,7 +453,8 @@ class U2idx(object):
|
|
452
453
|
done_flag.append(True)
|
453
454
|
self.active_id = ""
|
454
455
|
|
455
|
-
|
456
|
+
if sort:
|
457
|
+
ret.sort(key=itemgetter("rp"))
|
456
458
|
|
457
459
|
return ret, list(taglist.keys()), lim < 0 and not clamped
|
458
460
|
|
copyparty/up2k.py
CHANGED
@@ -3481,6 +3481,7 @@ class Up2k(object):
|
|
3481
3481
|
for chash in written:
|
3482
3482
|
job["need"].remove(chash)
|
3483
3483
|
except Exception as ex:
|
3484
|
+
# dead tcp connections can get here by timeout (OK)
|
3484
3485
|
return -2, "confirm_chunk, chash(%s) %r" % (chash, ex) # type: ignore
|
3485
3486
|
|
3486
3487
|
ret = len(job["need"])
|
@@ -3885,11 +3886,9 @@ class Up2k(object):
|
|
3885
3886
|
if unpost:
|
3886
3887
|
raise Pebkac(400, "cannot unpost folders")
|
3887
3888
|
elif stat.S_ISLNK(st.st_mode) or stat.S_ISREG(st.st_mode):
|
3888
|
-
|
3889
|
-
dbv, vrem = dbv.get_dbv(vrem)
|
3890
|
-
voldir = vsplit(vrem)[0]
|
3889
|
+
voldir = vsplit(rem)[0]
|
3891
3890
|
vpath_dir = vsplit(vpath)[0]
|
3892
|
-
g = [(
|
3891
|
+
g = [(vn, voldir, vpath_dir, adir, [(fn, 0)], [], {})] # type: ignore
|
3893
3892
|
else:
|
3894
3893
|
self.log("rm: skip type-{:x} file [{}]".format(st.st_mode, atop))
|
3895
3894
|
return 0, [], []
|
@@ -3916,7 +3915,10 @@ class Up2k(object):
|
|
3916
3915
|
volpath = ("%s/%s" % (vrem, fn)).strip("/")
|
3917
3916
|
vpath = ("%s/%s" % (dbv.vpath, volpath)).strip("/")
|
3918
3917
|
self.log("rm %s\n %s" % (vpath, abspath))
|
3919
|
-
|
3918
|
+
if not unpost:
|
3919
|
+
# recursion-only sanchk
|
3920
|
+
_ = dbv.get(volpath, uname, *permsets[0])
|
3921
|
+
|
3920
3922
|
if xbd:
|
3921
3923
|
if not runhook(
|
3922
3924
|
self.log,
|
copyparty/util.py
CHANGED
@@ -192,6 +192,9 @@ except:
|
|
192
192
|
ansi_re = re.compile("\033\\[[^mK]*[mK]")
|
193
193
|
|
194
194
|
|
195
|
+
BOS_SEP = ("%s" % (os.sep,)).encode("ascii")
|
196
|
+
|
197
|
+
|
195
198
|
surrogateescape.register_surrogateescape()
|
196
199
|
if WINDOWS and PY2:
|
197
200
|
FS_ENCODING = "utf-8"
|
@@ -644,13 +647,20 @@ class HLog(logging.Handler):
|
|
644
647
|
|
645
648
|
class NetMap(object):
|
646
649
|
def __init__(
|
647
|
-
self,
|
650
|
+
self,
|
651
|
+
ips ,
|
652
|
+
cidrs ,
|
653
|
+
keep_lo=False,
|
654
|
+
strict_cidr=False,
|
655
|
+
defer_mutex=False,
|
648
656
|
) :
|
649
657
|
"""
|
650
658
|
ips: list of plain ipv4/ipv6 IPs, not cidr
|
651
659
|
cidrs: list of cidr-notation IPs (ip/prefix)
|
652
660
|
"""
|
653
|
-
|
661
|
+
|
662
|
+
# fails multiprocessing; defer assignment
|
663
|
+
self.mutex = None if defer_mutex else threading.Lock()
|
654
664
|
|
655
665
|
if "::" in ips:
|
656
666
|
ips = [x for x in ips if x != "::"] + list(
|
@@ -689,6 +699,8 @@ class NetMap(object):
|
|
689
699
|
try:
|
690
700
|
return self.cache[ip]
|
691
701
|
except:
|
702
|
+
# intentionally crash the calling thread if unset:
|
703
|
+
|
692
704
|
with self.mutex:
|
693
705
|
return self._map(ip)
|
694
706
|
|
@@ -2106,6 +2118,23 @@ def unquotep(txt ) :
|
|
2106
2118
|
return w8dec(unq2)
|
2107
2119
|
|
2108
2120
|
|
2121
|
+
def vroots(vp1 , vp2 ) :
|
2122
|
+
"""
|
2123
|
+
input("q/w/e/r","a/s/d/e/r") output("/q/w/","/a/s/d/")
|
2124
|
+
"""
|
2125
|
+
while vp1 and vp2:
|
2126
|
+
zt1 = vp1.rsplit("/", 1) if "/" in vp1 else ("", vp1)
|
2127
|
+
zt2 = vp2.rsplit("/", 1) if "/" in vp2 else ("", vp2)
|
2128
|
+
if zt1[1] != zt2[1]:
|
2129
|
+
break
|
2130
|
+
vp1 = zt1[0]
|
2131
|
+
vp2 = zt2[0]
|
2132
|
+
return (
|
2133
|
+
"/%s/" % (vp1,) if vp1 else "/",
|
2134
|
+
"/%s/" % (vp2,) if vp2 else "/",
|
2135
|
+
)
|
2136
|
+
|
2137
|
+
|
2109
2138
|
def vsplit(vpath ) :
|
2110
2139
|
if "/" not in vpath:
|
2111
2140
|
return "", vpath
|
@@ -2407,22 +2436,27 @@ def wunlink(log , abspath , flags ) :
|
|
2407
2436
|
return _fs_mvrm(log, abspath, "", False, flags)
|
2408
2437
|
|
2409
2438
|
|
2410
|
-
def get_df(abspath )
|
2439
|
+
def get_df(abspath , prune ) :
|
2411
2440
|
try:
|
2412
|
-
|
2441
|
+
ap = fsenc(abspath)
|
2442
|
+
while prune and not os.path.isdir(ap) and BOS_SEP in ap:
|
2443
|
+
# strip leafs until it hits an existing folder
|
2444
|
+
ap = ap.rsplit(BOS_SEP, 1)[0]
|
2445
|
+
|
2413
2446
|
if ANYWIN:
|
2447
|
+
abspath = fsdec(ap)
|
2414
2448
|
bfree = ctypes.c_ulonglong(0)
|
2415
2449
|
ctypes.windll.kernel32.GetDiskFreeSpaceExW( # type: ignore
|
2416
2450
|
ctypes.c_wchar_p(abspath), None, None, ctypes.pointer(bfree)
|
2417
2451
|
)
|
2418
|
-
return (bfree.value, None)
|
2452
|
+
return (bfree.value, None, "")
|
2419
2453
|
else:
|
2420
|
-
sv = os.statvfs(
|
2454
|
+
sv = os.statvfs(ap)
|
2421
2455
|
free = sv.f_frsize * sv.f_bfree
|
2422
2456
|
total = sv.f_frsize * sv.f_blocks
|
2423
|
-
return (free, total)
|
2424
|
-
except:
|
2425
|
-
return (None, None)
|
2457
|
+
return (free, total, "")
|
2458
|
+
except Exception as ex:
|
2459
|
+
return (None, None, repr(ex))
|
2426
2460
|
|
2427
2461
|
|
2428
2462
|
if not ANYWIN and not MACOS:
|
@@ -2560,7 +2594,7 @@ def list_ips() :
|
|
2560
2594
|
return list(ret)
|
2561
2595
|
|
2562
2596
|
|
2563
|
-
def build_netmap(csv ):
|
2597
|
+
def build_netmap(csv , defer_mutex = False):
|
2564
2598
|
csv = csv.lower().strip()
|
2565
2599
|
|
2566
2600
|
if csv in ("any", "all", "no", ",", ""):
|
@@ -2595,10 +2629,12 @@ def build_netmap(csv ):
|
|
2595
2629
|
cidrs.append(zs)
|
2596
2630
|
|
2597
2631
|
ips = [x.split("/")[0] for x in cidrs]
|
2598
|
-
return NetMap(ips, cidrs, True)
|
2632
|
+
return NetMap(ips, cidrs, True, False, defer_mutex)
|
2599
2633
|
|
2600
2634
|
|
2601
|
-
def load_ipu(
|
2635
|
+
def load_ipu(
|
2636
|
+
log , ipus , defer_mutex = False
|
2637
|
+
) :
|
2602
2638
|
ip_u = {"": "*"}
|
2603
2639
|
cidr_u = {}
|
2604
2640
|
for ipu in ipus:
|
@@ -2615,7 +2651,7 @@ def load_ipu(log , ipus ) :
|
|
2615
2651
|
cidr_u[cidr] = uname
|
2616
2652
|
ip_u[cip] = uname
|
2617
2653
|
try:
|
2618
|
-
nm = NetMap(["::"], list(cidr_u.keys()), True, True)
|
2654
|
+
nm = NetMap(["::"], list(cidr_u.keys()), True, True, defer_mutex)
|
2619
2655
|
except Exception as ex:
|
2620
2656
|
t = "failed to translate --ipu into netmap, probably due to invalid config: %r"
|
2621
2657
|
log("root", t % (ex,), 1)
|
copyparty/web/a/partyfuse.py
CHANGED
@@ -844,7 +844,7 @@ def main():
|
|
844
844
|
|
845
845
|
# dircache is always a boost,
|
846
846
|
# only want to disable it for tests etc,
|
847
|
-
cdn =
|
847
|
+
cdn = 24 # max num dirs; keep larger than max dir depth; 0=disable
|
848
848
|
cds = 1 # numsec until an entry goes stale
|
849
849
|
|
850
850
|
where = "local directory"
|
copyparty/web/a/u2c.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
|
-
S_VERSION = "2.
|
5
|
-
S_BUILD_DT = "2024-10-
|
4
|
+
S_VERSION = "2.5"
|
5
|
+
S_BUILD_DT = "2024-10-18"
|
6
6
|
|
7
7
|
"""
|
8
8
|
u2c.py: upload to copyparty
|
@@ -154,6 +154,7 @@ class HCli(object):
|
|
154
154
|
self.tls = tls
|
155
155
|
self.verify = ar.te or not ar.td
|
156
156
|
self.conns = []
|
157
|
+
self.hconns = []
|
157
158
|
if tls:
|
158
159
|
import ssl
|
159
160
|
|
@@ -173,7 +174,7 @@ class HCli(object):
|
|
173
174
|
"User-Agent": "u2c/%s" % (S_VERSION,),
|
174
175
|
}
|
175
176
|
|
176
|
-
def _connect(self):
|
177
|
+
def _connect(self, timeout):
|
177
178
|
args = {}
|
178
179
|
if PY37:
|
179
180
|
args["blocksize"] = 1048576
|
@@ -185,7 +186,7 @@ class HCli(object):
|
|
185
186
|
if self.ctx:
|
186
187
|
args = {"context": self.ctx}
|
187
188
|
|
188
|
-
return C(self.addr, self.port, timeout=
|
189
|
+
return C(self.addr, self.port, timeout=timeout, **args)
|
189
190
|
|
190
191
|
def req(self, meth, vpath, hdrs, body=None, ctype=None):
|
191
192
|
hdrs.update(self.base_hdrs)
|
@@ -198,7 +199,9 @@ class HCli(object):
|
|
198
199
|
0 if not body else body.len if hasattr(body, "len") else len(body)
|
199
200
|
)
|
200
201
|
|
201
|
-
|
202
|
+
# large timeout for handshakes (safededup)
|
203
|
+
conns = self.hconns if ctype == MJ else self.conns
|
204
|
+
c = conns.pop() if conns else self._connect(999 if ctype == MJ else 128)
|
202
205
|
try:
|
203
206
|
c.request(meth, vpath, body, hdrs)
|
204
207
|
if PY27:
|
@@ -207,7 +210,7 @@ class HCli(object):
|
|
207
210
|
rsp = c.getresponse()
|
208
211
|
|
209
212
|
data = rsp.read()
|
210
|
-
|
213
|
+
conns.append(c)
|
211
214
|
return rsp.status, data.decode("utf-8")
|
212
215
|
except:
|
213
216
|
c.close()
|
@@ -868,9 +871,10 @@ def upload(fsl, stats, maxsz):
|
|
868
871
|
if sc >= 400:
|
869
872
|
raise Exception("http %s: %s" % (sc, txt))
|
870
873
|
finally:
|
871
|
-
fsl.f
|
872
|
-
|
873
|
-
|
874
|
+
if fsl.f:
|
875
|
+
fsl.f.close()
|
876
|
+
if nsub != -1:
|
877
|
+
fsl.unsub()
|
874
878
|
|
875
879
|
|
876
880
|
class Ctl(object):
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/splash.html
CHANGED
@@ -129,11 +129,20 @@
|
|
129
129
|
|
130
130
|
{% if k304 or k304vis %}
|
131
131
|
{% if k304 %}
|
132
|
-
<li><a id="h" href="{{ r }}/?k304=n">disable k304</a> (currently enabled)
|
132
|
+
<li><a id="h" href="{{ r }}/?cc&setck=k304=n">disable k304</a> (currently enabled)
|
133
133
|
{%- else %}
|
134
|
-
<li><a id="i" href="{{ r }}/?k304=y" class="r">enable k304</a> (currently disabled)
|
134
|
+
<li><a id="i" href="{{ r }}/?cc&setck=k304=y" class="r">enable k304</a> (currently disabled)
|
135
135
|
{% endif %}
|
136
|
-
<blockquote id="j">enabling
|
136
|
+
<blockquote id="j">enabling k304 will disconnect your client on every HTTP 304, which can prevent some buggy proxies from getting stuck (suddenly not loading pages), <em>but</em> it will also make things slower in general</blockquote></li>
|
137
|
+
{% endif %}
|
138
|
+
|
139
|
+
{% if no304 or no304vis %}
|
140
|
+
{% if no304 %}
|
141
|
+
<li><a id="ab" href="{{ r }}/?cc&setck=no304=n">disable no304</a> (currently enabled)
|
142
|
+
{%- else %}
|
143
|
+
<li><a id="ac" href="{{ r }}/?cc&setck=no304=y" class="r">enable no304</a> (currently disabled)
|
144
|
+
{% endif %}
|
145
|
+
<blockquote id="ad">enabling no304 will disable all caching; try this if k304 wasn't enough. This will waste a huge amount of network traffic!</blockquote></li>
|
137
146
|
{% endif %}
|
138
147
|
|
139
148
|
<li><a id="k" href="{{ r }}/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li>
|
copyparty/web/splash.js.gz
CHANGED
Binary file
|
copyparty/web/up2k.js.gz
CHANGED
Binary file
|
copyparty/web/w.hash.js.gz
CHANGED
Binary file
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.15.
|
3
|
+
Version: 1.15.10
|
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
|
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
21
21
|
Classifier: Programming Language :: Python :: 3.10
|
22
22
|
Classifier: Programming Language :: Python :: 3.11
|
23
23
|
Classifier: Programming Language :: Python :: 3.12
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
24
25
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
25
26
|
Classifier: Programming Language :: Python :: Implementation :: Jython
|
26
27
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
@@ -101,6 +102,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
101
102
|
* [file manager](#file-manager) - cut/paste, rename, and delete files/folders (if you have permission)
|
102
103
|
* [shares](#shares) - share a file or folder by creating a temporary link
|
103
104
|
* [batch rename](#batch-rename) - select some files and press `F2` to bring up the rename UI
|
105
|
+
* [rss feeds](#rss-feeds) - monitor a folder with your RSS reader
|
104
106
|
* [media player](#media-player) - plays almost every audio format there is
|
105
107
|
* [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
|
106
108
|
* [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
|
@@ -899,6 +901,30 @@ or a mix of both:
|
|
899
901
|
the metadata keys you can use in the format field are the ones in the file-browser table header (whatever is collected with `-mte` and `-mtp`)
|
900
902
|
|
901
903
|
|
904
|
+
## rss feeds
|
905
|
+
|
906
|
+
monitor a folder with your RSS reader , optionally recursive
|
907
|
+
|
908
|
+
must be enabled per-volume with volflag `rss` or globally with `--rss`
|
909
|
+
|
910
|
+
the feed includes itunes metadata for use with podcast readers such as [AntennaPod](https://antennapod.org/)
|
911
|
+
|
912
|
+
a feed example: https://cd.ocv.me/a/d2/d22/?rss&fext=mp3
|
913
|
+
|
914
|
+
url parameters:
|
915
|
+
|
916
|
+
* `pw=hunter2` for password auth
|
917
|
+
* `recursive` to also include subfolders
|
918
|
+
* `title=foo` changes the feed title (default: folder name)
|
919
|
+
* `fext=mp3,opus` only include mp3 and opus files (default: all)
|
920
|
+
* `nf=30` only show the first 30 results (default: 250)
|
921
|
+
* `sort=m` sort by mtime (file last-modified), newest first (default)
|
922
|
+
* `u` = upload-time; NOTE: non-uploaded files have upload-time `0`
|
923
|
+
* `n` = filename
|
924
|
+
* `a` = filesize
|
925
|
+
* uppercase = reverse-sort; `M` = oldest file first
|
926
|
+
|
927
|
+
|
902
928
|
## media player
|
903
929
|
|
904
930
|
plays almost every audio format there is (if the server has FFmpeg installed for on-demand transcoding)
|
@@ -1962,6 +1988,9 @@ quick summary of more eccentric web-browsers trying to view a directory index:
|
|
1962
1988
|
| **ie4** and **netscape** 4.0 | can browse, upload with `?b=u`, auth with `&pw=wark` |
|
1963
1989
|
| **ncsa mosaic** 2.7 | does not get a pass, [pic1](https://user-images.githubusercontent.com/241032/174189227-ae816026-cf6f-4be5-a26e-1b3b072c1b2f.png) - [pic2](https://user-images.githubusercontent.com/241032/174189225-5651c059-5152-46e9-ac26-7e98e497901b.png) |
|
1964
1990
|
| **SerenityOS** (7e98457) | hits a page fault, works with `?b=u`, file upload not-impl |
|
1991
|
+
| **nintendo 3ds** | can browse, upload, view thumbnails (thx bnjmn) |
|
1992
|
+
|
1993
|
+
<p align="center"><img src="https://github.com/user-attachments/assets/88deab3d-6cad-4017-8841-2f041472b853" /></p>
|
1965
1994
|
|
1966
1995
|
|
1967
1996
|
# client examples
|
@@ -1,22 +1,22 @@
|
|
1
1
|
copyparty/__init__.py,sha256=Chqw7uXX4r_-a2p6-xthrrqVHFI4aZdW45sWU7UvqeE,2597
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256
|
2
|
+
copyparty/__main__.py,sha256=S9jBx-HMqtUusAjpA64akmN4SKeRMGS5dNfzS4-miC4,111480
|
3
|
+
copyparty/__version__.py,sha256=fkcFvGlCD_NUIpsBFycKnSiV9ebZj5bDCWyy5H38XJw,259
|
4
|
+
copyparty/authsrv.py,sha256=FNb0l3o57R9xnYrZsqjTOqPwWkXGgP9YdX7NWx0ZMow,98955
|
5
5
|
copyparty/broker_mp.py,sha256=jsHUM2BSfRVRyZT869iPCqYEHSqedk6VkwvygZwbEZE,4017
|
6
6
|
copyparty/broker_mpw.py,sha256=PYFgQfssOCfdI6qayW1ZjO1j1-7oez094muhYMbPOz0,3339
|
7
7
|
copyparty/broker_thr.py,sha256=MXrwjusP0z1LPURUhi5jx_TL3jrXhYcDrJPDSKu6EEU,1705
|
8
8
|
copyparty/broker_util.py,sha256=76mfnFOpX1gUUvtjm8UQI7jpTIaVINX10QonM-B7ggc,1680
|
9
9
|
copyparty/cert.py,sha256=0ZAPeXeMR164vWn9GQU3JDKooYXEq_NOQkDeg543ivg,8009
|
10
|
-
copyparty/cfg.py,sha256=
|
10
|
+
copyparty/cfg.py,sha256=E9iBGNjIUrDAPLFRgKsVOmAknP9bDE27xh0gkmNdH1s,10127
|
11
11
|
copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
|
12
12
|
copyparty/fsutil.py,sha256=5CshJWO7CflfaRRNOb3JxghUH7W5rmS_HWNmKfx42MM,4538
|
13
13
|
copyparty/ftpd.py,sha256=G_h1urfIikzfCWGXnW9p-rioWdNM_Je6vWYq0-QSbC8,17580
|
14
|
-
copyparty/httpcli.py,sha256=
|
14
|
+
copyparty/httpcli.py,sha256=rb-QxygFrRM7LEmTadMKn4FZ2bl5ddoPfJXsRrItKDg,201389
|
15
15
|
copyparty/httpconn.py,sha256=mQSgljh0Q-jyWjF4tQLrHbRKRe9WKl19kGqsGMsJpWo,6880
|
16
16
|
copyparty/httpsrv.py,sha256=d_UiGnQKniBoEV68lNFgnYm-byda7uj56mFf-YC7piI,17223
|
17
17
|
copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
|
18
18
|
copyparty/mdns.py,sha256=vC078llnL1v0pvL3mnwacuStFHPJUQuxo9Opj-IbHL4,18155
|
19
|
-
copyparty/metrics.py,sha256
|
19
|
+
copyparty/metrics.py,sha256=-1Rkk44gBh_1YJbdzGZHaqR4pEwkbno6fSdsRb5wDIk,8865
|
20
20
|
copyparty/mtag.py,sha256=8WGjEn0T0Ri9ww1yBpLUnFHZiTQMye1BMXL6SkK3MRo,18893
|
21
21
|
copyparty/multicast.py,sha256=Ha27l2oATEa-Qo2WOzkeRgjAm6G_YDCfbVJWR-ao2UE,12319
|
22
22
|
copyparty/pwhash.py,sha256=AdLMLyIi2IDhGtbKIQOswKUxWvO7ARYYRF_ThsryOoc,4124
|
@@ -24,15 +24,15 @@ copyparty/smbd.py,sha256=Or7RF13cl1r3ncnpVh8BqyAGqH2Oa04O9iPZWCoB0Bo,14609
|
|
24
24
|
copyparty/ssdp.py,sha256=R1Z61GZOxBMF2Sk4RTxKWMOemogmcjEWG-CvLihd45k,7023
|
25
25
|
copyparty/star.py,sha256=tV5BbX6AiQ7N4UU8DYtSTckNYeoeey4DBqq4LjfymbY,3818
|
26
26
|
copyparty/sutil.py,sha256=JTMrQwcWH85hXB_cKG206eDZ967WZDGaP00AWvl_gB0,3214
|
27
|
-
copyparty/svchub.py,sha256=
|
27
|
+
copyparty/svchub.py,sha256=sfA_2BIXf0GP6-vNJGsttcvuVTjMek7TXWoGhYSa1bg,41178
|
28
28
|
copyparty/szip.py,sha256=sDypi1_yR6-62fIZ_3D0L9PfIzCUiK_3JqcaJCvTBCs,8601
|
29
29
|
copyparty/tcpsrv.py,sha256=l_vb9FoF0AJur0IoqHNUSBDqMgBO_MRUZeDszi1UNfY,19881
|
30
30
|
copyparty/tftpd.py,sha256=jZbf2JpeJmkuQWJErmAPG-dKhtYNvIUHbkAgodSXw9Y,13582
|
31
31
|
copyparty/th_cli.py,sha256=o6FMkerYvAXS455z3DUossVztu_nzFlYSQhs6qN6Jt8,4636
|
32
32
|
copyparty/th_srv.py,sha256=hI9wY1E_9N9Cgqvtr8zADeVqqiLGTiTdAnYAA7WFvJw,29346
|
33
|
-
copyparty/u2idx.py,sha256=
|
34
|
-
copyparty/up2k.py,sha256=
|
35
|
-
copyparty/util.py,sha256=
|
33
|
+
copyparty/u2idx.py,sha256=HLO49L1zmpJtBcJiXgD12a6pAlQdnf2pFelHMA7habw,13462
|
34
|
+
copyparty/up2k.py,sha256=PYAcSuiEJAikQSDGNodyv-jXNtGxmN-KB8_hZ4989Qg,165385
|
35
|
+
copyparty/util.py,sha256=bV_zxOiG3GIxt91EkTR7r-YjvR7WOM9IIvg0zuq0s7k,93378
|
36
36
|
copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
37
|
copyparty/bos/bos.py,sha256=Wb7eWsXJgR5AFlBR9ZOyKrLTwy-Kct9RrGiOu4Jo37Y,1622
|
38
38
|
copyparty/bos/path.py,sha256=yEjCq2ki9CvxA5sCT8pS0keEXwugs0ZeUyUhdBziOCI,777
|
@@ -57,7 +57,7 @@ copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8
|
|
57
57
|
copyparty/web/baguettebox.js.gz,sha256=YIaxFDsubJfGIdzzxA-cL6GwJVmpWZyaPhW9hHcOIIw,7964
|
58
58
|
copyparty/web/browser.css.gz,sha256=4bAS9Xkl2fflhaxRSRSVoYQcpXsg1mCWxsYjId7phbU,11610
|
59
59
|
copyparty/web/browser.html,sha256=ISpfvWEawufJCYZIqvuXiyUgiXgjmOTtScz4zrEaypI,4870
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
60
|
+
copyparty/web/browser.js.gz,sha256=yDhq0t-6R9OCv--ca_dCCsg7ZkhJjN3Y-9-PQdZPRpg,85071
|
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
|
@@ -75,17 +75,17 @@ copyparty/web/shares.css.gz,sha256=T2fSezuluDVIiNIERAuUREByhHFlIwwNyx7EBOAVVyQ,4
|
|
75
75
|
copyparty/web/shares.html,sha256=ZNHtLBM-Y4BX2qa9AGTrZzZp_IP5PLM3QvFMYKolFfM,2494
|
76
76
|
copyparty/web/shares.js.gz,sha256=814O61mxLSWs0AO2fbGJ8d4BSPB7pE9NdkKiVf1gj6E,926
|
77
77
|
copyparty/web/splash.css.gz,sha256=RjdNoIT5BSxXRFu0ldMUH4ghRNUMCTs2mGKzstrpI6o,1033
|
78
|
-
copyparty/web/splash.html,sha256=
|
79
|
-
copyparty/web/splash.js.gz,sha256=
|
78
|
+
copyparty/web/splash.html,sha256=_09d2C79S4sIyaks5pPdq1PBgabdlEFhB6Z-KiynwD8,5699
|
79
|
+
copyparty/web/splash.js.gz,sha256=f9aSzI0vw0teK52MBi8Y9FsSMURDfRdZFEvpzqNBon8,2694
|
80
80
|
copyparty/web/svcs.html,sha256=P5YZimYLeQMT0uz6u3clQSNZRc5Zs0Ok-ffcbcGSYuc,11762
|
81
81
|
copyparty/web/svcs.js.gz,sha256=k81ZvZ3I-f4fMHKrNGGOgOlvXnCBz0mVjD-8mieoWCA,520
|
82
82
|
copyparty/web/ui.css.gz,sha256=wloSacrHgP722hy4XiOvVY2GI9-V4zvfvzu84LLWS_o,2779
|
83
|
-
copyparty/web/up2k.js.gz,sha256=
|
83
|
+
copyparty/web/up2k.js.gz,sha256=lGR1Xb0RkIZ1eHmncsSwWRuFc6FC2rZalvjo3oNTV1s,23291
|
84
84
|
copyparty/web/util.js.gz,sha256=NvjPYhIa0-C_NhUyW-Ra-XinUCRjj8G3pYq1zJHYWEk,14805
|
85
|
-
copyparty/web/w.hash.js.gz,sha256=
|
85
|
+
copyparty/web/w.hash.js.gz,sha256=l3GpSJD6mcU-1CRWkIj7PybgbjlfSr8oeO3vortIrQk,1105
|
86
86
|
copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
87
|
-
copyparty/web/a/partyfuse.py,sha256=
|
88
|
-
copyparty/web/a/u2c.py,sha256=
|
87
|
+
copyparty/web/a/partyfuse.py,sha256=efBOupuGVCaGVGylJ-mcx0kz6paqc2sk8kHIprfGUQU,27993
|
88
|
+
copyparty/web/a/u2c.py,sha256=ZmLcGuOWB66ZkAMpJBiR1Xpa75PgpzdFRTt3GGVCorc,49533
|
89
89
|
copyparty/web/a/webdav-cfg.bat,sha256=Y4NoGZlksAIg4cBMb7KdJrpKC6Nx97onaTl6yMjaimk,1449
|
90
90
|
copyparty/web/dd/2.png,sha256=gJ14XFPzaw95L6z92fSq9eMPikSQyu-03P1lgiGe0_I,258
|
91
91
|
copyparty/web/dd/3.png,sha256=4lho8Koz5tV7jJ4ODo6GMTScZfkqsT05yp48EDFIlyg,252
|
@@ -106,9 +106,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
|
|
106
106
|
copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
|
107
107
|
copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
|
108
108
|
copyparty/web/deps/sha512.hw.js.gz,sha256=vqoXeracj-99Z5MfY3jK2N4WiSzYQdfjy0RnUlQDhSU,8110
|
109
|
-
copyparty-1.15.
|
110
|
-
copyparty-1.15.
|
111
|
-
copyparty-1.15.
|
112
|
-
copyparty-1.15.
|
113
|
-
copyparty-1.15.
|
114
|
-
copyparty-1.15.
|
109
|
+
copyparty-1.15.10.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
110
|
+
copyparty-1.15.10.dist-info/METADATA,sha256=DhJZfQ1d9CPtx3LXk0ga7thnke1xWg3GJI_HlTPXZLw,140056
|
111
|
+
copyparty-1.15.10.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
112
|
+
copyparty-1.15.10.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
113
|
+
copyparty-1.15.10.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
114
|
+
copyparty-1.15.10.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|