copyparty 1.19.8__py3-none-any.whl → 1.19.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.
Files changed (52) hide show
  1. copyparty/__main__.py +26 -14
  2. copyparty/__version__.py +2 -2
  3. copyparty/authsrv.py +102 -9
  4. copyparty/broker_mpw.py +3 -0
  5. copyparty/cfg.py +4 -0
  6. copyparty/fsutil.py +66 -23
  7. copyparty/ftpd.py +3 -0
  8. copyparty/httpcli.py +23 -11
  9. copyparty/mdns.py +3 -1
  10. copyparty/mtag.py +2 -4
  11. copyparty/svchub.py +14 -2
  12. copyparty/u2idx.py +29 -4
  13. copyparty/up2k.py +20 -9
  14. copyparty/util.py +48 -22
  15. copyparty/web/baguettebox.js.gz +0 -0
  16. copyparty/web/browser.css.gz +0 -0
  17. copyparty/web/browser.js.gz +0 -0
  18. copyparty/web/dbg-audio.js.gz +0 -0
  19. copyparty/web/deps/busy.mp3.gz +0 -0
  20. copyparty/web/deps/easymde.css.gz +0 -0
  21. copyparty/web/deps/easymde.js.gz +0 -0
  22. copyparty/web/deps/marked.js.gz +0 -0
  23. copyparty/web/deps/mini-fa.css.gz +0 -0
  24. copyparty/web/deps/prism.css.gz +0 -0
  25. copyparty/web/deps/prism.js.gz +0 -0
  26. copyparty/web/deps/prismd.css.gz +0 -0
  27. copyparty/web/deps/scp.woff2 +0 -0
  28. copyparty/web/deps/sha512.ac.js.gz +0 -0
  29. copyparty/web/md.css.gz +0 -0
  30. copyparty/web/md.js.gz +0 -0
  31. copyparty/web/md2.css.gz +0 -0
  32. copyparty/web/md2.js.gz +0 -0
  33. copyparty/web/mde.css.gz +0 -0
  34. copyparty/web/mde.js.gz +0 -0
  35. copyparty/web/msg.css.gz +0 -0
  36. copyparty/web/rups.css.gz +0 -0
  37. copyparty/web/rups.js.gz +0 -0
  38. copyparty/web/shares.css.gz +0 -0
  39. copyparty/web/shares.js.gz +0 -0
  40. copyparty/web/splash.css.gz +0 -0
  41. copyparty/web/splash.js.gz +0 -0
  42. copyparty/web/svcs.js.gz +0 -0
  43. copyparty/web/ui.css.gz +0 -0
  44. copyparty/web/up2k.js.gz +0 -0
  45. copyparty/web/util.js.gz +0 -0
  46. copyparty/web/w.hash.js.gz +0 -0
  47. {copyparty-1.19.8.dist-info → copyparty-1.19.10.dist-info}/METADATA +7 -3
  48. {copyparty-1.19.8.dist-info → copyparty-1.19.10.dist-info}/RECORD +52 -52
  49. {copyparty-1.19.8.dist-info → copyparty-1.19.10.dist-info}/WHEEL +0 -0
  50. {copyparty-1.19.8.dist-info → copyparty-1.19.10.dist-info}/entry_points.txt +0 -0
  51. {copyparty-1.19.8.dist-info → copyparty-1.19.10.dist-info}/licenses/LICENSE +0 -0
  52. {copyparty-1.19.8.dist-info → copyparty-1.19.10.dist-info}/top_level.txt +0 -0
copyparty/httpcli.py CHANGED
@@ -730,6 +730,9 @@ class HttpCli(object):
730
730
  else:
731
731
  avn = vn
732
732
 
733
+ if "bcasechk" in vn.flags and not vn.casechk(rem, True):
734
+ return self.tx_404() and False
735
+
733
736
  (
734
737
  self.can_read,
735
738
  self.can_write,
@@ -1546,6 +1549,7 @@ class HttpCli(object):
1546
1549
  if xtag is not None:
1547
1550
  props = set([y.tag.split("}")[-1] for y in xtag])
1548
1551
  # assume <allprop/> otherwise; nobody ever gonna <propname/>
1552
+ self.hint = ""
1549
1553
 
1550
1554
  zi = int(time.time())
1551
1555
  vst = os.stat_result((16877, -1, -1, 1, 1000, 1000, 8, zi, zi, zi))
@@ -1555,7 +1559,9 @@ class HttpCli(object):
1555
1559
  except OSError as ex:
1556
1560
  if ex.errno not in (errno.ENOENT, errno.ENOTDIR):
1557
1561
  raise
1558
- raise Pebkac(404)
1562
+ if tap:
1563
+ raise Pebkac(404)
1564
+ st = vst
1559
1565
 
1560
1566
  topdir = {"vp": "", "st": st}
1561
1567
  fgen = []
@@ -1595,6 +1601,9 @@ class HttpCli(object):
1595
1601
  )
1596
1602
 
1597
1603
  elif depth == "0" or not stat.S_ISDIR(st.st_mode):
1604
+ if depth == "0" and not self.vpath and not vn.realpath:
1605
+ # rootless server; give dummy listing
1606
+ self.can_read = True
1598
1607
  # propfind on a file; return as topdir
1599
1608
  if not self.can_read and not self.can_get:
1600
1609
  self.log("inaccessible: %r" % ("/" + self.vpath,))
@@ -1627,7 +1636,11 @@ class HttpCli(object):
1627
1636
  self.log("inaccessible: %r" % ("/" + self.vpath,))
1628
1637
  raise Pebkac(401, "authenticate")
1629
1638
 
1630
- zi = vn.flags["du_iwho"] if "quota-available-bytes" in props else 0
1639
+ zi = (
1640
+ vn.flags["du_iwho"]
1641
+ if vn.realpath and "quota-available-bytes" in props
1642
+ else 0
1643
+ )
1631
1644
  if zi and (
1632
1645
  zi == 9
1633
1646
  or (zi == 7 and self.uname != "*")
@@ -1761,6 +1774,7 @@ class HttpCli(object):
1761
1774
  xprop = xroot.find(r"./{DAV:}propertyupdate/{DAV:}set/{DAV:}prop")
1762
1775
  for ze in xprop:
1763
1776
  ze.clear()
1777
+ self.hint = ""
1764
1778
 
1765
1779
  txt = """<multistatus xmlns="DAV:"><response><propstat><status>HTTP/1.1 403 Forbidden</status></propstat></response></multistatus>"""
1766
1780
  xroot = parse_xml(txt)
@@ -1816,6 +1830,7 @@ class HttpCli(object):
1816
1830
  ET.register_namespace("D", "DAV:")
1817
1831
  lk = parse_xml(txt)
1818
1832
  assert lk.tag == "{DAV:}lockinfo"
1833
+ self.hint = ""
1819
1834
 
1820
1835
  token = str(uuid.uuid4())
1821
1836
 
@@ -3403,8 +3418,6 @@ class HttpCli(object):
3403
3418
  sz, sha_hex, sha_b64 = copier(
3404
3419
  p_data, f, hasher, max_sz, self.args.s_wr_slp
3405
3420
  )
3406
- if sz == 0:
3407
- raise Pebkac(400, "empty files in post")
3408
3421
  finally:
3409
3422
  f.close()
3410
3423
 
@@ -4711,11 +4724,9 @@ class HttpCli(object):
4711
4724
  packer = StreamZip
4712
4725
  ext = "zip"
4713
4726
 
4714
- fn = items[0] if items and items[0] else self.vpath
4715
- if fn:
4716
- fn = fn.rstrip("/").split("/")[-1]
4717
- else:
4718
- fn = self.host.split(":")[0]
4727
+ fn = self.vpath.split("/")[-1] or self.host.split(":")[0]
4728
+ if items:
4729
+ fn = "sel-" + fn
4719
4730
 
4720
4731
  if vn.flags.get("zipmax") and not (
4721
4732
  vn.flags.get("zipmaxu") and self.uname != "*"
@@ -4891,8 +4902,8 @@ class HttpCli(object):
4891
4902
  else:
4892
4903
  fullfile = b""
4893
4904
 
4894
- if not sz_md and b"\n" in buf[:2]:
4895
- lead = buf[: buf.find(b"\n") + 1]
4905
+ if not sz_md and buf.startswith((b"\n", b"\r\n")):
4906
+ lead = b"\n" if buf.startswith(b"\n") else b"\r\n"
4896
4907
  sz_md += len(lead)
4897
4908
 
4898
4909
  sz_md += len(buf)
@@ -6207,6 +6218,7 @@ class HttpCli(object):
6207
6218
 
6208
6219
  if "v" in self.uparam:
6209
6220
  add_og = True
6221
+ og_fn = ""
6210
6222
 
6211
6223
  if "b" in self.uparam:
6212
6224
  self.out_headers["X-Robots-Tag"] = "noindex, nofollow"
copyparty/mdns.py CHANGED
@@ -12,7 +12,9 @@ from ipaddress import IPv4Network, IPv6Network
12
12
  from .__init__ import TYPE_CHECKING
13
13
  from .__init__ import unicode as U
14
14
  from .multicast import MC_Sck, MCast
15
- from .stolen.dnslib import AAAA
15
+ from .stolen.dnslib import (
16
+ AAAA,
17
+ )
16
18
  from .stolen.dnslib import CLASS as DC
17
19
  from .stolen.dnslib import (
18
20
  NSEC,
copyparty/mtag.py CHANGED
@@ -426,9 +426,8 @@ def _get_cover_from_epub2(
426
426
  ) :
427
427
  # <meta name="cover" content="id-to-cover-image"> in <metadata>, then
428
428
  # <item> in <manifest>
429
- cover_id = package_root.find("./metadata/meta[@name='cover']", package_ns).get(
430
- "content"
431
- )
429
+ xn = package_root.find("./metadata/meta[@name='cover']", package_ns)
430
+ cover_id = xn.get("content") if xn is not None else None
432
431
 
433
432
  if not cover_id:
434
433
  return None
@@ -504,7 +503,6 @@ class MTag(object):
504
503
  "album-artist",
505
504
  "tpe2",
506
505
  "aart",
507
- "conductor",
508
506
  "organization",
509
507
  "band",
510
508
  ],
copyparty/svchub.py CHANGED
@@ -24,6 +24,7 @@ from .__init__ import ANYWIN, EXE, MACOS, PY2, TYPE_CHECKING, E, EnvParams, unic
24
24
  from .authsrv import BAD_CFG, AuthSrv, n_du_who, n_ver_who
25
25
  from .bos import bos
26
26
  from .cert import ensure_cert
27
+ from .fsutil import ramdisk_chk
27
28
  from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, HAVE_MUTAGEN
28
29
  from .pwhash import HAVE_ARGON2
29
30
  from .tcpsrv import TcpSrv
@@ -304,6 +305,7 @@ class SvcHub(object):
304
305
 
305
306
  # initiate all services to manage
306
307
  self.asrv = AuthSrv(self.args, self.log, dargs=self.dargs)
308
+ ramdisk_chk(self.asrv)
307
309
 
308
310
  if args.cgen:
309
311
  self.asrv.cgen()
@@ -842,6 +844,10 @@ class SvcHub(object):
842
844
  if w8:
843
845
  time.sleep(w8)
844
846
  self.log("qr-code", qr)
847
+ if self.args.qr_stdout:
848
+ self.pr(self.tcpsrv.qr)
849
+ if self.args.qr_stderr:
850
+ self.pr(self.tcpsrv.qr, file=sys.stderr)
845
851
  w8 = self.args.qr_every
846
852
  msg = "%s\033[%dA" % (qr, len(qr.split("\n")))
847
853
  while w8:
@@ -875,8 +881,13 @@ class SvcHub(object):
875
881
  self.sticky_qr()
876
882
  if self.args.qr_wait or self.args.qr_every or self.args.qr_winch:
877
883
  Daemon(self._qr_thr, "qr")
878
- elif not self.args.qr_pin:
879
- self.log("qr-code", self.tcpsrv.qr)
884
+ else:
885
+ if not self.args.qr_pin:
886
+ self.log("qr-code", self.tcpsrv.qr)
887
+ if self.args.qr_stdout:
888
+ self.pr(self.tcpsrv.qr)
889
+ if self.args.qr_stderr:
890
+ self.pr(self.tcpsrv.qr, file=sys.stderr)
880
891
  else:
881
892
  self.log("root", "workers OK\n")
882
893
 
@@ -1340,6 +1351,7 @@ class SvcHub(object):
1340
1351
  with self.reload_mutex:
1341
1352
  self.log("root", "reloading config")
1342
1353
  self.asrv.reload(9 if up2k else 4)
1354
+ ramdisk_chk(self.asrv)
1343
1355
  if up2k:
1344
1356
  self.up2k.reload(rescan_all_vols)
1345
1357
  t += "; volumes are now reinitializing"
copyparty/u2idx.py CHANGED
@@ -50,6 +50,11 @@ class U2idx(object):
50
50
  self.log("your python does not have sqlite3; searching will be disabled")
51
51
  return
52
52
 
53
+ if self.args.srch_icase:
54
+ self._open_db = self._open_db_icase
55
+ else:
56
+ self._open_db = self._open_db_std
57
+
53
58
 
54
59
  self.active_id = ""
55
60
  self.active_cur = None
@@ -65,6 +70,15 @@ class U2idx(object):
65
70
  def log(self, msg , c = 0) :
66
71
  self.log_func("u2idx", msg, c)
67
72
 
73
+ def _open_db_std(self, *args, **kwargs):
74
+ kwargs["check_same_thread"] = False
75
+ return sqlite3.connect(*args, **kwargs)
76
+
77
+ def _open_db_icase(self, *args, **kwargs):
78
+ db = self._open_db_std(*args, **kwargs)
79
+ db.create_function("casefold", 1, lambda x: x.casefold() if x else x)
80
+ return db
81
+
68
82
  def shutdown(self) :
69
83
  if not HAVE_SQLITE3:
70
84
  return
@@ -142,8 +156,7 @@ class U2idx(object):
142
156
  uri = ""
143
157
  try:
144
158
  uri = "{}?mode=ro&nolock=1".format(Path(db_path).as_uri())
145
- db = sqlite3.connect(uri, timeout=2, uri=True, check_same_thread=False)
146
- cur = db.cursor()
159
+ cur = self._open_db(uri, timeout=2, uri=True).cursor()
147
160
  cur.execute('pragma table_info("up")').fetchone()
148
161
  self.log("ro: %r" % (db_path,))
149
162
  except:
@@ -154,7 +167,7 @@ class U2idx(object):
154
167
  if not cur:
155
168
  # on windows, this steals the write-lock from up2k.deferred_init --
156
169
  # seen on win 10.0.17763.2686, py 3.10.4, sqlite 3.37.2
157
- cur = sqlite3.connect(db_path, timeout=2, check_same_thread=False).cursor()
170
+ cur = self._open_db(db_path, timeout=2).cursor()
158
171
  self.log("opened %r" % (db_path,))
159
172
 
160
173
  self.cur[ptop] = cur
@@ -167,6 +180,8 @@ class U2idx(object):
167
180
  if not HAVE_SQLITE3:
168
181
  return [], [], False
169
182
 
183
+ icase = self.args.srch_icase
184
+
170
185
  q = ""
171
186
  v = ""
172
187
  va = []
@@ -226,9 +241,13 @@ class U2idx(object):
226
241
  elif v == "path":
227
242
  v = "trim(?||up.rd,'/')"
228
243
  va.append("\nrd")
244
+ if icase:
245
+ v = "casefold(%s)" % (v,)
229
246
 
230
247
  elif v == "name":
231
248
  v = "up.fn"
249
+ if icase:
250
+ v = "casefold(%s)" % (v,)
232
251
 
233
252
  elif v == "tags" or ptn_mt.match(v):
234
253
  have_mt = True
@@ -279,6 +298,12 @@ class U2idx(object):
279
298
  tail = "||'%'"
280
299
  v = v[:-1]
281
300
 
301
+ if icase and "casefold(" in q:
302
+ try:
303
+ v = unicode(v).casefold()
304
+ except:
305
+ v = unicode(v).lower()
306
+
282
307
  q += " {}?{} ".format(head, tail)
283
308
  va.append(v)
284
309
  is_key = True
@@ -313,7 +338,7 @@ class U2idx(object):
313
338
  uname ,
314
339
  vols ,
315
340
  uq ,
316
- uv ,
341
+ uv ,
317
342
  have_mt ,
318
343
  sort ,
319
344
  lim ,
copyparty/up2k.py CHANGED
@@ -60,6 +60,7 @@ from .util import (
60
60
  sfsenc,
61
61
  spack,
62
62
  statdir,
63
+ trystat_shutil_copy2,
63
64
  ub64enc,
64
65
  unhumanize,
65
66
  vjoin,
@@ -208,7 +209,7 @@ class Up2k(object):
208
209
  t = "could not initialize sqlite3, will use in-memory registry only"
209
210
  self.log(t, 3)
210
211
 
211
- self.fstab = Fstab(self.log_func, self.args)
212
+ self.fstab = Fstab(self.log_func, self.args, True)
212
213
  self.gen_fk = self._gen_fk if self.args.log_fk else gen_filekey
213
214
 
214
215
  if self.args.hash_mt < 2:
@@ -1132,7 +1133,7 @@ class Up2k(object):
1132
1133
  ft = "\033[0;32m{}{:.0}"
1133
1134
  ff = "\033[0;35m{}{:.0}"
1134
1135
  fv = "\033[0;36m{}:\033[90m{}"
1135
- zs = "du_iwho ext_th_d html_head put_name2 mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
1136
+ zs = "bcasechk du_iwho ext_th_d html_head put_name2 mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
1136
1137
  fx = set(zs.split())
1137
1138
  fd = vf_bmap()
1138
1139
  fd.update(vf_cmap())
@@ -1490,6 +1491,7 @@ class Up2k(object):
1490
1491
 
1491
1492
  th_cvd = self.args.th_coversd
1492
1493
  th_cvds = self.args.th_coversd_set
1494
+ scan_pr_s = self.args.scan_pr_s
1493
1495
 
1494
1496
  self.pp.msg = "a%d %s" % (self.pp.n, cdir)
1495
1497
 
@@ -1701,7 +1703,7 @@ class Up2k(object):
1701
1703
  if nohash or not sz:
1702
1704
  wark = up2k_wark_from_metadata(self.salt, sz, lmod, rd, fn)
1703
1705
  else:
1704
- if sz > 1024 * 1024:
1706
+ if sz > 1024 * 1024 * scan_pr_s:
1705
1707
  self.log("file: %r" % (abspath,))
1706
1708
 
1707
1709
  try:
@@ -2753,7 +2755,7 @@ class Up2k(object):
2753
2755
  cur.close()
2754
2756
  db.close()
2755
2757
 
2756
- shutil.copy2(fsenc(db_path), fsenc(bak))
2758
+ trystat_shutil_copy2(self.log, fsenc(db_path), fsenc(bak))
2757
2759
  return self._orz(db_path)
2758
2760
 
2759
2761
  def _read_ver(self, cur ) :
@@ -2990,7 +2992,7 @@ class Up2k(object):
2990
2992
 
2991
2993
  # check if filesystem supports sparse files;
2992
2994
  # refuse out-of-order / multithreaded uploading if sprs False
2993
- sprs = self.fstab.get(pdir) != "ng"
2995
+ sprs = self.fstab.get(pdir)[0] != "ng"
2994
2996
 
2995
2997
  if True:
2996
2998
  jcur = self.cur.get(ptop)
@@ -3575,7 +3577,7 @@ class Up2k(object):
3575
3577
  t = "BUG: no valid sources to link from! orig(%r) fsrc(%r) link(%r)"
3576
3578
  self.log(t, 1)
3577
3579
  raise Exception(t % (src, fsrc, dst))
3578
- shutil.copy2(fsenc(csrc), fsenc(dst))
3580
+ trystat_shutil_copy2(self.log, fsenc(csrc), fsenc(dst))
3579
3581
 
3580
3582
  if lmod and (not linked or SYMTIME):
3581
3583
  bos.utime_c(self.log, dst, int(lmod), False)
@@ -4124,6 +4126,9 @@ class Up2k(object):
4124
4126
  except:
4125
4127
  raise Pebkac(400, "file not found on disk (already deleted?)")
4126
4128
 
4129
+ if "bcasechk" in vn.flags and not vn.casechk(rem, False):
4130
+ raise Pebkac(400, "file does not exist case-sensitively")
4131
+
4127
4132
  scandir = not self.args.no_scandir
4128
4133
  if is_dir:
4129
4134
  # note: deletion inside shares would require a rewrite here;
@@ -4248,6 +4253,9 @@ class Up2k(object):
4248
4253
  self.db_act = self.vol_act[svn_dbv.realpath] = time.time()
4249
4254
 
4250
4255
  st = bos.stat(sabs)
4256
+ if "bcasechk" in svn.flags and not svn.casechk(srem, False):
4257
+ raise Pebkac(400, "file does not exist case-sensitively")
4258
+
4251
4259
  if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode):
4252
4260
  with self.mutex:
4253
4261
  try:
@@ -4405,7 +4413,7 @@ class Up2k(object):
4405
4413
  b1, b2 = fsenc(sabs), fsenc(dabs)
4406
4414
  is_link = os.path.islink(b1) # due to _relink
4407
4415
  try:
4408
- shutil.copy2(b1, b2)
4416
+ trystat_shutil_copy2(self.log, b1, b2)
4409
4417
  except:
4410
4418
  try:
4411
4419
  wunlink(self.log, dabs, dvn.flags)
@@ -4465,6 +4473,9 @@ class Up2k(object):
4465
4473
  raise Pebkac(400, "mv: cannot move a mountpoint")
4466
4474
 
4467
4475
  st = bos.lstat(sabs)
4476
+ if "bcasechk" in svn.flags and not svn.casechk(srem, False):
4477
+ raise Pebkac(400, "file does not exist case-sensitively")
4478
+
4468
4479
  if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode):
4469
4480
  with self.mutex:
4470
4481
  try:
@@ -4710,7 +4721,7 @@ class Up2k(object):
4710
4721
  b1, b2 = fsenc(sabs), fsenc(dabs)
4711
4722
  is_link = os.path.islink(b1) # due to _relink
4712
4723
  try:
4713
- shutil.copy2(b1, b2)
4724
+ trystat_shutil_copy2(self.log, b1, b2)
4714
4725
  except:
4715
4726
  try:
4716
4727
  wunlink(self.log, dabs, dvn.flags)
@@ -5145,7 +5156,7 @@ class Up2k(object):
5145
5156
  sprs = False
5146
5157
 
5147
5158
  if not ANYWIN and sprs and sz > 1024 * 1024:
5148
- fs = self.fstab.get(pdir)
5159
+ fs, mnt = self.fstab.get(pdir)
5149
5160
  if fs == "ok":
5150
5161
  pass
5151
5162
  elif "nosparse" in vf:
copyparty/util.py CHANGED
@@ -525,6 +525,8 @@ def py_desc() :
525
525
  ofs = py_ver.find(".final.")
526
526
  if ofs > 0:
527
527
  py_ver = py_ver[:ofs]
528
+ if "free-threading" in sys.version:
529
+ py_ver += "t"
528
530
 
529
531
  host_os = platform.system()
530
532
  compiler = platform.python_compiler().split("http")[0]
@@ -1040,16 +1042,18 @@ class ProgressPrinter(threading.Thread):
1040
1042
  sigblock()
1041
1043
  tp = 0
1042
1044
  msg = None
1043
- no_stdout = self.args.q
1045
+ slp_pr = self.args.scan_pr_r
1046
+ slp_ps = min(slp_pr, self.args.scan_st_r)
1047
+ no_stdout = self.args.q or slp_pr == slp_ps
1044
1048
  fmt = " {}\033[K\r" if VT100 else " {} $\r"
1045
1049
  while not self.end:
1046
- time.sleep(0.1)
1050
+ time.sleep(slp_ps)
1047
1051
  if msg == self.msg or self.end:
1048
1052
  continue
1049
1053
 
1050
1054
  msg = self.msg
1051
1055
  now = time.time()
1052
- if msg and now - tp > 10:
1056
+ if msg and now - tp >= slp_pr:
1053
1057
  tp = now
1054
1058
  self.log("progress: %r" % (msg,), 6)
1055
1059
 
@@ -1495,10 +1499,12 @@ def vol_san(vols , txt ) :
1495
1499
  bvp = vol.vpath.encode("utf-8")
1496
1500
  bvph = b"$hist(/" + bvp + b")"
1497
1501
 
1498
- txt = txt.replace(bap, bvp)
1499
- txt = txt.replace(bhp, bvph)
1500
- txt = txt.replace(bap.replace(b"\\", b"\\\\"), bvp)
1501
- txt = txt.replace(bhp.replace(b"\\", b"\\\\"), bvph)
1502
+ if bap:
1503
+ txt = txt.replace(bap, bvp)
1504
+ txt = txt.replace(bap.replace(b"\\", b"\\\\"), bvp)
1505
+ if bhp:
1506
+ txt = txt.replace(bhp, bvph)
1507
+ txt = txt.replace(bhp.replace(b"\\", b"\\\\"), bvph)
1502
1508
 
1503
1509
  if vol.histpath != vol.dbpath:
1504
1510
  bdp = vol.dbpath.encode("utf-8")
@@ -2174,14 +2180,14 @@ def odfusion(
2174
2180
  ret = base.copy()
2175
2181
  if oth.startswith("+"):
2176
2182
  for k in words1:
2177
- ret[k] = True
2183
+ ret[k] = True # type: ignore
2178
2184
  elif oth[:1] in ("-", "/"):
2179
2185
  for k in words1:
2180
- ret.pop(k, None)
2186
+ ret.pop(k, None) # type: ignore
2181
2187
  else:
2182
2188
  ret = ODict.fromkeys(words0, True)
2183
2189
 
2184
- return ret
2190
+ return ret # type: ignore
2185
2191
 
2186
2192
 
2187
2193
  def ipnorm(ip ) :
@@ -2553,6 +2559,24 @@ def set_fperms(f , vf ) :
2553
2559
  os.fchown(fno, vf["uid"], vf["gid"])
2554
2560
 
2555
2561
 
2562
+ def trystat_shutil_copy2(log , src , dst ) :
2563
+ try:
2564
+ return shutil.copy2(src, dst)
2565
+ except:
2566
+ # ignore failed mtime on linux+ntfs; for example:
2567
+ # shutil.py:437 <copy2>: copystat(src, dst, follow_symlinks=follow_symlinks)
2568
+ # shutil.py:376 <copystat>: lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns),
2569
+ # [PermissionError] [Errno 1] Operation not permitted, '/windows/_videos'
2570
+ _, _, tb = sys.exc_info()
2571
+ for _, _, fun, _ in traceback.extract_tb(tb):
2572
+ if fun == "copystat":
2573
+ if log:
2574
+ t = "warning: failed to retain some file attributes (timestamp and/or permissions) during copy from %r to %r:\n%s"
2575
+ log(t % (src, dst, min_ex()), 3)
2576
+ return dst # close enough
2577
+ raise
2578
+
2579
+
2556
2580
  def _fs_mvrm(
2557
2581
  log , src , dst , atomic , flags
2558
2582
  ) :
@@ -2591,7 +2615,7 @@ def _fs_mvrm(
2591
2615
  t = "something appeared at dst; aborting rename %r ==> %r"
2592
2616
  log(t % (src, dst), 1)
2593
2617
  return False
2594
- osfun(*args)
2618
+ osfun(*args) # type: ignore
2595
2619
  if attempt:
2596
2620
  now = time.time()
2597
2621
  t = "%sd in %.2f sec, attempt %d: %r"
@@ -2641,7 +2665,7 @@ def atomic_move(log , src , dst , flags ) :
2641
2665
  os.unlink(bdst)
2642
2666
  except:
2643
2667
  pass
2644
- shutil.move(bsrc, bdst)
2668
+ shutil.move(bsrc, bdst) # type: ignore
2645
2669
 
2646
2670
 
2647
2671
  def wunlink(log , abspath , flags ) :
@@ -3046,7 +3070,7 @@ def sendfile_kern(
3046
3070
  try:
3047
3071
  req = min(0x2000000, upper - ofs) # 32 MiB
3048
3072
  if use_poll:
3049
- poll.poll(10000)
3073
+ poll.poll(10000) # type: ignore
3050
3074
  else:
3051
3075
  select.select([], [out_fd], [], 10)
3052
3076
  n = os.sendfile(out_fd, in_fd, ofs, req)
@@ -3334,7 +3358,9 @@ NICEB = NICES.encode("utf-8")
3334
3358
 
3335
3359
 
3336
3360
  def runcmd(
3337
- argv , timeout = None, **ka
3361
+ argv ,
3362
+ timeout = None,
3363
+ **ka
3338
3364
  ) :
3339
3365
  isbytes = isinstance(argv[0], (bytes, bytearray))
3340
3366
  oom = ka.pop("oom", 0) # 0..1000
@@ -3353,19 +3379,19 @@ def runcmd(
3353
3379
  if ANYWIN:
3354
3380
  if isbytes:
3355
3381
  if argv[0] in CMD_EXEB:
3356
- argv[0] += b".exe"
3382
+ argv[0] += b".exe" # type: ignore
3357
3383
  else:
3358
3384
  if argv[0] in CMD_EXES:
3359
- argv[0] += ".exe"
3385
+ argv[0] += ".exe" # type: ignore
3360
3386
 
3361
3387
  if ka.pop("nice", None):
3362
3388
  if WINDOWS:
3363
3389
  ka["creationflags"] = 0x4000
3364
3390
  elif NICEB:
3365
3391
  if isbytes:
3366
- argv = [NICEB] + argv
3392
+ argv = [NICEB] + argv # type: ignore
3367
3393
  else:
3368
- argv = [NICES] + argv
3394
+ argv = [NICES] + argv # type: ignore
3369
3395
 
3370
3396
  p = sp.Popen(argv, stdout=cout, stderr=cerr, **ka)
3371
3397
 
@@ -3377,10 +3403,10 @@ def runcmd(
3377
3403
  pass
3378
3404
 
3379
3405
  if not timeout or PY2:
3380
- bout, berr = p.communicate(sin)
3406
+ bout, berr = p.communicate(sin) # type: ignore
3381
3407
  else:
3382
3408
  try:
3383
- bout, berr = p.communicate(sin, timeout=timeout)
3409
+ bout, berr = p.communicate(sin, timeout=timeout) # type: ignore
3384
3410
  except sp.TimeoutExpired:
3385
3411
  if kill == "n":
3386
3412
  return -18, "", "" # SIGCONT; leave it be
@@ -3390,7 +3416,7 @@ def runcmd(
3390
3416
  killtree(p.pid)
3391
3417
 
3392
3418
  try:
3393
- bout, berr = p.communicate(timeout=1)
3419
+ bout, berr = p.communicate(timeout=1) # type: ignore
3394
3420
  except:
3395
3421
  bout = b""
3396
3422
  berr = b""
@@ -3812,7 +3838,7 @@ def runhook(
3812
3838
  at ,
3813
3839
  txt ,
3814
3840
  ) :
3815
- args = (broker or up2k).args
3841
+ args = (broker or up2k).args # type: ignore
3816
3842
  verbose = args.hook_v
3817
3843
  vp = vp.replace("\\", "/")
3818
3844
  ret = {"rc": 0}
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
copyparty/web/md.css.gz CHANGED
Binary file
copyparty/web/md.js.gz CHANGED
Binary file
copyparty/web/md2.css.gz CHANGED
Binary file
copyparty/web/md2.js.gz CHANGED
Binary file
copyparty/web/mde.css.gz CHANGED
Binary file
copyparty/web/mde.js.gz CHANGED
Binary file
copyparty/web/msg.css.gz CHANGED
Binary file
copyparty/web/rups.css.gz CHANGED
Binary file
copyparty/web/rups.js.gz CHANGED
Binary file
Binary file
Binary file
Binary file
Binary file
copyparty/web/svcs.js.gz CHANGED
Binary file
copyparty/web/ui.css.gz CHANGED
Binary file
copyparty/web/up2k.js.gz CHANGED
Binary file
copyparty/web/util.js.gz CHANGED
Binary file
Binary file