copyparty 1.15.2__py3-none-any.whl → 1.15.4__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/szip.py CHANGED
@@ -99,7 +99,7 @@ def gen_hdr(
99
99
  ret += spack(b"<LL", vsz, vsz)
100
100
 
101
101
  # windows support (the "?" replace below too)
102
- fn = sanitize_fn(fn, "/", [])
102
+ fn = sanitize_fn(fn, "/")
103
103
  bfn = fn.encode("utf-8" if utf8 else "cp437", "replace").replace(b"?", b"_")
104
104
 
105
105
  # add ntfs (0x24) and/or unix (0x10) extrafields for utc, add z64 if requested
copyparty/u2idx.py CHANGED
@@ -50,6 +50,7 @@ class U2idx(object):
50
50
  self.log("your python does not have sqlite3; searching will be disabled")
51
51
  return
52
52
 
53
+
53
54
  self.active_id = ""
54
55
  self.active_cur = None
55
56
  self.cur = {}
copyparty/up2k.py CHANGED
@@ -285,7 +285,9 @@ class Up2k(object):
285
285
  else:
286
286
  mtpq = "(?)"
287
287
  if up_en:
288
- ups = [(0, 0, time.time(), "cannot show list (server too busy)")]
288
+ ups = [(1, 0, 0, time.time(), "cannot show list (server too busy)")]
289
+ if PY2:
290
+ ups = []
289
291
 
290
292
  ups.sort(reverse=True)
291
293
 
@@ -322,7 +324,7 @@ class Up2k(object):
322
324
  zt = (
323
325
  ineed / ihash,
324
326
  job["size"],
325
- int(job["t0"]),
327
+ int(job["t0c"]),
326
328
  int(job["poke"]),
327
329
  djoin(vtop, job["prel"], job["name"]),
328
330
  )
@@ -358,10 +360,9 @@ class Up2k(object):
358
360
  continue
359
361
  addr = (ip or "\n") if cfg in (1, 2) else ""
360
362
  user = (uname or "\n") if cfg in (1, 3) else ""
361
- drp = self.droppable.get(ptop, {})
362
- for wark, job in tab2.items():
363
+ for job in tab2.values():
363
364
  if (
364
- wark in drp
365
+ "done" in job
365
366
  or (user and user != job["user"])
366
367
  or (addr and addr != job["addr"])
367
368
  ):
@@ -402,9 +403,8 @@ class Up2k(object):
402
403
  for ptop, tab2 in self.registry.items():
403
404
  nbytes = 0
404
405
  nfiles = 0
405
- drp = self.droppable.get(ptop, {})
406
- for wark, job in tab2.items():
407
- if wark in drp:
406
+ for job in tab2.values():
407
+ if "done" in job:
408
408
  continue
409
409
 
410
410
  nfiles += 1
@@ -1048,6 +1048,7 @@ class Up2k(object):
1048
1048
 
1049
1049
  reg = {}
1050
1050
  drp = None
1051
+ emptylist = []
1051
1052
  snap = os.path.join(histpath, "up2k.snap")
1052
1053
  if bos.path.exists(snap):
1053
1054
  with gzip.GzipFile(snap, "rb") as f:
@@ -1064,6 +1065,9 @@ class Up2k(object):
1064
1065
  fp = djoin(job["ptop"], job["prel"], job["name"])
1065
1066
  if bos.path.exists(fp):
1066
1067
  reg[k] = job
1068
+ if "done" in job:
1069
+ job["need"] = job["hash"] = emptylist
1070
+ continue
1067
1071
  job["poke"] = time.time()
1068
1072
  job["busy"] = {}
1069
1073
  else:
@@ -2758,7 +2762,7 @@ class Up2k(object):
2758
2762
  if ptop not in self.registry:
2759
2763
  raise Pebkac(410, "location unavailable")
2760
2764
 
2761
- cj["name"] = sanitize_fn(cj["name"], "", [".prologue.html", ".epilogue.html"])
2765
+ cj["name"] = sanitize_fn(cj["name"], "")
2762
2766
  cj["poke"] = now = self.db_act = self.vol_act[ptop] = time.time()
2763
2767
  wark = self._get_wark(cj)
2764
2768
  job = None
@@ -3003,7 +3007,7 @@ class Up2k(object):
3003
3007
 
3004
3008
  job = deepcopy(job)
3005
3009
  job["wark"] = wark
3006
- job["at"] = cj.get("at") or time.time()
3010
+ job["at"] = cj.get("at") or now
3007
3011
  zs = "vtop ptop prel name lmod host user addr poke"
3008
3012
  for k in zs.split():
3009
3013
  job[k] = cj.get(k) or ""
@@ -3328,6 +3332,9 @@ class Up2k(object):
3328
3332
  self.log("unknown wark [{}], known: {}".format(wark, known))
3329
3333
  raise Pebkac(400, "unknown wark" + SSEELOG)
3330
3334
 
3335
+ if "t0c" not in job:
3336
+ job["t0c"] = time.time()
3337
+
3331
3338
  if len(chashes) > 1 and len(chashes[1]) < 44:
3332
3339
  # first hash is full-length; expand remaining ones
3333
3340
  uniq = []
@@ -3508,7 +3515,11 @@ class Up2k(object):
3508
3515
  if self.idx_wark(vflags, *z2):
3509
3516
  del self.registry[ptop][wark]
3510
3517
  else:
3511
- self.registry[ptop][wark]["done"] = 1
3518
+ for k in "host tnam busy sprs poke t0c".split():
3519
+ del job[k]
3520
+ job["t0"] = int(job["t0"])
3521
+ job["hash"] = []
3522
+ job["done"] = 1
3512
3523
  self.regdrop(ptop, wark)
3513
3524
 
3514
3525
  if wake_sr:
@@ -4695,7 +4706,11 @@ class Up2k(object):
4695
4706
  bos.unlink(path)
4696
4707
  return
4697
4708
 
4698
- newest = float(max(x["poke"] for _, x in reg.items()) if reg else 0)
4709
+ newest = float(
4710
+ max(x["t0"] if "done" in x else x["poke"] for _, x in reg.items())
4711
+ if reg
4712
+ else 0
4713
+ )
4699
4714
  etag = (len(reg), newest)
4700
4715
  if etag == self.snap_prev.get(ptop):
4701
4716
  return
@@ -4706,12 +4721,15 @@ class Up2k(object):
4706
4721
  path2 = "{}.{}".format(path, os.getpid())
4707
4722
  body = {"droppable": self.droppable[ptop], "registry": reg}
4708
4723
  j = json.dumps(body, sort_keys=True, separators=(",\n", ": ")).encode("utf-8")
4724
+ # j = re.sub(r'"(need|hash)": \[\],\n', "", j) # bytes=slow, utf8=hungry
4725
+ j = j.replace(b'"need": [],\n', b"") # surprisingly optimal
4726
+ j = j.replace(b'"hash": [],\n', b"")
4709
4727
  with gzip.GzipFile(path2, "wb") as f:
4710
4728
  f.write(j)
4711
4729
 
4712
4730
  atomic_move(self.log, path2, path, VF_CAREFUL)
4713
4731
 
4714
- self.log("snap: {} |{}|".format(path, len(reg.keys())))
4732
+ self.log("snap: %s |%d| %.2fs" % (path, len(reg), time.time() - now))
4715
4733
  self.snap_prev[ptop] = etag
4716
4734
 
4717
4735
  def _tagger(self) :
copyparty/util.py CHANGED
@@ -4,6 +4,7 @@ from __future__ import print_function, unicode_literals
4
4
  import argparse
5
5
  import base64
6
6
  import binascii
7
+ import codecs
7
8
  import errno
8
9
  import hashlib
9
10
  import hmac
@@ -30,7 +31,17 @@ from collections import Counter
30
31
  from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network
31
32
  from queue import Queue
32
33
 
33
- from .__init__ import ANYWIN, EXE, MACOS, PY2, PY36, TYPE_CHECKING, VT100, WINDOWS
34
+ from .__init__ import (
35
+ ANYWIN,
36
+ EXE,
37
+ MACOS,
38
+ PY2,
39
+ PY36,
40
+ TYPE_CHECKING,
41
+ VT100,
42
+ WINDOWS,
43
+ EnvParams,
44
+ )
34
45
  from .__version__ import S_BUILD_DT, S_VERSION
35
46
  from .stolen import surrogateescape
36
47
 
@@ -258,6 +269,30 @@ if ANYWIN:
258
269
  UNPLICATIONS = [["no_dav", "daw"]]
259
270
 
260
271
 
272
+ DAV_ALLPROP_L = [
273
+ "contentclass",
274
+ "creationdate",
275
+ "defaultdocument",
276
+ "displayname",
277
+ "getcontentlanguage",
278
+ "getcontentlength",
279
+ "getcontenttype",
280
+ "getlastmodified",
281
+ "href",
282
+ "iscollection",
283
+ "ishidden",
284
+ "isreadonly",
285
+ "isroot",
286
+ "isstructureddocument",
287
+ "lastaccessed",
288
+ "name",
289
+ "parentname",
290
+ "resourcetype",
291
+ "supportedlock",
292
+ ]
293
+ DAV_ALLPROPS = set(DAV_ALLPROP_L)
294
+
295
+
261
296
  MIMES = {
262
297
  "opus": "audio/ogg; codecs=opus",
263
298
  }
@@ -728,60 +763,6 @@ class _Unrecv(object):
728
763
  self.buf = buf + self.buf
729
764
 
730
765
 
731
- class _LUnrecv(object):
732
- """
733
- with expensive debug logging
734
- """
735
-
736
- def __init__(self, s , log ) :
737
- self.s = s
738
- self.log = log
739
- self.buf = b""
740
-
741
- def recv(self, nbytes , spins ) :
742
- if self.buf:
743
- ret = self.buf[:nbytes]
744
- self.buf = self.buf[nbytes:]
745
- t = "\033[0;7mur:pop:\033[0;1;32m {}\n\033[0;7mur:rem:\033[0;1;35m {}\033[0m"
746
- print(t.format(ret, self.buf))
747
- return ret
748
-
749
- ret = self.s.recv(nbytes)
750
- t = "\033[0;7mur:recv\033[0;1;33m {}\033[0m"
751
- print(t.format(ret))
752
- if not ret:
753
- raise UnrecvEOF("client stopped sending data")
754
-
755
- return ret
756
-
757
- def recv_ex(self, nbytes , raise_on_trunc = True) :
758
- """read an exact number of bytes"""
759
- try:
760
- ret = self.recv(nbytes, 1)
761
- err = False
762
- except:
763
- ret = b""
764
- err = True
765
-
766
- while not err and len(ret) < nbytes:
767
- try:
768
- ret += self.recv(nbytes - len(ret), 1)
769
- except OSError:
770
- err = True
771
-
772
- if err:
773
- t = "client only sent {} of {} expected bytes".format(len(ret), nbytes)
774
- if raise_on_trunc:
775
- raise UnrecvEOF(t)
776
- elif self.log:
777
- self.log(t, 3)
778
-
779
- return ret
780
-
781
- def unrecv(self, buf ) :
782
- self.buf = buf + self.buf
783
- t = "\033[0;7mur:push\033[0;1;31m {}\n\033[0;7mur:rem:\033[0;1;35m {}\033[0m"
784
- print(t.format(buf, self.buf))
785
766
 
786
767
 
787
768
  Unrecv = _Unrecv
@@ -1916,13 +1897,10 @@ def undot(path ) :
1916
1897
  return "/".join(ret)
1917
1898
 
1918
1899
 
1919
- def sanitize_fn(fn , ok , bad ) :
1900
+ def sanitize_fn(fn , ok ) :
1920
1901
  if "/" not in ok:
1921
1902
  fn = fn.replace("\\", "/").split("/")[-1]
1922
1903
 
1923
- if fn.lower() in bad:
1924
- fn = "_" + fn
1925
-
1926
1904
  if ANYWIN:
1927
1905
  remap = [
1928
1906
  ["<", "<"],
@@ -1948,9 +1926,9 @@ def sanitize_fn(fn , ok , bad ) :
1948
1926
  return fn.strip()
1949
1927
 
1950
1928
 
1951
- def sanitize_vpath(vp , ok , bad ) :
1929
+ def sanitize_vpath(vp , ok ) :
1952
1930
  parts = vp.replace(os.sep, "/").split("/")
1953
- ret = [sanitize_fn(x, ok, bad) for x in parts]
1931
+ ret = [sanitize_fn(x, ok) for x in parts]
1954
1932
  return "/".join(ret)
1955
1933
 
1956
1934
 
@@ -3376,9 +3354,15 @@ def loadpy(ap , hot ) :
3376
3354
 
3377
3355
  def gzip_orig_sz(fn ) :
3378
3356
  with open(fsenc(fn), "rb") as f:
3379
- f.seek(-4, 2)
3380
- rv = f.read(4)
3381
- return sunpack(b"I", rv)[0] # type: ignore
3357
+ return gzip_file_orig_sz(f)
3358
+
3359
+
3360
+ def gzip_file_orig_sz(f) :
3361
+ start = f.tell()
3362
+ f.seek(-4, 2)
3363
+ rv = f.read(4)
3364
+ f.seek(start, 0)
3365
+ return sunpack(b"I", rv)[0] # type: ignore
3382
3366
 
3383
3367
 
3384
3368
  def align_tab(lines ) :
@@ -3513,6 +3497,103 @@ def hidedir(dp) :
3513
3497
  pass
3514
3498
 
3515
3499
 
3500
+ try:
3501
+ if sys.version_info < (3, 10):
3502
+ # py3.8 doesn't have .files
3503
+ # py3.9 has broken .is_file
3504
+ raise ImportError()
3505
+ import importlib.resources as impresources
3506
+ except ImportError:
3507
+ try:
3508
+ import importlib_resources as impresources
3509
+ except ImportError:
3510
+ impresources = None
3511
+ try:
3512
+ if sys.version_info > (3, 10):
3513
+ raise ImportError()
3514
+ import pkg_resources
3515
+ except ImportError:
3516
+ pkg_resources = None
3517
+
3518
+
3519
+ def _pkg_resource_exists(pkg , name ) :
3520
+ if not pkg_resources:
3521
+ return False
3522
+ try:
3523
+ return pkg_resources.resource_exists(pkg, name)
3524
+ except NotImplementedError:
3525
+ return False
3526
+
3527
+
3528
+ def stat_resource(E , name ):
3529
+ path = os.path.join(E.mod, name)
3530
+ if os.path.exists(path):
3531
+ return os.stat(fsenc(path))
3532
+ return None
3533
+
3534
+
3535
+ def _find_impresource(E , name ):
3536
+ try:
3537
+ files = impresources.files(E.pkg)
3538
+ except ImportError:
3539
+ return None
3540
+
3541
+ return files.joinpath(name)
3542
+
3543
+
3544
+ _rescache_has = {}
3545
+
3546
+
3547
+ def _has_resource(E , name ):
3548
+ try:
3549
+ return _rescache_has[name]
3550
+ except:
3551
+ pass
3552
+
3553
+ if len(_rescache_has) > 999:
3554
+ _rescache_has.clear()
3555
+
3556
+ if impresources:
3557
+ res = _find_impresource(E, name)
3558
+ if res and res.is_file():
3559
+ _rescache_has[name] = True
3560
+ return True
3561
+
3562
+ if pkg_resources:
3563
+ if _pkg_resource_exists(E.pkg.__name__, name):
3564
+ _rescache_has[name] = True
3565
+ return True
3566
+
3567
+ _rescache_has[name] = False
3568
+ return False
3569
+
3570
+
3571
+ def has_resource(E , name ):
3572
+ return _has_resource(E, name) or os.path.exists(os.path.join(E.mod, name))
3573
+
3574
+
3575
+ def load_resource(E , name , mode="rb") :
3576
+ enc = None if "b" in mode else "utf-8"
3577
+
3578
+ if impresources:
3579
+ res = _find_impresource(E, name)
3580
+ if res and res.is_file():
3581
+ if enc:
3582
+ return res.open(mode, encoding=enc)
3583
+ else:
3584
+ # throws if encoding= is mentioned at all
3585
+ return res.open(mode)
3586
+
3587
+ if pkg_resources:
3588
+ if _pkg_resource_exists(E.pkg.__name__, name):
3589
+ stream = pkg_resources.resource_stream(E.pkg.__name__, name)
3590
+ if enc:
3591
+ stream = codecs.getreader(enc)(stream)
3592
+ return stream
3593
+
3594
+ return open(os.path.join(E.mod, name), mode, encoding=enc)
3595
+
3596
+
3516
3597
  class Pebkac(Exception):
3517
3598
  def __init__(
3518
3599
  self, code , msg = None, log = None