copyparty 1.13.6__py3-none-any.whl → 1.13.8__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 (42) hide show
  1. copyparty/__main__.py +25 -7
  2. copyparty/__version__.py +2 -2
  3. copyparty/authsrv.py +9 -6
  4. copyparty/cert.py +1 -1
  5. copyparty/fsutil.py +3 -3
  6. copyparty/ftpd.py +15 -2
  7. copyparty/httpcli.py +224 -81
  8. copyparty/httpconn.py +3 -0
  9. copyparty/httpsrv.py +38 -11
  10. copyparty/ico.py +1 -1
  11. copyparty/mtag.py +15 -6
  12. copyparty/pwhash.py +10 -0
  13. copyparty/smbd.py +20 -2
  14. copyparty/ssdp.py +3 -3
  15. copyparty/stolen/dnslib/dns.py +6 -0
  16. copyparty/stolen/ifaddr/__init__.py +15 -1
  17. copyparty/stolen/ifaddr/_shared.py +1 -0
  18. copyparty/stolen/qrcodegen.py +6 -0
  19. copyparty/sutil.py +1 -1
  20. copyparty/svchub.py +72 -3
  21. copyparty/szip.py +1 -3
  22. copyparty/tcpsrv.py +63 -8
  23. copyparty/tftpd.py +30 -4
  24. copyparty/th_srv.py +22 -1
  25. copyparty/u2idx.py +4 -1
  26. copyparty/up2k.py +221 -93
  27. copyparty/util.py +166 -31
  28. copyparty/web/a/u2c.py +10 -3
  29. copyparty/web/browser.css.gz +0 -0
  30. copyparty/web/browser2.html +0 -1
  31. copyparty/web/md.html +3 -0
  32. copyparty/web/mde.html +3 -0
  33. copyparty/web/msg.html +3 -0
  34. copyparty/web/splash.html +3 -0
  35. copyparty/web/svcs.html +3 -0
  36. copyparty/web/up2k.js.gz +0 -0
  37. {copyparty-1.13.6.dist-info → copyparty-1.13.8.dist-info}/METADATA +64 -14
  38. {copyparty-1.13.6.dist-info → copyparty-1.13.8.dist-info}/RECORD +42 -42
  39. {copyparty-1.13.6.dist-info → copyparty-1.13.8.dist-info}/LICENSE +0 -0
  40. {copyparty-1.13.6.dist-info → copyparty-1.13.8.dist-info}/WHEEL +0 -0
  41. {copyparty-1.13.6.dist-info → copyparty-1.13.8.dist-info}/entry_points.txt +0 -0
  42. {copyparty-1.13.6.dist-info → copyparty-1.13.8.dist-info}/top_level.txt +0 -0
copyparty/up2k.py CHANGED
@@ -28,8 +28,8 @@ from .fsutil import Fstab
28
28
  from .mtag import MParser, MTag
29
29
  from .util import (
30
30
  HAVE_SQLITE3,
31
- VF_CAREFUL,
32
31
  SYMTIME,
32
+ VF_CAREFUL,
33
33
  Daemon,
34
34
  MTHash,
35
35
  Pebkac,
@@ -46,6 +46,7 @@ from .util import (
46
46
  hidedir,
47
47
  humansize,
48
48
  min_ex,
49
+ pathmod,
49
50
  quotep,
50
51
  rand_name,
51
52
  ren_open,
@@ -162,6 +163,7 @@ class Up2k(object):
162
163
  self.xiu_ptn = re.compile(r"(?:^|,)i([0-9]+)")
163
164
  self.xiu_busy = False # currently running hook
164
165
  self.xiu_asleep = True # needs rescan_cond poke to schedule self
166
+ self.fx_backlog = []
165
167
 
166
168
  self.cur = {}
167
169
  self.mem_cur = None
@@ -2541,7 +2543,7 @@ class Up2k(object):
2541
2543
  if self.mutex.acquire(timeout=10):
2542
2544
  got_lock = True
2543
2545
  with self.reg_mutex:
2544
- return self._handle_json(cj)
2546
+ ret = self._handle_json(cj)
2545
2547
  else:
2546
2548
  t = "cannot receive uploads right now;\nserver busy with {}.\nPlease wait; the client will retry..."
2547
2549
  raise Pebkac(503, t.format(self.blocked or "[unknown]"))
@@ -2549,11 +2551,16 @@ class Up2k(object):
2549
2551
  if not PY2:
2550
2552
  raise
2551
2553
  with self.mutex, self.reg_mutex:
2552
- return self._handle_json(cj)
2554
+ ret = self._handle_json(cj)
2553
2555
  finally:
2554
2556
  if got_lock:
2555
2557
  self.mutex.release()
2556
2558
 
2559
+ if self.fx_backlog:
2560
+ self.do_fx_backlog()
2561
+
2562
+ return ret
2563
+
2557
2564
  def _handle_json(self, cj ) :
2558
2565
  ptop = cj["ptop"]
2559
2566
  if not self.register_vpath(ptop, cj["vcfg"]):
@@ -2747,7 +2754,7 @@ class Up2k(object):
2747
2754
  job = deepcopy(job)
2748
2755
  job["wark"] = wark
2749
2756
  job["at"] = cj.get("at") or time.time()
2750
- for k in "lmod ptop vtop prel host user addr".split():
2757
+ for k in "lmod ptop vtop prel name host user addr".split():
2751
2758
  job[k] = cj.get(k) or ""
2752
2759
 
2753
2760
  pdir = djoin(cj["ptop"], cj["prel"])
@@ -2755,28 +2762,43 @@ class Up2k(object):
2755
2762
  job["name"] = rand_name(
2756
2763
  pdir, cj["name"], vfs.flags["nrand"]
2757
2764
  )
2758
- else:
2759
- job["name"] = self._untaken(pdir, cj, now)
2760
2765
 
2761
2766
  dst = djoin(job["ptop"], job["prel"], job["name"])
2762
2767
  xbu = vfs.flags.get("xbu")
2763
- if xbu and not runhook(
2764
- self.log,
2765
- xbu, # type: ignore
2766
- dst,
2767
- job["vtop"],
2768
- job["host"],
2769
- job["user"],
2770
- self.asrv.vfs.get_perms(job["vtop"], job["user"]),
2771
- job["lmod"],
2772
- job["size"],
2773
- job["addr"],
2774
- job["at"],
2775
- "",
2776
- ):
2777
- t = "upload blocked by xbu server config: {}".format(dst)
2778
- self.log(t, 1)
2779
- raise Pebkac(403, t)
2768
+ if xbu:
2769
+ vp = djoin(job["vtop"], job["prel"], job["name"])
2770
+ hr = runhook(
2771
+ self.log,
2772
+ None,
2773
+ self,
2774
+ "xbu.up2k.dupe",
2775
+ xbu, # type: ignore
2776
+ dst,
2777
+ vp,
2778
+ job["host"],
2779
+ job["user"],
2780
+ self.asrv.vfs.get_perms(job["vtop"], job["user"]),
2781
+ job["lmod"],
2782
+ job["size"],
2783
+ job["addr"],
2784
+ job["at"],
2785
+ "",
2786
+ )
2787
+ if not hr:
2788
+ t = "upload blocked by xbu server config: %s" % (dst,)
2789
+ self.log(t, 1)
2790
+ raise Pebkac(403, t)
2791
+ if hr.get("reloc"):
2792
+ x = pathmod(self.asrv.vfs, dst, vp, hr["reloc"])
2793
+ if x:
2794
+ pdir, _, job["name"], (vfs, rem) = x
2795
+ dst = os.path.join(pdir, job["name"])
2796
+ job["ptop"] = vfs.realpath
2797
+ job["vtop"] = vfs.vpath
2798
+ job["prel"] = rem
2799
+ bos.makedirs(pdir)
2800
+
2801
+ job["name"] = self._untaken(pdir, job, now)
2780
2802
 
2781
2803
  if not self.args.nw:
2782
2804
  dvf = vfs.flags
@@ -2848,11 +2870,11 @@ class Up2k(object):
2848
2870
  # one chunk may occur multiple times in a file;
2849
2871
  # filter to unique values for the list of missing chunks
2850
2872
  # (preserve order to reduce disk thrashing)
2851
- lut = {}
2873
+ lut = set()
2852
2874
  for k in cj["hash"]:
2853
2875
  if k not in lut:
2854
2876
  job["need"].append(k)
2855
- lut[k] = 1
2877
+ lut.add(k)
2856
2878
 
2857
2879
  try:
2858
2880
  self._new_upload(job)
@@ -3012,7 +3034,7 @@ class Up2k(object):
3012
3034
 
3013
3035
  def handle_chunks(
3014
3036
  self, ptop , wark , chashes
3015
- ) :
3037
+ ) :
3016
3038
  with self.mutex, self.reg_mutex:
3017
3039
  self.db_act = self.vol_act[ptop] = time.time()
3018
3040
  job = self.registry[ptop].get(wark)
@@ -3021,12 +3043,37 @@ class Up2k(object):
3021
3043
  self.log("unknown wark [{}], known: {}".format(wark, known))
3022
3044
  raise Pebkac(400, "unknown wark" + SSEELOG)
3023
3045
 
3046
+ if len(chashes) > 1 and len(chashes[1]) < 44:
3047
+ # first hash is full-length; expand remaining ones
3048
+ uniq = []
3049
+ lut = set()
3050
+ for chash in job["hash"]:
3051
+ if chash not in lut:
3052
+ uniq.append(chash)
3053
+ lut.add(chash)
3054
+ try:
3055
+ nchunk = uniq.index(chashes[0])
3056
+ except:
3057
+ raise Pebkac(400, "unknown chunk0 [%s]" % (chashes[0]))
3058
+ expanded = [chashes[0]]
3059
+ for prefix in chashes[1:]:
3060
+ nchunk += 1
3061
+ chash = uniq[nchunk]
3062
+ if not chash.startswith(prefix):
3063
+ t = "next sibling chunk does not start with expected prefix [%s]: [%s]"
3064
+ raise Pebkac(400, t % (prefix, chash))
3065
+ expanded.append(chash)
3066
+ chashes = expanded
3067
+
3024
3068
  for chash in chashes:
3025
3069
  if chash not in job["need"]:
3026
3070
  msg = "chash = {} , need:\n".format(chash)
3027
3071
  msg += "\n".join(job["need"])
3028
3072
  self.log(msg)
3029
- raise Pebkac(400, "already got that (%s) but thanks??" % (chash,))
3073
+ t = "already got that (%s) but thanks??"
3074
+ if chash not in job["hash"]:
3075
+ t = "unknown chunk wtf: %s"
3076
+ raise Pebkac(400, t % (chash,))
3030
3077
 
3031
3078
  if chash in job["busy"]:
3032
3079
  nh = len(job["hash"])
@@ -3034,9 +3081,11 @@ class Up2k(object):
3034
3081
  t = "that chunk is already being written to:\n {}\n {} {}/{}\n {}"
3035
3082
  raise Pebkac(400, t.format(wark, chash, idx, nh, job["name"]))
3036
3083
 
3084
+ assert chash # type: ignore
3037
3085
  chunksize = up2k_chunksize(job["size"])
3038
3086
 
3039
3087
  coffsets = []
3088
+ nchunks = []
3040
3089
  for chash in chashes:
3041
3090
  nchunk = [n for n, v in enumerate(job["hash"]) if v == chash]
3042
3091
  if not nchunk:
@@ -3044,6 +3093,7 @@ class Up2k(object):
3044
3093
 
3045
3094
  ofs = [chunksize * x for x in nchunk]
3046
3095
  coffsets.append(ofs)
3096
+ nchunks.append(nchunk)
3047
3097
 
3048
3098
  for ofs1, ofs2 in zip(coffsets, coffsets[1:]):
3049
3099
  gap = (ofs2[0] - ofs1[0]) - chunksize
@@ -3055,16 +3105,16 @@ class Up2k(object):
3055
3105
 
3056
3106
  if not job["sprs"]:
3057
3107
  cur_sz = bos.path.getsize(path)
3058
- if ofs[0] > cur_sz:
3108
+ if coffsets[0][0] > cur_sz:
3059
3109
  t = "please upload sequentially using one thread;\nserver filesystem does not support sparse files.\n file: {}\n chunk: {}\n cofs: {}\n flen: {}"
3060
- t = t.format(job["name"], nchunk[0], ofs[0], cur_sz)
3110
+ t = t.format(job["name"], nchunks[0][0], coffsets[0][0], cur_sz)
3061
3111
  raise Pebkac(400, t)
3062
3112
 
3063
3113
  job["busy"][chash] = 1
3064
3114
 
3065
3115
  job["poke"] = time.time()
3066
3116
 
3067
- return chunksize, coffsets, path, job["lmod"], job["sprs"]
3117
+ return chashes, chunksize, coffsets, path, job["lmod"], job["sprs"]
3068
3118
 
3069
3119
  def release_chunks(self, ptop , wark , chashes ) :
3070
3120
  with self.reg_mutex:
@@ -3111,6 +3161,9 @@ class Up2k(object):
3111
3161
  with self.mutex, self.reg_mutex:
3112
3162
  self._finish_upload(ptop, wark)
3113
3163
 
3164
+ if self.fx_backlog:
3165
+ self.do_fx_backlog()
3166
+
3114
3167
  def _finish_upload(self, ptop , wark ) :
3115
3168
  """mutex(main,reg) me"""
3116
3169
  try:
@@ -3304,25 +3357,30 @@ class Up2k(object):
3304
3357
 
3305
3358
  xau = False if skip_xau else vflags.get("xau")
3306
3359
  dst = djoin(ptop, rd, fn)
3307
- if xau and not runhook(
3308
- self.log,
3309
- xau,
3310
- dst,
3311
- djoin(vtop, rd, fn),
3312
- host,
3313
- usr,
3314
- self.asrv.vfs.get_perms(djoin(vtop, rd, fn), usr),
3315
- int(ts),
3316
- sz,
3317
- ip,
3318
- at or time.time(),
3319
- "",
3320
- ):
3321
- t = "upload blocked by xau server config"
3322
- self.log(t, 1)
3323
- wunlink(self.log, dst, vflags)
3324
- self.registry[ptop].pop(wark, None)
3325
- raise Pebkac(403, t)
3360
+ if xau:
3361
+ hr = runhook(
3362
+ self.log,
3363
+ None,
3364
+ self,
3365
+ "xau.up2k",
3366
+ xau,
3367
+ dst,
3368
+ djoin(vtop, rd, fn),
3369
+ host,
3370
+ usr,
3371
+ self.asrv.vfs.get_perms(djoin(vtop, rd, fn), usr),
3372
+ ts,
3373
+ sz,
3374
+ ip,
3375
+ at or time.time(),
3376
+ "",
3377
+ )
3378
+ if not hr:
3379
+ t = "upload blocked by xau server config"
3380
+ self.log(t, 1)
3381
+ wunlink(self.log, dst, vflags)
3382
+ self.registry[ptop].pop(wark, None)
3383
+ raise Pebkac(403, t)
3326
3384
 
3327
3385
  xiu = vflags.get("xiu")
3328
3386
  if xiu:
@@ -3506,6 +3564,9 @@ class Up2k(object):
3506
3564
  if xbd:
3507
3565
  if not runhook(
3508
3566
  self.log,
3567
+ None,
3568
+ self,
3569
+ "xbd",
3509
3570
  xbd,
3510
3571
  abspath,
3511
3572
  vpath,
@@ -3515,7 +3576,7 @@ class Up2k(object):
3515
3576
  stl.st_mtime,
3516
3577
  st.st_size,
3517
3578
  ip,
3518
- 0,
3579
+ time.time(),
3519
3580
  "",
3520
3581
  ):
3521
3582
  t = "delete blocked by xbd server config: {}"
@@ -3540,6 +3601,9 @@ class Up2k(object):
3540
3601
  if xad:
3541
3602
  runhook(
3542
3603
  self.log,
3604
+ None,
3605
+ self,
3606
+ "xad",
3543
3607
  xad,
3544
3608
  abspath,
3545
3609
  vpath,
@@ -3549,7 +3613,7 @@ class Up2k(object):
3549
3613
  stl.st_mtime,
3550
3614
  st.st_size,
3551
3615
  ip,
3552
- 0,
3616
+ time.time(),
3553
3617
  "",
3554
3618
  )
3555
3619
 
@@ -3565,7 +3629,7 @@ class Up2k(object):
3565
3629
 
3566
3630
  return n_files, ok + ok2, ng + ng2
3567
3631
 
3568
- def handle_mv(self, uname , svp , dvp ) :
3632
+ def handle_mv(self, uname , ip , svp , dvp ) :
3569
3633
  if svp == dvp or dvp.startswith(svp + "/"):
3570
3634
  raise Pebkac(400, "mv: cannot move parent into subfolder")
3571
3635
 
@@ -3582,7 +3646,7 @@ class Up2k(object):
3582
3646
  if stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode):
3583
3647
  with self.mutex:
3584
3648
  try:
3585
- ret = self._mv_file(uname, svp, dvp, curs)
3649
+ ret = self._mv_file(uname, ip, svp, dvp, curs)
3586
3650
  finally:
3587
3651
  for v in curs:
3588
3652
  v.connection.commit()
@@ -3615,7 +3679,7 @@ class Up2k(object):
3615
3679
  raise Pebkac(500, "mv: bug at {}, top {}".format(svpf, svp))
3616
3680
 
3617
3681
  dvpf = dvp + svpf[len(svp) :]
3618
- self._mv_file(uname, svpf, dvpf, curs)
3682
+ self._mv_file(uname, ip, svpf, dvpf, curs)
3619
3683
  finally:
3620
3684
  for v in curs:
3621
3685
  v.connection.commit()
@@ -3640,7 +3704,7 @@ class Up2k(object):
3640
3704
  return "k"
3641
3705
 
3642
3706
  def _mv_file(
3643
- self, uname , svp , dvp , curs
3707
+ self, uname , ip , svp , dvp , curs
3644
3708
  ) :
3645
3709
  """mutex(main) me; will mutex(reg)"""
3646
3710
  svn, srem = self.asrv.vfs.get(svp, uname, True, False, True)
@@ -3674,21 +3738,27 @@ class Up2k(object):
3674
3738
  except:
3675
3739
  pass # broken symlink; keep as-is
3676
3740
 
3741
+ ftime = stl.st_mtime
3742
+ fsize = st.st_size
3743
+
3677
3744
  xbr = svn.flags.get("xbr")
3678
3745
  xar = dvn.flags.get("xar")
3679
3746
  if xbr:
3680
3747
  if not runhook(
3681
3748
  self.log,
3749
+ None,
3750
+ self,
3751
+ "xbr",
3682
3752
  xbr,
3683
3753
  sabs,
3684
3754
  svp,
3685
3755
  "",
3686
3756
  uname,
3687
3757
  self.asrv.vfs.get_perms(svp, uname),
3688
- stl.st_mtime,
3689
- st.st_size,
3690
- "",
3691
- 0,
3758
+ ftime,
3759
+ fsize,
3760
+ ip,
3761
+ time.time(),
3692
3762
  "",
3693
3763
  ):
3694
3764
  t = "move blocked by xbr server config: {}".format(svp)
@@ -3716,16 +3786,19 @@ class Up2k(object):
3716
3786
  if xar:
3717
3787
  runhook(
3718
3788
  self.log,
3789
+ None,
3790
+ self,
3791
+ "xar.ln",
3719
3792
  xar,
3720
3793
  dabs,
3721
3794
  dvp,
3722
3795
  "",
3723
3796
  uname,
3724
3797
  self.asrv.vfs.get_perms(dvp, uname),
3725
- 0,
3726
- 0,
3727
- "",
3728
- 0,
3798
+ ftime,
3799
+ fsize,
3800
+ ip,
3801
+ time.time(),
3729
3802
  "",
3730
3803
  )
3731
3804
 
@@ -3734,13 +3807,6 @@ class Up2k(object):
3734
3807
  c1, w, ftime_, fsize_, ip, at = self._find_from_vpath(svn.realpath, srem)
3735
3808
  c2 = self.cur.get(dvn.realpath)
3736
3809
 
3737
- if ftime_ is None:
3738
- ftime = stl.st_mtime
3739
- fsize = st.st_size
3740
- else:
3741
- ftime = ftime_
3742
- fsize = fsize_ or 0
3743
-
3744
3810
  has_dupes = False
3745
3811
  if w:
3746
3812
  assert c1
@@ -3748,7 +3814,9 @@ class Up2k(object):
3748
3814
  self._copy_tags(c1, c2, w)
3749
3815
 
3750
3816
  with self.reg_mutex:
3751
- has_dupes = self._forget_file(svn.realpath, srem, c1, w, is_xvol, fsize)
3817
+ has_dupes = self._forget_file(
3818
+ svn.realpath, srem, c1, w, is_xvol, fsize_ or fsize
3819
+ )
3752
3820
 
3753
3821
  if not is_xvol:
3754
3822
  has_dupes = self._relink(w, svn.realpath, srem, dabs)
@@ -3818,7 +3886,7 @@ class Up2k(object):
3818
3886
 
3819
3887
  if is_link:
3820
3888
  try:
3821
- times = (int(time.time()), int(stl.st_mtime))
3889
+ times = (int(time.time()), int(ftime))
3822
3890
  bos.utime(dabs, times, False)
3823
3891
  except:
3824
3892
  pass
@@ -3828,16 +3896,19 @@ class Up2k(object):
3828
3896
  if xar:
3829
3897
  runhook(
3830
3898
  self.log,
3899
+ None,
3900
+ self,
3901
+ "xar.mv",
3831
3902
  xar,
3832
3903
  dabs,
3833
3904
  dvp,
3834
3905
  "",
3835
3906
  uname,
3836
3907
  self.asrv.vfs.get_perms(dvp, uname),
3837
- 0,
3838
- 0,
3839
- "",
3840
- 0,
3908
+ ftime,
3909
+ fsize,
3910
+ ip,
3911
+ time.time(),
3841
3912
  "",
3842
3913
  )
3843
3914
 
@@ -4121,23 +4192,35 @@ class Up2k(object):
4121
4192
  xbu = self.flags[job["ptop"]].get("xbu")
4122
4193
  ap_chk = djoin(pdir, job["name"])
4123
4194
  vp_chk = djoin(job["vtop"], job["prel"], job["name"])
4124
- if xbu and not runhook(
4125
- self.log,
4126
- xbu,
4127
- ap_chk,
4128
- vp_chk,
4129
- job["host"],
4130
- job["user"],
4131
- self.asrv.vfs.get_perms(vp_chk, job["user"]),
4132
- int(job["lmod"]),
4133
- job["size"],
4134
- job["addr"],
4135
- int(job["t0"]),
4136
- "",
4137
- ):
4138
- t = "upload blocked by xbu server config: {}".format(vp_chk)
4139
- self.log(t, 1)
4140
- raise Pebkac(403, t)
4195
+ if xbu:
4196
+ hr = runhook(
4197
+ self.log,
4198
+ None,
4199
+ self,
4200
+ "xbu.up2k",
4201
+ xbu,
4202
+ ap_chk,
4203
+ vp_chk,
4204
+ job["host"],
4205
+ job["user"],
4206
+ self.asrv.vfs.get_perms(vp_chk, job["user"]),
4207
+ job["lmod"],
4208
+ job["size"],
4209
+ job["addr"],
4210
+ job["t0"],
4211
+ "",
4212
+ )
4213
+ if not hr:
4214
+ t = "upload blocked by xbu server config: {}".format(vp_chk)
4215
+ self.log(t, 1)
4216
+ raise Pebkac(403, t)
4217
+ if hr.get("reloc"):
4218
+ x = pathmod(self.asrv.vfs, ap_chk, vp_chk, hr["reloc"])
4219
+ if x:
4220
+ pdir, _, job["name"], (vfs, rem) = x
4221
+ job["ptop"] = vfs.realpath
4222
+ job["vtop"] = vfs.vpath
4223
+ job["prel"] = rem
4141
4224
 
4142
4225
  tnam = job["name"] + ".PARTIAL"
4143
4226
  if self.args.dotpart:
@@ -4411,6 +4494,9 @@ class Up2k(object):
4411
4494
  with self.rescan_cond:
4412
4495
  self.rescan_cond.notify_all()
4413
4496
 
4497
+ if self.fx_backlog:
4498
+ self.do_fx_backlog()
4499
+
4414
4500
  return True
4415
4501
 
4416
4502
  def hash_file(
@@ -4442,6 +4528,48 @@ class Up2k(object):
4442
4528
  self.hashq.put(zt)
4443
4529
  self.n_hashq += 1
4444
4530
 
4531
+ def do_fx_backlog(self):
4532
+ with self.mutex, self.reg_mutex:
4533
+ todo = self.fx_backlog
4534
+ self.fx_backlog = []
4535
+ for act, hr, req_vp in todo:
4536
+ self.hook_fx(act, hr, req_vp)
4537
+
4538
+ def hook_fx(self, act , hr , req_vp ) :
4539
+ bad = [k for k in hr if k != "vp"]
4540
+ if bad:
4541
+ t = "got unsupported key in %s from hook: %s"
4542
+ raise Exception(t % (act, bad))
4543
+
4544
+ for fvp in hr.get("vp") or []:
4545
+ # expect vpath including filename; either absolute
4546
+ # or relative to the client's vpath (request url)
4547
+ if fvp.startswith("/"):
4548
+ fvp, fn = vsplit(fvp[1:])
4549
+ fvp = "/" + fvp
4550
+ else:
4551
+ fvp, fn = vsplit(fvp)
4552
+
4553
+ x = pathmod(self.asrv.vfs, "", req_vp, {"vp": fvp, "fn": fn})
4554
+ if not x:
4555
+ t = "hook_fx(%s): failed to resolve %s based on %s"
4556
+ self.log(t % (act, fvp, req_vp))
4557
+ continue
4558
+
4559
+ ap, rd, fn, (vn, rem) = x
4560
+ vp = vjoin(rd, fn)
4561
+ if not vp:
4562
+ raise Exception("hook_fx: blank vp from pathmod")
4563
+
4564
+ if act == "idx":
4565
+ rd = rd[len(vn.vpath) :].strip("/")
4566
+ self.hash_file(
4567
+ vn.realpath, vn.vpath, vn.flags, rd, fn, "", time.time(), "", True
4568
+ )
4569
+
4570
+ if act == "del":
4571
+ self._handle_rm(LEELOO_DALLAS, "", vp, [], False, False)
4572
+
4445
4573
  def shutdown(self) :
4446
4574
  self.stop = True
4447
4575