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 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
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 15, 6)
3
+ VERSION = (1, 15, 7)
4
4
  CODENAME = "fill the drives"
5
- BUILD_DT = (2024, 10, 12)
5
+ BUILD_DT = (2024, 10, 14)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
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, f , ofs , sz , sha , flags
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, sha)
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(f, cstart[0], post_sz, sha_b64, vfs.flags)
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 = (uname or "\n") if cfg in (1, 3) else ""
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 user != job["user"])
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.get("need", [])]
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 (user and user != job["user"]) or (addr and addr != job["addr"]):
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__(self, ips , cidrs , keep_lo=False) :
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, False)
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
- pass
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.1"
5
- S_BUILD_DT = "2024-09-23"
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
- eprint("handshake failed, retrying: %s\n %s\n\n" % (file.name, em))
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.up_b and self.at_up:
1020
- spd = humansize(self.up_b / self.at_up)
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.up_b:
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.up_b else " found"
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} v{1} https://youtu.be/BIcOO6TLKaY".format(S_BUILD_DT, S_VERSION)
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.6
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=6yMejKWZGgZBsIJ3A_lkpieMdpd0q7-nUgbsFTrpcR4,109765
3
- copyparty/__version__.py,sha256=cN7xKCyFmKnXASldOC8xD6LcXlzSFiM6G6TpRgB0zh4,258
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=VTx0gZUdK_0xRORMZT1XymSYvsXsCZs6-wsM2J4raYA,17459
14
- copyparty/httpcli.py,sha256=QWOSdYhOxU3i-uCeoUUCEYt2O6pXbi3KjXoTr0YZm4Y,191346
15
- copyparty/httpconn.py,sha256=DN09_r3cPFN4UGCVtAhnIH6cBIDKF-fe4IgMeoHUnSc,6809
16
- copyparty/httpsrv.py,sha256=TnfqA4edgO187y8fV0Q-lchnnD2bMSBTPZgncEjGMhY,17009
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=qLG2CE8VHro7I6FnYMwiij0LcBC5lJw-hiucBag3NHY,39939
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=UlycCjhC0C1tMwZvHXlkoXRTaFIEbivg-sXGEBQCBCs,164564
35
- copyparty/util.py,sha256=4VKiIzGPY6kb7PkTL3lfD1z-ewQKMQM5_J3meGirlS0,91338
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=QmEZOPJfOVmtVjHWP7XATyzvAAY1OzelikPKdRShOLg,46740
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.6.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
110
- copyparty-1.15.6.dist-info/METADATA,sha256=rFYHpfJWHe5_4nuyJ9BCvq4YxRoaDRIgdY-VBjjqLqs,137956
111
- copyparty-1.15.6.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
112
- copyparty-1.15.6.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
113
- copyparty-1.15.6.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
114
- copyparty-1.15.6.dist-info/RECORD,,
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,,