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/__main__.py +94 -43
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +75 -13
- copyparty/cert.py +10 -6
- copyparty/cfg.py +3 -0
- copyparty/ftpd.py +44 -7
- copyparty/httpcli.py +188 -64
- copyparty/httpsrv.py +3 -3
- copyparty/mdns.py +24 -3
- copyparty/mtag.py +6 -1
- copyparty/smbd.py +1 -1
- copyparty/ssdp.py +23 -3
- copyparty/star.py +4 -4
- copyparty/sutil.py +12 -6
- copyparty/svchub.py +4 -2
- copyparty/szip.py +4 -4
- copyparty/tcpsrv.py +4 -1
- copyparty/tftpd.py +5 -10
- copyparty/th_cli.py +1 -1
- copyparty/th_srv.py +22 -11
- copyparty/up2k.py +84 -16
- copyparty/util.py +69 -22
- copyparty/web/a/u2c.py +14 -6
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/deps/marked.js.gz +0 -0
- copyparty/web/md2.js.gz +0 -0
- copyparty/web/ui.css.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.13.2.dist-info → copyparty-1.13.4.dist-info}/METADATA +39 -7
- {copyparty-1.13.2.dist-info → copyparty-1.13.4.dist-info}/RECORD +36 -36
- {copyparty-1.13.2.dist-info → copyparty-1.13.4.dist-info}/WHEEL +1 -1
- {copyparty-1.13.2.dist-info → copyparty-1.13.4.dist-info}/LICENSE +0 -0
- {copyparty-1.13.2.dist-info → copyparty-1.13.4.dist-info}/entry_points.txt +0 -0
- {copyparty-1.13.2.dist-info → copyparty-1.13.4.dist-info}/top_level.txt +0 -0
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
|
-
|
213
|
-
|
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
|
-
|
282
|
-
|
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
|
-
|
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
|
-
|
646
|
-
|
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
|
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 =
|
2973
|
+
dk_len = vn.flags.get("dk")
|
2955
2974
|
if not dk_len:
|
2956
2975
|
return False
|
2957
2976
|
|
2958
|
-
|
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,
|
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(
|
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.
|
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
|
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
|
-
|
4188
|
-
|
4189
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
4769
|
-
|
4770
|
-
|
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
|
334
|
+
cip = unicode(saddr[0])
|
335
335
|
if cip.startswith("::ffff:"):
|
336
336
|
cip = cip[7:]
|
337
337
|
|
338
|
-
addr = (cip,
|
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
|
-
|
300
|
-
|
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]
|