copyparty 1.13.2__py3-none-any.whl → 1.13.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/ftpd.py CHANGED
@@ -19,6 +19,7 @@ from .__init__ import PY2, TYPE_CHECKING
19
19
  from .authsrv import VFS
20
20
  from .bos import bos
21
21
  from .util import (
22
+ VF_CAREFUL,
22
23
  Daemon,
23
24
  ODict,
24
25
  Pebkac,
@@ -30,6 +31,7 @@ from .util import (
30
31
  runhook,
31
32
  sanitize_fn,
32
33
  vjoin,
34
+ wunlink,
33
35
  )
34
36
 
35
37
  if TYPE_CHECKING:
@@ -134,6 +136,9 @@ class FtpFs(AbstractedFS):
134
136
  self.listdirinfo = self.listdir
135
137
  self.chdir(".")
136
138
 
139
+ def log(self, msg , c = 0) :
140
+ self.hub.log("ftpd", msg, c)
141
+
137
142
  def v2a(
138
143
  self,
139
144
  vpath ,
@@ -202,17 +207,37 @@ class FtpFs(AbstractedFS):
202
207
  w = "w" in mode or "a" in mode or "+" in mode
203
208
 
204
209
  ap = self.rv2a(filename, r, w)[0]
210
+ self.validpath(ap)
205
211
  if w:
206
212
  try:
207
213
  st = bos.stat(ap)
208
214
  td = time.time() - st.st_mtime
215
+ need_unlink = True
209
216
  except:
217
+ need_unlink = False
210
218
  td = 0
211
219
 
212
- if td < -1 or td > self.args.ftp_wt:
213
- raise FSE("Cannot open existing file for writing")
220
+ if w and need_unlink:
221
+ if td >= -1 and td <= self.args.ftp_wt:
222
+ # within permitted timeframe; unlink and accept
223
+ do_it = True
224
+ elif self.args.no_del or self.args.ftp_no_ow:
225
+ # file too old, or overwrite not allowed; reject
226
+ do_it = False
227
+ else:
228
+ # allow overwrite if user has delete permission
229
+ # (avoids win2000 freaking out and deleting the server copy without uploading its own)
230
+ try:
231
+ self.rv2a(filename, False, True, False, True)
232
+ do_it = True
233
+ except:
234
+ do_it = False
235
+
236
+ if not do_it:
237
+ raise FSE("File already exists")
238
+
239
+ wunlink(self.log, ap, VF_CAREFUL)
214
240
 
215
- self.validpath(ap)
216
241
  return open(fsenc(ap), mode, self.args.iobuf)
217
242
 
218
243
  def chdir(self, path ) :
@@ -277,9 +302,20 @@ class FtpFs(AbstractedFS):
277
302
  # display write-only folders as empty
278
303
  return []
279
304
 
280
- # return list of volumes
281
- r = {x.split("/")[0]: 1 for x in self.hub.asrv.vfs.all_vols.keys()}
282
- return list(sorted(list(r.keys())))
305
+ # return list of accessible volumes
306
+ ret = []
307
+ for vn in self.hub.asrv.vfs.all_vols.values():
308
+ if "/" in vn.vpath or not vn.vpath:
309
+ continue # only include toplevel-mounted vols
310
+
311
+ try:
312
+ self.hub.asrv.vfs.get(vn.vpath, self.uname, True, False)
313
+ ret.append(vn.vpath)
314
+ except:
315
+ pass
316
+
317
+ ret.sort()
318
+ return ret
283
319
 
284
320
  def rmdir(self, path ) :
285
321
  ap = self.rv2a(path, d=True)[0]
@@ -429,9 +465,10 @@ class FtpHandler(FTPHandler):
429
465
  None,
430
466
  xbu,
431
467
  ap,
432
- vfs.canonical(rem),
468
+ vp,
433
469
  "",
434
470
  self.uname,
471
+ self.hub.asrv.vfs.get_perms(vp, self.uname),
435
472
  0,
436
473
  0,
437
474
  self.cli_ip,
copyparty/httpcli.py CHANGED
@@ -642,8 +642,12 @@ class HttpCli(object):
642
642
  if not self._check_nonfatal(pex, post):
643
643
  self.keepalive = False
644
644
 
645
- em = str(ex)
646
- msg = em if pex is ex else min_ex()
645
+ if pex is ex:
646
+ em = msg = str(ex)
647
+ else:
648
+ em = repr(ex)
649
+ msg = min_ex()
650
+
647
651
  if pex.code != 404 or self.do_log:
648
652
  self.log(
649
653
  "%s\033[0m, %s" % (msg, self.vpath),
@@ -691,6 +695,7 @@ class HttpCli(object):
691
695
  self.vpath,
692
696
  self.host,
693
697
  self.uname,
698
+ "",
694
699
  time.time(),
695
700
  0,
696
701
  self.ip,
@@ -755,7 +760,6 @@ class HttpCli(object):
755
760
  is_jinja = True
756
761
 
757
762
  if is_jinja:
758
- print("applying jinja")
759
763
  with self.conn.hsrv.mutex:
760
764
  if html not in self.conn.hsrv.j2:
761
765
  j2env = jinja2.Environment()
@@ -1628,6 +1632,7 @@ class HttpCli(object):
1628
1632
  self.vpath,
1629
1633
  self.host,
1630
1634
  self.uname,
1635
+ self.asrv.vfs.get_perms(self.vpath, self.uname),
1631
1636
  time.time(),
1632
1637
  len(buf),
1633
1638
  self.ip,
@@ -1671,6 +1676,8 @@ class HttpCli(object):
1671
1676
  remains = int(self.headers.get("content-length", -1))
1672
1677
  if remains == -1:
1673
1678
  self.keepalive = False
1679
+ self.in_hdr_recv = True
1680
+ self.s.settimeout(max(self.args.s_tbody // 20, 1))
1674
1681
  return read_socket_unbounded(self.sr, bufsz), remains
1675
1682
  else:
1676
1683
  return read_socket(self.sr, bufsz, remains), remains
@@ -1775,6 +1782,7 @@ class HttpCli(object):
1775
1782
  self.vpath,
1776
1783
  self.host,
1777
1784
  self.uname,
1785
+ self.asrv.vfs.get_perms(self.vpath, self.uname),
1778
1786
  at,
1779
1787
  remains,
1780
1788
  self.ip,
@@ -1865,6 +1873,7 @@ class HttpCli(object):
1865
1873
  self.vpath,
1866
1874
  self.host,
1867
1875
  self.uname,
1876
+ self.asrv.vfs.get_perms(self.vpath, self.uname),
1868
1877
  mt,
1869
1878
  post_sz,
1870
1879
  self.ip,
@@ -1901,6 +1910,9 @@ class HttpCli(object):
1901
1910
  0 if ANYWIN else bos.stat(path).st_ino,
1902
1911
  )[: vfs.flags["fk"]]
1903
1912
 
1913
+ if "media" in self.uparam or "medialinks" in vfs.flags:
1914
+ vsuf += "&v" if vsuf else "?v"
1915
+
1904
1916
  vpath = "/".join([x for x in [vfs.vpath, rem, fn] if x])
1905
1917
  vpath = quotep(vpath)
1906
1918
 
@@ -2022,7 +2034,7 @@ class HttpCli(object):
2022
2034
 
2023
2035
  v = self.uparam[k]
2024
2036
 
2025
- if self._use_dirkey():
2037
+ if self._use_dirkey(self.vn, ""):
2026
2038
  vn = self.vn
2027
2039
  rem = self.rem
2028
2040
  else:
@@ -2544,6 +2556,7 @@ class HttpCli(object):
2544
2556
  self.vpath,
2545
2557
  self.host,
2546
2558
  self.uname,
2559
+ self.asrv.vfs.get_perms(self.vpath, self.uname),
2547
2560
  at,
2548
2561
  0,
2549
2562
  self.ip,
@@ -2607,6 +2620,7 @@ class HttpCli(object):
2607
2620
  self.vpath,
2608
2621
  self.host,
2609
2622
  self.uname,
2623
+ self.asrv.vfs.get_perms(self.vpath, self.uname),
2610
2624
  at,
2611
2625
  sz,
2612
2626
  self.ip,
@@ -2682,6 +2696,9 @@ class HttpCli(object):
2682
2696
  0 if ANYWIN or not ap else bos.stat(ap).st_ino,
2683
2697
  )[: vfs.flags["fk"]]
2684
2698
 
2699
+ if "media" in self.uparam or "medialinks" in vfs.flags:
2700
+ vsuf += "&v" if vsuf else "?v"
2701
+
2685
2702
  vpath = "{}/{}".format(upload_vpath, lfn).strip("/")
2686
2703
  rel_url = quotep(self.args.RS + vpath) + vsuf
2687
2704
  msg += 'sha512: {} // {} // {} bytes // <a href="/{}">{}</a> {}\n'.format(
@@ -2848,6 +2865,7 @@ class HttpCli(object):
2848
2865
  self.vpath,
2849
2866
  self.host,
2850
2867
  self.uname,
2868
+ self.asrv.vfs.get_perms(self.vpath, self.uname),
2851
2869
  time.time(),
2852
2870
  0,
2853
2871
  self.ip,
@@ -2886,6 +2904,7 @@ class HttpCli(object):
2886
2904
  self.vpath,
2887
2905
  self.host,
2888
2906
  self.uname,
2907
+ self.asrv.vfs.get_perms(self.vpath, self.uname),
2889
2908
  new_lastmod,
2890
2909
  sz,
2891
2910
  self.ip,
@@ -2940,22 +2959,24 @@ class HttpCli(object):
2940
2959
 
2941
2960
  return file_lastmod, True
2942
2961
 
2943
- def _use_dirkey(self, ap = "") :
2962
+ def _use_dirkey(self, vn , ap ) :
2944
2963
  if self.can_read or not self.can_get:
2945
2964
  return False
2946
2965
 
2947
- if self.vn.flags.get("dky"):
2966
+ if vn.flags.get("dky"):
2948
2967
  return True
2949
2968
 
2950
2969
  req = self.uparam.get("k") or ""
2951
2970
  if not req:
2952
2971
  return False
2953
2972
 
2954
- dk_len = self.vn.flags.get("dk")
2973
+ dk_len = vn.flags.get("dk")
2955
2974
  if not dk_len:
2956
2975
  return False
2957
2976
 
2958
- ap = ap or self.vn.canonical(self.rem)
2977
+ if not ap:
2978
+ ap = vn.canonical(self.rem)
2979
+
2959
2980
  zs = self.gen_fk(2, self.args.dk_salt, ap, 0, 0)[:dk_len]
2960
2981
  if req == zs:
2961
2982
  return True
@@ -2964,6 +2985,71 @@ class HttpCli(object):
2964
2985
  self.log(t % (zs, req, self.req, ap), 6)
2965
2986
  return False
2966
2987
 
2988
+ def _use_filekey(self, vn , ap , st ) :
2989
+ if self.can_read or not self.can_get:
2990
+ return False
2991
+
2992
+ req = self.uparam.get("k") or ""
2993
+ if not req:
2994
+ return False
2995
+
2996
+ fk_len = vn.flags.get("fk")
2997
+ if not fk_len:
2998
+ return False
2999
+
3000
+ if not ap:
3001
+ ap = self.vn.canonical(self.rem)
3002
+
3003
+ alg = 2 if "fka" in vn.flags else 1
3004
+
3005
+ zs = self.gen_fk(
3006
+ alg, self.args.fk_salt, ap, st.st_size, 0 if ANYWIN else st.st_ino
3007
+ )[:fk_len]
3008
+
3009
+ if req == zs:
3010
+ return True
3011
+
3012
+ t = "wrong filekey, want %s, got %s\n vp: %s\n ap: %s"
3013
+ self.log(t % (zs, req, self.req, ap), 6)
3014
+ return False
3015
+
3016
+ def _add_logues(
3017
+ self, vn , abspath , lnames
3018
+ ) :
3019
+ logues = ["", ""]
3020
+ if not self.args.no_logues:
3021
+ for n, fn in enumerate([".prologue.html", ".epilogue.html"]):
3022
+ if lnames is not None and fn not in lnames:
3023
+ continue
3024
+ fn = os.path.join(abspath, fn)
3025
+ if bos.path.exists(fn):
3026
+ with open(fsenc(fn), "rb") as f:
3027
+ logues[n] = f.read().decode("utf-8")
3028
+ if "exp" in vn.flags:
3029
+ logues[n] = self._expand(
3030
+ logues[n], vn.flags.get("exp_lg") or []
3031
+ )
3032
+
3033
+ readme = ""
3034
+ if not self.args.no_readme and not logues[1]:
3035
+ if lnames is None:
3036
+ fns = ["README.md", "readme.md"]
3037
+ elif "readme.md" in lnames:
3038
+ fns = [lnames["readme.md"]]
3039
+ else:
3040
+ fns = []
3041
+
3042
+ for fn in fns:
3043
+ fn = os.path.join(abspath, fn)
3044
+ if bos.path.isfile(fn):
3045
+ with open(fsenc(fn), "rb") as f:
3046
+ readme = f.read().decode("utf-8")
3047
+ break
3048
+ if readme and "exp" in vn.flags:
3049
+ readme = self._expand(readme, vn.flags.get("exp_md") or [])
3050
+
3051
+ return logues, readme
3052
+
2967
3053
  def _expand(self, txt , phs ) :
2968
3054
  for ph in phs:
2969
3055
  if ph.startswith("hdr."):
@@ -2993,6 +3079,7 @@ class HttpCli(object):
2993
3079
  logtail = ""
2994
3080
 
2995
3081
  if ptop is not None:
3082
+ ap_data = "<%s>" % (req_path,)
2996
3083
  try:
2997
3084
  dp, fn = os.path.split(req_path)
2998
3085
  tnam = fn + ".PARTIAL"
@@ -3190,7 +3277,14 @@ class HttpCli(object):
3190
3277
 
3191
3278
  sendfun = sendfile_kern if use_sendfile else sendfile_py
3192
3279
  remains = sendfun(
3193
- self.log, lower, upper, f, self.s, self.args.s_wr_sz, self.args.s_wr_slp
3280
+ self.log,
3281
+ lower,
3282
+ upper,
3283
+ f,
3284
+ self.s,
3285
+ self.args.s_wr_sz,
3286
+ self.args.s_wr_slp,
3287
+ not self.args.no_poll,
3194
3288
  )
3195
3289
 
3196
3290
  if remains > 0:
@@ -3332,7 +3426,16 @@ class HttpCli(object):
3332
3426
 
3333
3427
  if lower < upper and not broken:
3334
3428
  with open(req_path, "rb") as f:
3335
- remains = sendfile_py(self.log, lower, upper, f, self.s, wr_sz, wr_slp)
3429
+ remains = sendfile_py(
3430
+ self.log,
3431
+ lower,
3432
+ upper,
3433
+ f,
3434
+ self.s,
3435
+ wr_sz,
3436
+ wr_slp,
3437
+ not self.args.no_poll,
3438
+ )
3336
3439
 
3337
3440
  spd = self._spd((upper - lower) - remains)
3338
3441
  if self.do_log:
@@ -3419,7 +3522,7 @@ class HttpCli(object):
3419
3522
 
3420
3523
  bgen = packer(
3421
3524
  self.log,
3422
- self.args,
3525
+ self.asrv,
3423
3526
  fgen,
3424
3527
  utf8="utf" in uarg,
3425
3528
  pre_crc="crc" in uarg,
@@ -3844,7 +3947,7 @@ class HttpCli(object):
3844
3947
  dk_sz = False
3845
3948
  if dk:
3846
3949
  vn, rem = vfs.get(top, self.uname, False, False)
3847
- if vn.flags.get("dks") and self._use_dirkey(vn.canonical(rem)):
3950
+ if vn.flags.get("dks") and self._use_dirkey(vn, vn.canonical(rem)):
3848
3951
  dk_sz = vn.flags.get("dk")
3849
3952
 
3850
3953
  dots = False
@@ -4153,6 +4256,9 @@ class HttpCli(object):
4153
4256
  add_og = True
4154
4257
  og_fn = ""
4155
4258
 
4259
+ if "v" in self.uparam:
4260
+ add_og = og_ua = True
4261
+
4156
4262
  if "b" in self.uparam:
4157
4263
  self.out_headers["X-Robots-Tag"] = "noindex, nofollow"
4158
4264
 
@@ -4165,9 +4271,20 @@ class HttpCli(object):
4165
4271
  if idx and hasattr(idx, "p_end"):
4166
4272
  icur = idx.get_cur(dbv)
4167
4273
 
4274
+ if "k" in self.uparam or "dky" in vn.flags:
4275
+ if is_dir:
4276
+ use_dirkey = self._use_dirkey(vn, abspath)
4277
+ use_filekey = False
4278
+ else:
4279
+ use_filekey = self._use_filekey(vn, abspath, st)
4280
+ use_dirkey = False
4281
+ else:
4282
+ use_dirkey = use_filekey = False
4283
+
4168
4284
  th_fmt = self.uparam.get("th")
4169
4285
  if self.can_read or (
4170
- self.can_get and (vn.flags.get("dk") or "fk" not in vn.flags)
4286
+ self.can_get
4287
+ and (use_filekey or use_dirkey or (not is_dir and "fk" not in vn.flags))
4171
4288
  ):
4172
4289
  if th_fmt is not None:
4173
4290
  nothumb = "dthumb" in dbv.flags
@@ -4184,18 +4301,21 @@ class HttpCli(object):
4184
4301
  if cfn:
4185
4302
  fn = cfn[0]
4186
4303
  fp = os.path.join(abspath, fn)
4187
- if bos.path.exists(fp):
4188
- vrem = "{}/{}".format(vrem, fn).strip("/")
4189
- is_dir = False
4304
+ st = bos.stat(fp)
4305
+ vrem = "{}/{}".format(vrem, fn).strip("/")
4306
+ is_dir = False
4190
4307
  except:
4191
4308
  pass
4192
4309
  else:
4193
4310
  for fn in self.args.th_covers:
4194
4311
  fp = os.path.join(abspath, fn)
4195
- if bos.path.exists(fp):
4312
+ try:
4313
+ st = bos.stat(fp)
4196
4314
  vrem = "{}/{}".format(vrem, fn).strip("/")
4197
4315
  is_dir = False
4198
4316
  break
4317
+ except:
4318
+ pass
4199
4319
 
4200
4320
  if is_dir:
4201
4321
  return self.tx_svg("folder")
@@ -4247,21 +4367,10 @@ class HttpCli(object):
4247
4367
 
4248
4368
  if not is_dir and (self.can_read or self.can_get):
4249
4369
  if not self.can_read and not fk_pass and "fk" in vn.flags:
4250
- alg = 2 if "fka" in vn.flags else 1
4251
- correct = self.gen_fk(
4252
- alg,
4253
- self.args.fk_salt,
4254
- abspath,
4255
- st.st_size,
4256
- 0 if ANYWIN else st.st_ino,
4257
- )[: vn.flags["fk"]]
4258
- got = self.uparam.get("k")
4259
- if got != correct:
4260
- t = "wrong filekey, want %s, got %s\n vp: %s\n ap: %s"
4261
- self.log(t % (correct, got, self.req, abspath), 6)
4370
+ if not use_filekey:
4262
4371
  return self.tx_404()
4263
4372
 
4264
- if add_og:
4373
+ if add_og and not abspath.lower().endswith(".md"):
4265
4374
  if og_ua or self.host not in self.headers.get("referer", ""):
4266
4375
  self.vpath, og_fn = vsplit(self.vpath)
4267
4376
  vpath = self.vpath
@@ -4276,7 +4385,7 @@ class HttpCli(object):
4276
4385
  (abspath.endswith(".md") or self.can_delete)
4277
4386
  and "nohtml" not in vn.flags
4278
4387
  and (
4279
- "v" in self.uparam
4388
+ ("v" in self.uparam and abspath.endswith(".md"))
4280
4389
  or "edit" in self.uparam
4281
4390
  or "edit2" in self.uparam
4282
4391
  )
@@ -4289,7 +4398,7 @@ class HttpCli(object):
4289
4398
  )
4290
4399
 
4291
4400
  elif is_dir and not self.can_read:
4292
- if self._use_dirkey(abspath):
4401
+ if use_dirkey:
4293
4402
  is_dk = True
4294
4403
  elif not self.can_write:
4295
4404
  return self.tx_404(True)
@@ -4346,29 +4455,6 @@ class HttpCli(object):
4346
4455
  tpl = "browser2"
4347
4456
  is_js = False
4348
4457
 
4349
- logues = ["", ""]
4350
- if not self.args.no_logues:
4351
- for n, fn in enumerate([".prologue.html", ".epilogue.html"]):
4352
- fn = os.path.join(abspath, fn)
4353
- if bos.path.exists(fn):
4354
- with open(fsenc(fn), "rb") as f:
4355
- logues[n] = f.read().decode("utf-8")
4356
- if "exp" in vn.flags:
4357
- logues[n] = self._expand(
4358
- logues[n], vn.flags.get("exp_lg") or []
4359
- )
4360
-
4361
- readme = ""
4362
- if not self.args.no_readme and not logues[1]:
4363
- for fn in ["README.md", "readme.md"]:
4364
- fn = os.path.join(abspath, fn)
4365
- if bos.path.isfile(fn):
4366
- with open(fsenc(fn), "rb") as f:
4367
- readme = f.read().decode("utf-8")
4368
- break
4369
- if readme and "exp" in vn.flags:
4370
- readme = self._expand(readme, vn.flags.get("exp_md") or [])
4371
-
4372
4458
  vf = vn.flags
4373
4459
  unlist = vf.get("unlist", "")
4374
4460
  ls_ret = {
@@ -4387,8 +4473,6 @@ class HttpCli(object):
4387
4473
  "frand": bool(vn.flags.get("rand")),
4388
4474
  "unlist": unlist,
4389
4475
  "perms": perms,
4390
- "logues": logues,
4391
- "readme": readme,
4392
4476
  }
4393
4477
  cgv = {
4394
4478
  "ls0": None,
@@ -4406,8 +4490,8 @@ class HttpCli(object):
4406
4490
  "have_zip": (not self.args.no_zip),
4407
4491
  "have_unpost": int(self.args.unpost),
4408
4492
  "sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
4409
- "readme": readme,
4410
4493
  "dgrid": "grid" in vf,
4494
+ "dgsel": "gsel" in vf,
4411
4495
  "dsort": vf["sort"],
4412
4496
  "dcrop": vf["crop"],
4413
4497
  "dth3x": vf["th3x"],
@@ -4428,7 +4512,6 @@ class HttpCli(object):
4428
4512
  "have_b_u": (self.can_write and self.uparam.get("b") == "u"),
4429
4513
  "sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
4430
4514
  "url_suf": url_suf,
4431
- "logues": logues,
4432
4515
  "title": html_escape("%s %s" % (self.args.bname, self.vpath), crlf=True),
4433
4516
  "srv_info": srv_infot,
4434
4517
  "dtheme": self.args.theme,
@@ -4448,6 +4531,10 @@ class HttpCli(object):
4448
4531
  j2a["no_prism"] = True
4449
4532
 
4450
4533
  if not self.can_read and not is_dk:
4534
+ logues, readme = self._add_logues(vn, abspath, None)
4535
+ ls_ret["logues"] = j2a["logues"] = logues
4536
+ ls_ret["readme"] = cgv["readme"] = readme
4537
+
4451
4538
  if is_ls:
4452
4539
  return self.tx_ls(ls_ret)
4453
4540
 
@@ -4504,6 +4591,8 @@ class HttpCli(object):
4504
4591
  ):
4505
4592
  ls_names = exclude_dotfiles(ls_names)
4506
4593
 
4594
+ lnames = {x.lower(): x for x in ls_names}
4595
+
4507
4596
  add_dk = vf.get("dk")
4508
4597
  add_fk = vf.get("fk")
4509
4598
  fk_alg = 2 if "fka" in vf else 1
@@ -4693,9 +4782,45 @@ class HttpCli(object):
4693
4782
  else:
4694
4783
  taglist = list(tagset)
4695
4784
 
4785
+ logues, readme = self._add_logues(vn, abspath, lnames)
4786
+ ls_ret["logues"] = j2a["logues"] = logues
4787
+ ls_ret["readme"] = cgv["readme"] = readme
4788
+
4696
4789
  if not files and not dirs and not readme and not logues[0] and not logues[1]:
4697
4790
  logues[1] = "this folder is empty"
4698
4791
 
4792
+ if "descript.ion" in lnames and os.path.isfile(
4793
+ os.path.join(abspath, lnames["descript.ion"])
4794
+ ):
4795
+ rem = []
4796
+ with open(os.path.join(abspath, lnames["descript.ion"]), "rb") as f:
4797
+ for bln in [x.strip() for x in f]:
4798
+ try:
4799
+ if bln.endswith(b"\x04\xc2"):
4800
+ # multiline comment; replace literal r"\n" with " // "
4801
+ bln = bln.replace(br"\\n", b" // ")[:-2]
4802
+ ln = bln.decode("utf-8", "replace")
4803
+ if ln.startswith('"'):
4804
+ fn, desc = ln.split('" ', 1)
4805
+ fn = fn[1:]
4806
+ else:
4807
+ fn, desc = ln.split(" ", 1)
4808
+ fe = next(
4809
+ (x for x in files if x["name"].lower() == fn.lower()), None
4810
+ )
4811
+ if fe:
4812
+ fe["tags"]["descript.ion"] = desc
4813
+ else:
4814
+ t = "<li><code>%s</code> %s</li>"
4815
+ rem.append(t % (html_escape(fn), html_escape(desc)))
4816
+ except:
4817
+ pass
4818
+ if "descript.ion" not in taglist:
4819
+ taglist.insert(0, "descript.ion")
4820
+ if rem and not logues[1]:
4821
+ t = "<h3>descript.ion</h3><ul>\n"
4822
+ logues[1] = t + "\n".join(rem) + "</ul>"
4823
+
4699
4824
  if is_ls:
4700
4825
  ls_ret["dirs"] = dirs
4701
4826
  ls_ret["files"] = files
@@ -4765,14 +4890,13 @@ class HttpCli(object):
4765
4890
  self.conn.hsrv.j2[tpl] = j2env.get_template(tname)
4766
4891
  thumb = ""
4767
4892
  is_pic = is_vid = is_au = False
4768
- covernames = self.args.th_coversd
4769
- for fn in ls_names:
4770
- if fn.lower() in covernames:
4771
- thumb = fn
4893
+ for fn in self.args.th_coversd:
4894
+ if fn in lnames:
4895
+ thumb = lnames[fn]
4772
4896
  break
4773
4897
  if og_fn:
4774
4898
  ext = og_fn.split(".")[-1].lower()
4775
- if ext in self.thumbcli.thumbable:
4899
+ if self.thumbcli and ext in self.thumbcli.thumbable:
4776
4900
  is_pic = (
4777
4901
  ext in self.thumbcli.fmt_pil
4778
4902
  or ext in self.thumbcli.fmt_vips
copyparty/httpsrv.py CHANGED
@@ -12,7 +12,7 @@ import time
12
12
 
13
13
  import queue
14
14
 
15
- from .__init__ import ANYWIN, CORES, EXE, MACOS, TYPE_CHECKING, EnvParams
15
+ from .__init__ import ANYWIN, CORES, EXE, MACOS, TYPE_CHECKING, EnvParams, unicode
16
16
 
17
17
  try:
18
18
  MNFE = ModuleNotFoundError
@@ -331,11 +331,11 @@ class HttpSrv(object):
331
331
 
332
332
  try:
333
333
  sck, saddr = srv_sck.accept()
334
- cip, cport = saddr[:2]
334
+ cip = unicode(saddr[0])
335
335
  if cip.startswith("::ffff:"):
336
336
  cip = cip[7:]
337
337
 
338
- addr = (cip, cport)
338
+ addr = (cip, saddr[1])
339
339
  except (OSError, socket.error) as ex:
340
340
  if self.stopping:
341
341
  break
copyparty/mdns.py CHANGED
@@ -288,6 +288,22 @@ class MDNS(MCast):
288
288
  def run2(self) :
289
289
  last_hop = time.time()
290
290
  ihop = self.args.mc_hop
291
+
292
+ try:
293
+ if self.args.no_poll:
294
+ raise Exception()
295
+ fd2sck = {}
296
+ srvpoll = select.poll()
297
+ for sck in self.srv:
298
+ fd = sck.fileno()
299
+ fd2sck[fd] = sck
300
+ srvpoll.register(fd, select.POLLIN)
301
+ except Exception as ex:
302
+ srvpoll = None
303
+ if not self.args.no_poll:
304
+ t = "WARNING: failed to poll(), will use select() instead: %r"
305
+ self.log(t % (ex,), 3)
306
+
291
307
  while self.running:
292
308
  timeout = (
293
309
  0.02 + random.random() * 0.07
@@ -296,8 +312,13 @@ class MDNS(MCast):
296
312
  if self.unsolicited
297
313
  else (last_hop + ihop if ihop else 180)
298
314
  )
299
- rdy = select.select(self.srv, [], [], timeout)
300
- rx = rdy[0] # type: ignore
315
+ if srvpoll:
316
+ pr = srvpoll.poll(timeout * 1000)
317
+ rx = [fd2sck[x[0]] for x in pr if x[1] & select.POLLIN]
318
+ else:
319
+ rdy = select.select(self.srv, [], [], timeout)
320
+ rx = rdy[0] # type: ignore
321
+
301
322
  self.rx4.cln()
302
323
  self.rx6.cln()
303
324
  buf = b""
@@ -336,7 +357,7 @@ class MDNS(MCast):
336
357
  except:
337
358
  pass
338
359
 
339
- self.srv = {}
360
+ self.srv.clear()
340
361
 
341
362
  def eat(self, buf , addr , sck ) :
342
363
  cip = addr[0]