copyparty 1.15.6__py3-none-any.whl → 1.15.7__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 +2 -0
- copyparty/__version__.py +2 -2
- copyparty/ftpd.py +4 -0
- copyparty/httpcli.py +27 -3
- copyparty/httpconn.py +2 -0
- copyparty/httpsrv.py +6 -0
- copyparty/svchub.py +6 -0
- copyparty/up2k.py +24 -6
- copyparty/util.py +34 -3
- copyparty/web/a/u2c.py +30 -12
- {copyparty-1.15.6.dist-info → copyparty-1.15.7.dist-info}/METADATA +18 -1
- {copyparty-1.15.6.dist-info → copyparty-1.15.7.dist-info}/RECORD +16 -16
- {copyparty-1.15.6.dist-info → copyparty-1.15.7.dist-info}/LICENSE +0 -0
- {copyparty-1.15.6.dist-info → copyparty-1.15.7.dist-info}/WHEEL +0 -0
- {copyparty-1.15.6.dist-info → copyparty-1.15.7.dist-info}/entry_points.txt +0 -0
- {copyparty-1.15.6.dist-info → copyparty-1.15.7.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -1079,6 +1079,7 @@ def add_auth(ap):
|
|
1079
1079
|
ap2.add_argument("--ses-db", metavar="PATH", type=u, default=ses_db, help="where to store the sessions database (if you run multiple copyparty instances, make sure they use different DBs)")
|
1080
1080
|
ap2.add_argument("--ses-len", metavar="CHARS", type=int, default=20, help="session key length; default is 120 bits ((20//4)*4*6)")
|
1081
1081
|
ap2.add_argument("--no-ses", action="store_true", help="disable sessions; use plaintext passwords in cookies")
|
1082
|
+
ap2.add_argument("--ipu", metavar="CIDR=USR", type=u, action="append", help="users with IP matching \033[33mCIDR\033[0m are auto-authenticated as username \033[33mUSR\033[0m; example: [\033[32m172.16.24.0/24=dave]")
|
1082
1083
|
|
1083
1084
|
|
1084
1085
|
def add_chpw(ap):
|
@@ -1469,6 +1470,7 @@ def add_debug(ap):
|
|
1469
1470
|
ap2.add_argument("--bak-flips", action="store_true", help="[up2k] if a client uploads a bitflipped/corrupted chunk, store a copy according to \033[33m--bf-nc\033[0m and \033[33m--bf-dir\033[0m")
|
1470
1471
|
ap2.add_argument("--bf-nc", metavar="NUM", type=int, default=200, help="bak-flips: stop if there's more than \033[33mNUM\033[0m files at \033[33m--kf-dir\033[0m already; default: 6.3 GiB max (200*32M)")
|
1471
1472
|
ap2.add_argument("--bf-dir", metavar="PATH", type=u, default="bf", help="bak-flips: store corrupted chunks at \033[33mPATH\033[0m; default: folder named 'bf' wherever copyparty was started")
|
1473
|
+
ap2.add_argument("--bf-log", metavar="PATH", type=u, default="", help="bak-flips: log corruption info to a textfile at \033[33mPATH\033[0m")
|
1472
1474
|
|
1473
1475
|
|
1474
1476
|
# fmt: on
|
copyparty/__version__.py
CHANGED
copyparty/ftpd.py
CHANGED
@@ -72,6 +72,7 @@ class FtpAuth(DummyAuthorizer):
|
|
72
72
|
else:
|
73
73
|
raise AuthenticationFailed("banned")
|
74
74
|
|
75
|
+
args = self.hub.args
|
75
76
|
asrv = self.hub.asrv
|
76
77
|
uname = "*"
|
77
78
|
if username != "anonymous":
|
@@ -82,6 +83,9 @@ class FtpAuth(DummyAuthorizer):
|
|
82
83
|
uname = zs
|
83
84
|
break
|
84
85
|
|
86
|
+
if args.ipu and uname == "*":
|
87
|
+
uname = args.ipu_iu[args.ipu_nm.map(ip)]
|
88
|
+
|
85
89
|
if not uname or not (asrv.vfs.aread.get(uname) or asrv.vfs.awrite.get(uname)):
|
86
90
|
g = self.hub.gpwd
|
87
91
|
if g.lim:
|
copyparty/httpcli.py
CHANGED
@@ -584,6 +584,9 @@ class HttpCli(object):
|
|
584
584
|
or "*"
|
585
585
|
)
|
586
586
|
|
587
|
+
if self.args.ipu and self.uname == "*":
|
588
|
+
self.uname = self.conn.ipu_iu[self.conn.ipu_nm.map(self.ip)]
|
589
|
+
|
587
590
|
self.rvol = self.asrv.vfs.aread[self.uname]
|
588
591
|
self.wvol = self.asrv.vfs.awrite[self.uname]
|
589
592
|
self.avol = self.asrv.vfs.aadmin[self.uname]
|
@@ -2023,13 +2026,32 @@ class HttpCli(object):
|
|
2023
2026
|
return True
|
2024
2027
|
|
2025
2028
|
def bakflip(
|
2026
|
-
self,
|
2029
|
+
self,
|
2030
|
+
f ,
|
2031
|
+
ap ,
|
2032
|
+
ofs ,
|
2033
|
+
sz ,
|
2034
|
+
good_sha ,
|
2035
|
+
bad_sha ,
|
2036
|
+
flags ,
|
2027
2037
|
) :
|
2038
|
+
now = time.time()
|
2039
|
+
t = "bad-chunk: %.3f %s %s %d %s %s %s"
|
2040
|
+
t = t % (now, bad_sha, good_sha, ofs, self.ip, self.uname, ap)
|
2041
|
+
self.log(t, 5)
|
2042
|
+
|
2043
|
+
if self.args.bf_log:
|
2044
|
+
try:
|
2045
|
+
with open(self.args.bf_log, "ab+") as f2:
|
2046
|
+
f2.write((t + "\n").encode("utf-8", "replace"))
|
2047
|
+
except Exception as ex:
|
2048
|
+
self.log("append %s failed: %r" % (self.args.bf_log, ex))
|
2049
|
+
|
2028
2050
|
if not self.args.bak_flips or self.args.nw:
|
2029
2051
|
return
|
2030
2052
|
|
2031
2053
|
sdir = self.args.bf_dir
|
2032
|
-
fp = os.path.join(sdir,
|
2054
|
+
fp = os.path.join(sdir, bad_sha)
|
2033
2055
|
if bos.path.exists(fp):
|
2034
2056
|
return self.log("no bakflip; have it", 6)
|
2035
2057
|
|
@@ -2357,7 +2379,9 @@ class HttpCli(object):
|
|
2357
2379
|
|
2358
2380
|
if sha_b64 != chash:
|
2359
2381
|
try:
|
2360
|
-
self.bakflip(
|
2382
|
+
self.bakflip(
|
2383
|
+
f, path, cstart[0], post_sz, chash, sha_b64, vfs.flags
|
2384
|
+
)
|
2361
2385
|
except:
|
2362
2386
|
self.log("bakflip failed: " + min_ex())
|
2363
2387
|
|
copyparty/httpconn.py
CHANGED
@@ -56,6 +56,8 @@ class HttpConn(object):
|
|
56
56
|
self.asrv = hsrv.asrv # mypy404
|
57
57
|
self.u2fh = hsrv.u2fh # mypy404
|
58
58
|
self.pipes = hsrv.pipes # mypy404
|
59
|
+
self.ipu_iu = hsrv.ipu_iu
|
60
|
+
self.ipu_nm = hsrv.ipu_nm
|
59
61
|
self.ipa_nm = hsrv.ipa_nm
|
60
62
|
self.xff_nm = hsrv.xff_nm
|
61
63
|
self.xff_lan = hsrv.xff_lan # type: ignore
|
copyparty/httpsrv.py
CHANGED
@@ -69,6 +69,7 @@ from .util import (
|
|
69
69
|
build_netmap,
|
70
70
|
has_resource,
|
71
71
|
ipnorm,
|
72
|
+
load_ipu,
|
72
73
|
load_resource,
|
73
74
|
min_ex,
|
74
75
|
shut_socket,
|
@@ -171,6 +172,11 @@ class HttpSrv(object):
|
|
171
172
|
self.j2 = {x: env.get_template(x + ".html") for x in jn}
|
172
173
|
self.prism = has_resource(self.E, "web/deps/prism.js.gz")
|
173
174
|
|
175
|
+
if self.args.ipu:
|
176
|
+
self.ipu_iu, self.ipu_nm = load_ipu(self.log, self.args.ipu)
|
177
|
+
else:
|
178
|
+
self.ipu_iu = self.ipu_nm = None
|
179
|
+
|
174
180
|
self.ipa_nm = build_netmap(self.args.ipa)
|
175
181
|
self.xff_nm = build_netmap(self.args.xff_src)
|
176
182
|
self.xff_lan = build_netmap("lan")
|
copyparty/svchub.py
CHANGED
@@ -54,6 +54,7 @@ from .util import (
|
|
54
54
|
alltrace,
|
55
55
|
ansi_re,
|
56
56
|
build_netmap,
|
57
|
+
load_ipu,
|
57
58
|
min_ex,
|
58
59
|
mp,
|
59
60
|
odfusion,
|
@@ -215,6 +216,11 @@ class SvcHub(object):
|
|
215
216
|
noch.update([x for x in zsl if x])
|
216
217
|
args.chpw_no = noch
|
217
218
|
|
219
|
+
if args.ipu:
|
220
|
+
iu, nm = load_ipu(self.log, args.ipu)
|
221
|
+
setattr(args, "ipu_iu", iu)
|
222
|
+
setattr(args, "ipu_nm", nm)
|
223
|
+
|
218
224
|
if not self.args.no_ses:
|
219
225
|
self.setup_session_db()
|
220
226
|
|
copyparty/up2k.py
CHANGED
@@ -353,17 +353,18 @@ class Up2k(object):
|
|
353
353
|
return '[{"timeout":1}]'
|
354
354
|
|
355
355
|
ret = []
|
356
|
+
userset = set([(uname or "\n"), "*"])
|
356
357
|
try:
|
357
358
|
for ptop, tab2 in self.registry.items():
|
358
359
|
cfg = self.flags.get(ptop, {}).get("u2abort", 1)
|
359
360
|
if not cfg:
|
360
361
|
continue
|
361
362
|
addr = (ip or "\n") if cfg in (1, 2) else ""
|
362
|
-
user =
|
363
|
+
user = userset if cfg in (1, 3) else None
|
363
364
|
for job in tab2.values():
|
364
365
|
if (
|
365
366
|
"done" in job
|
366
|
-
or (user and
|
367
|
+
or (user and job["user"] not in user)
|
367
368
|
or (addr and addr != job["addr"])
|
368
369
|
):
|
369
370
|
continue
|
@@ -1008,6 +1009,7 @@ class Up2k(object):
|
|
1008
1009
|
vpath = k
|
1009
1010
|
|
1010
1011
|
_, flags = self._expr_idx_filter(flags)
|
1012
|
+
n4g = bool(flags.get("noforget"))
|
1011
1013
|
|
1012
1014
|
ft = "\033[0;32m{}{:.0}"
|
1013
1015
|
ff = "\033[0;35m{}{:.0}"
|
@@ -1065,21 +1067,35 @@ class Up2k(object):
|
|
1065
1067
|
for job in reg2.values():
|
1066
1068
|
job["dwrk"] = job["wark"]
|
1067
1069
|
|
1070
|
+
rm = []
|
1068
1071
|
for k, job in reg2.items():
|
1069
1072
|
job["ptop"] = ptop
|
1073
|
+
if "done" in job:
|
1074
|
+
job["need"] = job["hash"] = emptylist
|
1075
|
+
else:
|
1076
|
+
if "need" not in job:
|
1077
|
+
job["need"] = []
|
1078
|
+
if "hash" not in job:
|
1079
|
+
job["hash"] = []
|
1080
|
+
|
1070
1081
|
fp = djoin(ptop, job["prel"], job["name"])
|
1071
1082
|
if bos.path.exists(fp):
|
1072
1083
|
reg[k] = job
|
1073
1084
|
if "done" in job:
|
1074
|
-
job["need"] = job["hash"] = emptylist
|
1075
1085
|
continue
|
1076
1086
|
job["poke"] = time.time()
|
1077
1087
|
job["busy"] = {}
|
1078
1088
|
else:
|
1079
1089
|
self.log("ign deleted file in snap: [{}]".format(fp))
|
1090
|
+
if not n4g:
|
1091
|
+
rm.append(k)
|
1092
|
+
continue
|
1093
|
+
|
1094
|
+
for x in rm:
|
1095
|
+
del reg2[x]
|
1080
1096
|
|
1081
1097
|
if drp is None:
|
1082
|
-
drp = [k for k, v in reg.items() if not v
|
1098
|
+
drp = [k for k, v in reg.items() if not v["need"]]
|
1083
1099
|
else:
|
1084
1100
|
drp = [x for x in drp if x in reg]
|
1085
1101
|
|
@@ -3812,10 +3828,12 @@ class Up2k(object):
|
|
3812
3828
|
with self.mutex, self.reg_mutex:
|
3813
3829
|
abrt_cfg = self.flags.get(ptop, {}).get("u2abort", 1)
|
3814
3830
|
addr = (ip or "\n") if abrt_cfg in (1, 2) else ""
|
3815
|
-
user = (uname or "\n") if abrt_cfg in (1, 3) else
|
3831
|
+
user = ((uname or "\n"), "*") if abrt_cfg in (1, 3) else None
|
3816
3832
|
reg = self.registry.get(ptop, {}) if abrt_cfg else {}
|
3817
3833
|
for wark, job in reg.items():
|
3818
|
-
if (
|
3834
|
+
if (addr and addr != job["addr"]) or (
|
3835
|
+
user and job["user"] not in user
|
3836
|
+
):
|
3819
3837
|
continue
|
3820
3838
|
jrem = djoin(job["prel"], job["name"])
|
3821
3839
|
if ANYWIN:
|
copyparty/util.py
CHANGED
@@ -643,11 +643,15 @@ class HLog(logging.Handler):
|
|
643
643
|
|
644
644
|
|
645
645
|
class NetMap(object):
|
646
|
-
def __init__(
|
646
|
+
def __init__(
|
647
|
+
self, ips , cidrs , keep_lo=False, strict_cidr=False
|
648
|
+
) :
|
647
649
|
"""
|
648
650
|
ips: list of plain ipv4/ipv6 IPs, not cidr
|
649
651
|
cidrs: list of cidr-notation IPs (ip/prefix)
|
650
652
|
"""
|
653
|
+
self.mutex = threading.Lock()
|
654
|
+
|
651
655
|
if "::" in ips:
|
652
656
|
ips = [x for x in ips if x != "::"] + list(
|
653
657
|
[x.split("/")[0] for x in cidrs if ":" in x]
|
@@ -674,7 +678,7 @@ class NetMap(object):
|
|
674
678
|
bip = socket.inet_pton(fam, ip.split("/")[0])
|
675
679
|
self.bip.append(bip)
|
676
680
|
self.b2sip[bip] = ip.split("/")[0]
|
677
|
-
self.b2net[bip] = (IPv6Network if v6 else IPv4Network)(ip,
|
681
|
+
self.b2net[bip] = (IPv6Network if v6 else IPv4Network)(ip, strict_cidr)
|
678
682
|
|
679
683
|
self.bip.sort(reverse=True)
|
680
684
|
|
@@ -685,8 +689,10 @@ class NetMap(object):
|
|
685
689
|
try:
|
686
690
|
return self.cache[ip]
|
687
691
|
except:
|
688
|
-
|
692
|
+
with self.mutex:
|
693
|
+
return self._map(ip)
|
689
694
|
|
695
|
+
def _map(self, ip ) :
|
690
696
|
v6 = ":" in ip
|
691
697
|
ci = IPv6Address(ip) if v6 else IPv4Address(ip)
|
692
698
|
bip = next((x for x in self.bip if ci in self.b2net[x]), None)
|
@@ -2592,6 +2598,31 @@ def build_netmap(csv ):
|
|
2592
2598
|
return NetMap(ips, cidrs, True)
|
2593
2599
|
|
2594
2600
|
|
2601
|
+
def load_ipu(log , ipus ) :
|
2602
|
+
ip_u = {"": "*"}
|
2603
|
+
cidr_u = {}
|
2604
|
+
for ipu in ipus:
|
2605
|
+
try:
|
2606
|
+
cidr, uname = ipu.split("=")
|
2607
|
+
cip, csz = cidr.split("/")
|
2608
|
+
except:
|
2609
|
+
t = "\n invalid value %r for argument --ipu; must be CIDR=UNAME (192.168.0.0/16=amelia)"
|
2610
|
+
raise Exception(t % (ipu,))
|
2611
|
+
uname2 = cidr_u.get(cidr)
|
2612
|
+
if uname2 is not None:
|
2613
|
+
t = "\n invalid value %r for argument --ipu; cidr %s already mapped to %r"
|
2614
|
+
raise Exception(t % (ipu, cidr, uname2))
|
2615
|
+
cidr_u[cidr] = uname
|
2616
|
+
ip_u[cip] = uname
|
2617
|
+
try:
|
2618
|
+
nm = NetMap(["::"], list(cidr_u.keys()), True, True)
|
2619
|
+
except Exception as ex:
|
2620
|
+
t = "failed to translate --ipu into netmap, probably due to invalid config: %r"
|
2621
|
+
log("root", t % (ex,), 1)
|
2622
|
+
raise
|
2623
|
+
return ip_u, nm
|
2624
|
+
|
2625
|
+
|
2595
2626
|
def yieldfile(fn , bufsz ) :
|
2596
2627
|
readsz = min(bufsz, 128 * 1024)
|
2597
2628
|
with open(fsenc(fn), "rb", bufsz) as f:
|
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-
|
4
|
+
S_VERSION = "2.2"
|
5
|
+
S_BUILD_DT = "2024-10-13"
|
6
6
|
|
7
7
|
"""
|
8
8
|
u2c.py: upload to copyparty
|
@@ -728,6 +728,7 @@ def handshake(ar, file, search):
|
|
728
728
|
while True:
|
729
729
|
sc = 600
|
730
730
|
txt = ""
|
731
|
+
t0 = time.time()
|
731
732
|
try:
|
732
733
|
zs = json.dumps(req, separators=(",\n", ": "))
|
733
734
|
sc, txt = web.req("POST", url, {}, zs.encode("utf-8"), MJ)
|
@@ -752,7 +753,9 @@ def handshake(ar, file, search):
|
|
752
753
|
print("\nERROR: login required, or wrong password:\n%s" % (txt,))
|
753
754
|
raise BadAuth()
|
754
755
|
|
755
|
-
|
756
|
+
t = "handshake failed, retrying: %s\n t0=%.3f t1=%.3f td=%.3f\n %s\n\n"
|
757
|
+
now = time.time()
|
758
|
+
eprint(t % (file.name, t0, now, now - t0, em))
|
756
759
|
time.sleep(ar.cd)
|
757
760
|
|
758
761
|
try:
|
@@ -869,8 +872,8 @@ class Ctl(object):
|
|
869
872
|
self.hash_b = 0
|
870
873
|
self.up_f = 0
|
871
874
|
self.up_c = 0
|
872
|
-
self.up_b = 0
|
873
|
-
self.up_br = 0
|
875
|
+
self.up_b = 0 # num bytes handled
|
876
|
+
self.up_br = 0 # num bytes actually transferred
|
874
877
|
self.uploader_busy = 0
|
875
878
|
self.serialized = False
|
876
879
|
|
@@ -1013,11 +1016,14 @@ class Ctl(object):
|
|
1013
1016
|
t = "%s eta @ %s/s, %s, %d# left\033[K" % (self.eta, spd, sleft, nleft)
|
1014
1017
|
eprint(txt + "\033]0;{0}\033\\\r{0}{1}".format(t, tail))
|
1015
1018
|
|
1019
|
+
if self.ar.wlist:
|
1020
|
+
self.at_hash = time.time() - self.t0
|
1021
|
+
|
1016
1022
|
if self.hash_b and self.at_hash:
|
1017
1023
|
spd = humansize(self.hash_b / self.at_hash)
|
1018
1024
|
eprint("\nhasher: %.2f sec, %s/s\n" % (self.at_hash, spd))
|
1019
|
-
if self.
|
1020
|
-
spd = humansize(self.
|
1025
|
+
if self.up_br and self.at_up:
|
1026
|
+
spd = humansize(self.up_br / self.at_up)
|
1021
1027
|
eprint("upload: %.2f sec, %s/s\n" % (self.at_up, spd))
|
1022
1028
|
|
1023
1029
|
if not self.recheck:
|
@@ -1136,10 +1142,16 @@ class Ctl(object):
|
|
1136
1142
|
self.up_b = self.hash_b
|
1137
1143
|
|
1138
1144
|
if self.ar.wlist:
|
1145
|
+
vp = file.rel.decode("utf-8")
|
1146
|
+
if self.ar.chs:
|
1147
|
+
zsl = [
|
1148
|
+
"%s %d %d" % (zsii[0], n, zsii[1])
|
1149
|
+
for n, zsii in enumerate(file.cids)
|
1150
|
+
]
|
1151
|
+
print("chs: %s\n%s" % (vp, "\n".join(zsl)))
|
1139
1152
|
zsl = [self.ar.wsalt, str(file.size)] + [x[0] for x in file.kchunks]
|
1140
1153
|
zb = hashlib.sha512("\n".join(zsl).encode("utf-8")).digest()[:33]
|
1141
1154
|
wark = ub64enc(zb).decode("utf-8")
|
1142
|
-
vp = file.rel.decode("utf-8")
|
1143
1155
|
if self.ar.jw:
|
1144
1156
|
print("%s %s" % (wark, vp))
|
1145
1157
|
else:
|
@@ -1177,6 +1189,7 @@ class Ctl(object):
|
|
1177
1189
|
self.q_upload.put(None)
|
1178
1190
|
return
|
1179
1191
|
|
1192
|
+
chunksz = up2k_chunksize(file.size)
|
1180
1193
|
upath = file.abs.decode("utf-8", "replace")
|
1181
1194
|
if not VT100:
|
1182
1195
|
upath = upath.lstrip("\\?")
|
@@ -1236,9 +1249,14 @@ class Ctl(object):
|
|
1236
1249
|
file.up_c -= len(hs)
|
1237
1250
|
for cid in hs:
|
1238
1251
|
sz = file.kchunks[cid][1]
|
1252
|
+
self.up_br -= sz
|
1239
1253
|
self.up_b -= sz
|
1240
1254
|
file.up_b -= sz
|
1241
1255
|
|
1256
|
+
if hs and not file.up_b:
|
1257
|
+
# first hs of this file; is this an upload resume?
|
1258
|
+
file.up_b = chunksz * max(0, len(file.kchunks) - len(hs))
|
1259
|
+
|
1242
1260
|
file.ucids = hs
|
1243
1261
|
|
1244
1262
|
if not hs:
|
@@ -1252,7 +1270,7 @@ class Ctl(object):
|
|
1252
1270
|
c1 = c2 = ""
|
1253
1271
|
|
1254
1272
|
spd_h = humansize(file.size / file.t_hash, True)
|
1255
|
-
if file.
|
1273
|
+
if file.up_c:
|
1256
1274
|
t_up = file.t1_up - file.t0_up
|
1257
1275
|
spd_u = humansize(file.size / t_up, True)
|
1258
1276
|
|
@@ -1262,13 +1280,12 @@ class Ctl(object):
|
|
1262
1280
|
t = " found %s %s(%.2fs,%s/s)%s"
|
1263
1281
|
print(t % (upath, c1, file.t_hash, spd_h, c2))
|
1264
1282
|
else:
|
1265
|
-
kw = "uploaded" if file.
|
1283
|
+
kw = "uploaded" if file.up_c else " found"
|
1266
1284
|
print("{0} {1}".format(kw, upath))
|
1267
1285
|
|
1268
1286
|
self._check_if_done()
|
1269
1287
|
continue
|
1270
1288
|
|
1271
|
-
chunksz = up2k_chunksize(file.size)
|
1272
1289
|
njoin = (self.ar.sz * 1024 * 1024) // chunksz
|
1273
1290
|
cs = hs[:]
|
1274
1291
|
while cs:
|
@@ -1365,7 +1382,7 @@ def main():
|
|
1365
1382
|
cores = (os.cpu_count() if hasattr(os, "cpu_count") else 0) or 2
|
1366
1383
|
hcores = min(cores, 3) # 4% faster than 4+ on py3.9 @ r5-4500U
|
1367
1384
|
|
1368
|
-
ver = "{0}
|
1385
|
+
ver = "{0}, v{1}".format(S_BUILD_DT, S_VERSION)
|
1369
1386
|
if "--version" in sys.argv:
|
1370
1387
|
print(ver)
|
1371
1388
|
return
|
@@ -1403,6 +1420,7 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1403
1420
|
|
1404
1421
|
ap = app.add_argument_group("file-ID calculator; enable with url '-' to list warks (file identifiers) instead of upload/search")
|
1405
1422
|
ap.add_argument("--wsalt", type=unicode, metavar="S", default="hunter2", help="salt to use when creating warks; must match server config")
|
1423
|
+
ap.add_argument("--chs", action="store_true", help="verbose (print the hash/offset of each chunk in each file)")
|
1406
1424
|
ap.add_argument("--jw", action="store_true", help="just identifier+filepath, not mtime/size too")
|
1407
1425
|
|
1408
1426
|
ap = app.add_argument_group("performance tweaks")
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.15.
|
3
|
+
Version: 1.15.7
|
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
|
@@ -134,6 +134,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
134
134
|
* [event hooks](#event-hooks) - trigger a program on uploads, renames etc ([examples](./bin/hooks/))
|
135
135
|
* [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/))
|
136
136
|
* [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/))
|
137
|
+
* [ip auth](#ip-auth) - autologin based on IP range (CIDR)
|
137
138
|
* [identity providers](#identity-providers) - replace copyparty passwords with oauth and such
|
138
139
|
* [user-changeable passwords](#user-changeable-passwords) - if permitted, users can change their own passwords
|
139
140
|
* [using the cloud as storage](#using-the-cloud-as-storage) - connecting to an aws s3 bucket and similar
|
@@ -1486,6 +1487,22 @@ redefine behavior with plugins ([examples](./bin/handlers/))
|
|
1486
1487
|
replace 404 and 403 errors with something completely different (that's it for now)
|
1487
1488
|
|
1488
1489
|
|
1490
|
+
## ip auth
|
1491
|
+
|
1492
|
+
autologin based on IP range (CIDR) , using the global-option `--ipu`
|
1493
|
+
|
1494
|
+
for example, if everyone with an IP that starts with `192.168.123` should automatically log in as the user `spartacus`, then you can either specify `--ipu=192.168.123.0/24=spartacus` as a commandline option, or put this in a config file:
|
1495
|
+
|
1496
|
+
```yaml
|
1497
|
+
[global]
|
1498
|
+
ipu: 192.168.123.0/24=spartacus
|
1499
|
+
```
|
1500
|
+
|
1501
|
+
repeat the option to map additional subnets
|
1502
|
+
|
1503
|
+
**be careful with this one!** if you have a reverseproxy, then you definitely want to make sure you have [real-ip](#real-ip) configured correctly, and it's probably a good idea to nullmap the reverseproxy's IP just in case; so if your reverseproxy is sending requests from `172.24.27.9` then that would be `--ipu=172.24.27.9/32=`
|
1504
|
+
|
1505
|
+
|
1489
1506
|
## identity providers
|
1490
1507
|
|
1491
1508
|
replace copyparty passwords with oauth and such
|
@@ -1,6 +1,6 @@
|
|
1
1
|
copyparty/__init__.py,sha256=Chqw7uXX4r_-a2p6-xthrrqVHFI4aZdW45sWU7UvqeE,2597
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
2
|
+
copyparty/__main__.py,sha256=jPcqdsSJZP6AMoPygb6t8PwxmLXGK3jUeUvt28JcnyE,110130
|
3
|
+
copyparty/__version__.py,sha256=Z9byW4PqObsayHm5sQR8iTi3mFCP5dkS0YnU-6VoRT4,258
|
4
4
|
copyparty/authsrv.py,sha256=clsE8whf32_eIRES1r7VvsVkc4OiJCjiBEJM6pYkwwI,98670
|
5
5
|
copyparty/broker_mp.py,sha256=jsHUM2BSfRVRyZT869iPCqYEHSqedk6VkwvygZwbEZE,4017
|
6
6
|
copyparty/broker_mpw.py,sha256=PYFgQfssOCfdI6qayW1ZjO1j1-7oez094muhYMbPOz0,3339
|
@@ -10,10 +10,10 @@ copyparty/cert.py,sha256=0ZAPeXeMR164vWn9GQU3JDKooYXEq_NOQkDeg543ivg,8009
|
|
10
10
|
copyparty/cfg.py,sha256=33nLatBUmzRFKQ4KpoQei3ZY6EqRrlaHpQnvCNFXcHI,10112
|
11
11
|
copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
|
12
12
|
copyparty/fsutil.py,sha256=5CshJWO7CflfaRRNOb3JxghUH7W5rmS_HWNmKfx42MM,4538
|
13
|
-
copyparty/ftpd.py,sha256=
|
14
|
-
copyparty/httpcli.py,sha256=
|
15
|
-
copyparty/httpconn.py,sha256=
|
16
|
-
copyparty/httpsrv.py,sha256=
|
13
|
+
copyparty/ftpd.py,sha256=G_h1urfIikzfCWGXnW9p-rioWdNM_Je6vWYq0-QSbC8,17580
|
14
|
+
copyparty/httpcli.py,sha256=PIlYJa-1QTzsASD9jRbRJGsn8vAUNSg15qJEigcs9cs,192083
|
15
|
+
copyparty/httpconn.py,sha256=mQSgljh0Q-jyWjF4tQLrHbRKRe9WKl19kGqsGMsJpWo,6880
|
16
|
+
copyparty/httpsrv.py,sha256=k0xSadmUNSyUAWlZdOMFdsBP1fQK50Ptv7O6O4jFy2Q,17182
|
17
17
|
copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
|
18
18
|
copyparty/mdns.py,sha256=vC078llnL1v0pvL3mnwacuStFHPJUQuxo9Opj-IbHL4,18155
|
19
19
|
copyparty/metrics.py,sha256=aV09nntEmKMIyde8xoPtj1ehDOQVQOHchRF4uMMNzqM,8855
|
@@ -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=FuQGFBm-lJfe28M-SMBLieHy8jdWIQMkONK2GcWZU_E,40105
|
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
33
|
copyparty/u2idx.py,sha256=JjgqwgJBNj6sTn4PJfuqM3VEHqlmoyGC5bk4_92K2h0,13414
|
34
|
-
copyparty/up2k.py,sha256=
|
35
|
-
copyparty/util.py,sha256=
|
34
|
+
copyparty/up2k.py,sha256=PSVSdG00wC76mi0E7HF1WBFco4lujpSCTTtSzUusPVM,165096
|
35
|
+
copyparty/util.py,sha256=Vj8F50G0taqNd7c8TQd56IRbRZGFE1r0wnnrymbk7j0,92349
|
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
|
@@ -85,7 +85,7 @@ copyparty/web/util.js.gz,sha256=uh_NAVPiMcOCTy9oxUiDKmKh1GokTKBPe3FYNMMUYlM,1478
|
|
85
85
|
copyparty/web/w.hash.js.gz,sha256=7wP9EZQNXQxwZnCCFUVsi_-6TM9PLZJeZ9krutXRRj8,1060
|
86
86
|
copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
87
87
|
copyparty/web/a/partyfuse.py,sha256=fa9bBYNJHvtWpNVjQyyFzx6JOK7MJL1u0zj80PBYQKs,27960
|
88
|
-
copyparty/web/a/u2c.py,sha256=
|
88
|
+
copyparty/web/a/u2c.py,sha256=TkLmQL_3lwus0HI-m4cuP8E9qanfB8U9xGvW7zEJ8ho,47576
|
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.7.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
110
|
+
copyparty-1.15.7.dist-info/METADATA,sha256=DXlK1tdi4bTueoZXTbr33g_wqCpjko4sTPWpTeXXfGs,138774
|
111
|
+
copyparty-1.15.7.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
112
|
+
copyparty-1.15.7.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
113
|
+
copyparty-1.15.7.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
114
|
+
copyparty-1.15.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|