copyparty 1.16.16__py3-none-any.whl → 1.16.18__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 +21 -40
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +137 -56
- copyparty/cfg.py +10 -0
- copyparty/fsutil.py +7 -5
- copyparty/ftpd.py +11 -0
- copyparty/httpcli.py +87 -33
- copyparty/mtag.py +1 -2
- copyparty/svchub.py +5 -4
- copyparty/szip.py +1 -2
- copyparty/tcpsrv.py +18 -0
- copyparty/tftpd.py +29 -8
- copyparty/up2k.py +13 -8
- copyparty/util.py +62 -3
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.16.16.dist-info → copyparty-1.16.18.dist-info}/METADATA +14 -3
- {copyparty-1.16.16.dist-info → copyparty-1.16.18.dist-info}/RECORD +23 -23
- {copyparty-1.16.16.dist-info → copyparty-1.16.18.dist-info}/WHEEL +1 -1
- {copyparty-1.16.16.dist-info → copyparty-1.16.18.dist-info}/entry_points.txt +0 -0
- {copyparty-1.16.16.dist-info → copyparty-1.16.18.dist-info/licenses}/LICENSE +0 -0
- {copyparty-1.16.16.dist-info → copyparty-1.16.18.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
|
+
FN_EMB,
|
22
23
|
VF_CAREFUL,
|
23
24
|
Daemon,
|
24
25
|
ODict,
|
@@ -166,6 +167,16 @@ class FtpFs(AbstractedFS):
|
|
166
167
|
fn = sanitize_fn(fn or "", "")
|
167
168
|
vpath = vjoin(rd, fn)
|
168
169
|
vfs, rem = self.hub.asrv.vfs.get(vpath, self.uname, r, w, m, d)
|
170
|
+
if (
|
171
|
+
w
|
172
|
+
and fn.lower() in FN_EMB
|
173
|
+
and self.h.uname not in vfs.axs.uread
|
174
|
+
and "wo_up_readme" not in vfs.flags
|
175
|
+
):
|
176
|
+
fn = "_wo_" + fn
|
177
|
+
vpath = vjoin(rd, fn)
|
178
|
+
vfs, rem = self.hub.asrv.vfs.get(vpath, self.uname, r, w, m, d)
|
179
|
+
|
169
180
|
if not vfs.realpath:
|
170
181
|
t = "No filesystem mounted at [{}]"
|
171
182
|
raise FSE(t.format(vpath))
|
copyparty/httpcli.py
CHANGED
@@ -4,7 +4,6 @@ from __future__ import print_function, unicode_literals
|
|
4
4
|
import argparse # typechk
|
5
5
|
import copy
|
6
6
|
import errno
|
7
|
-
import gzip
|
8
7
|
import hashlib
|
9
8
|
import itertools
|
10
9
|
import json
|
@@ -22,6 +21,7 @@ from datetime import datetime
|
|
22
21
|
from operator import itemgetter
|
23
22
|
|
24
23
|
import jinja2 # typechk
|
24
|
+
from ipaddress import IPv6Network
|
25
25
|
|
26
26
|
try:
|
27
27
|
if os.environ.get("PRTY_NO_LZMA"):
|
@@ -45,6 +45,7 @@ from .util import (
|
|
45
45
|
APPLESAN_RE,
|
46
46
|
BITNESS,
|
47
47
|
DAV_ALLPROPS,
|
48
|
+
FN_EMB,
|
48
49
|
HAVE_SQLITE3,
|
49
50
|
HTTPCODE,
|
50
51
|
META_NOBOTS,
|
@@ -68,6 +69,7 @@ from .util import (
|
|
68
69
|
get_df,
|
69
70
|
get_spd,
|
70
71
|
guess_mime,
|
72
|
+
gzip,
|
71
73
|
gzip_file_orig_sz,
|
72
74
|
gzip_orig_sz,
|
73
75
|
has_resource,
|
@@ -89,6 +91,7 @@ from .util import (
|
|
89
91
|
read_socket,
|
90
92
|
read_socket_chunked,
|
91
93
|
read_socket_unbounded,
|
94
|
+
read_utf8,
|
92
95
|
relchk,
|
93
96
|
ren_open,
|
94
97
|
runhook,
|
@@ -382,11 +385,12 @@ class HttpCli(object):
|
|
382
385
|
t += ' Note: if you are behind cloudflare, then this default header is not a good choice; please first make sure your local reverse-proxy (if any) does not allow non-cloudflare IPs from providing cf-* headers, and then add this additional global setting: "--xff-hdr=cf-connecting-ip"'
|
383
386
|
else:
|
384
387
|
t += ' Note: depending on your reverse-proxy, and/or WAF, and/or other intermediates, you may want to read the true client IP from another header by also specifying "--xff-hdr=SomeOtherHeader"'
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
388
|
+
|
389
|
+
if "." in pip:
|
390
|
+
zs = ".".join(pip.split(".")[:2]) + ".0.0/16"
|
391
|
+
else:
|
392
|
+
zs = IPv6Network(pip + "/64", False).compressed
|
393
|
+
|
390
394
|
zs2 = ' or "--xff-src=lan"' if self.conn.xff_lan.map(pip) else ""
|
391
395
|
self.log(t % (self.args.xff_hdr, pip, cli_ip, zso, zs, zs2), 3)
|
392
396
|
self.bad_xff = True
|
@@ -863,8 +867,7 @@ class HttpCli(object):
|
|
863
867
|
html = html.replace("%", "", 1)
|
864
868
|
|
865
869
|
if html.startswith("@"):
|
866
|
-
|
867
|
-
html = f.read().decode("utf-8")
|
870
|
+
html = read_utf8(self.log, html[1:], True)
|
868
871
|
|
869
872
|
if html.startswith("%"):
|
870
873
|
html = html[1:]
|
@@ -1231,14 +1234,7 @@ class HttpCli(object):
|
|
1231
1234
|
return self.tx_404(True)
|
1232
1235
|
else:
|
1233
1236
|
vfs = self.asrv.vfs
|
1234
|
-
if
|
1235
|
-
not vfs.nodes
|
1236
|
-
and not vfs.axs.uread
|
1237
|
-
and not vfs.axs.uwrite
|
1238
|
-
and not vfs.axs.uget
|
1239
|
-
and not vfs.axs.uhtml
|
1240
|
-
and not vfs.axs.uadmin
|
1241
|
-
):
|
1237
|
+
if vfs.badcfg1:
|
1242
1238
|
t = "<h2>access denied due to failsafe; check server log</h2>"
|
1243
1239
|
html = self.j2s("splash", this=self, msg=t)
|
1244
1240
|
self.reply(html.encode("utf-8", "replace"), 500)
|
@@ -2544,6 +2540,16 @@ class HttpCli(object):
|
|
2544
2540
|
vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True)
|
2545
2541
|
dbv, vrem = vfs.get_dbv(rem)
|
2546
2542
|
|
2543
|
+
name = sanitize_fn(name, "")
|
2544
|
+
if (
|
2545
|
+
not self.can_read
|
2546
|
+
and self.can_write
|
2547
|
+
and name.lower() in FN_EMB
|
2548
|
+
and "wo_up_readme" not in dbv.flags
|
2549
|
+
):
|
2550
|
+
name = "_wo_" + name
|
2551
|
+
|
2552
|
+
body["name"] = name
|
2547
2553
|
body["vtop"] = dbv.vpath
|
2548
2554
|
body["ptop"] = dbv.realpath
|
2549
2555
|
body["prel"] = vrem
|
@@ -3720,8 +3726,7 @@ class HttpCli(object):
|
|
3720
3726
|
continue
|
3721
3727
|
fn = "%s/%s" % (abspath, fn)
|
3722
3728
|
if bos.path.isfile(fn):
|
3723
|
-
|
3724
|
-
logues[n] = f.read().decode("utf-8")
|
3729
|
+
logues[n] = read_utf8(self.log, fsenc(fn), False)
|
3725
3730
|
if "exp" in vn.flags:
|
3726
3731
|
logues[n] = self._expand(
|
3727
3732
|
logues[n], vn.flags.get("exp_lg") or []
|
@@ -3742,9 +3747,8 @@ class HttpCli(object):
|
|
3742
3747
|
for fn in fns:
|
3743
3748
|
fn = "%s/%s" % (abspath, fn)
|
3744
3749
|
if bos.path.isfile(fn):
|
3745
|
-
|
3746
|
-
|
3747
|
-
break
|
3750
|
+
txt = read_utf8(self.log, fsenc(fn), False)
|
3751
|
+
break
|
3748
3752
|
|
3749
3753
|
if txt and "exp" in vn.flags:
|
3750
3754
|
txt = self._expand(txt, vn.flags.get("exp_md") or [])
|
@@ -3777,6 +3781,19 @@ class HttpCli(object):
|
|
3777
3781
|
|
3778
3782
|
return txt
|
3779
3783
|
|
3784
|
+
def _can_zip(self, volflags ) :
|
3785
|
+
lvl = volflags["zip_who"]
|
3786
|
+
if self.args.no_zip or not lvl:
|
3787
|
+
return "download-as-zip/tar is disabled in server config"
|
3788
|
+
elif lvl <= 1 and not self.can_admin:
|
3789
|
+
return "download-as-zip/tar is admin-only on this server"
|
3790
|
+
elif lvl <= 2 and self.uname in ("", "*"):
|
3791
|
+
return "you must be authenticated to download-as-zip/tar on this server"
|
3792
|
+
elif self.args.ua_nozip and self.args.ua_nozip.search(self.ua):
|
3793
|
+
t = "this URL contains no valuable information for bots/crawlers"
|
3794
|
+
raise Pebkac(403, t)
|
3795
|
+
return ""
|
3796
|
+
|
3780
3797
|
def tx_res(self, req_path ) :
|
3781
3798
|
status = 200
|
3782
3799
|
logmsg = "{:4} {} ".format("", self.req)
|
@@ -4307,13 +4324,8 @@ class HttpCli(object):
|
|
4307
4324
|
rem ,
|
4308
4325
|
items ,
|
4309
4326
|
) :
|
4310
|
-
|
4311
|
-
if
|
4312
|
-
raise Pebkac(400, "download-as-zip/tar is disabled in server config")
|
4313
|
-
elif lvl <= 1 and not self.can_admin:
|
4314
|
-
raise Pebkac(400, "download-as-zip/tar is admin-only on this server")
|
4315
|
-
elif lvl <= 2 and self.uname in ("", "*"):
|
4316
|
-
t = "you must be authenticated to download-as-zip/tar on this server"
|
4327
|
+
t = self._can_zip(vn.flags)
|
4328
|
+
if t:
|
4317
4329
|
raise Pebkac(400, t)
|
4318
4330
|
|
4319
4331
|
logmsg = "{:4} {} ".format("", self.req)
|
@@ -4346,6 +4358,33 @@ class HttpCli(object):
|
|
4346
4358
|
else:
|
4347
4359
|
fn = self.host.split(":")[0]
|
4348
4360
|
|
4361
|
+
if vn.flags.get("zipmax") and (not self.uname or not "zipmaxu" in vn.flags):
|
4362
|
+
maxs = vn.flags.get("zipmaxs_v") or 0
|
4363
|
+
maxn = vn.flags.get("zipmaxn_v") or 0
|
4364
|
+
nf = 0
|
4365
|
+
nb = 0
|
4366
|
+
fgen = vn.zipgen(
|
4367
|
+
vpath, rem, set(items), self.uname, False, not self.args.no_scandir
|
4368
|
+
)
|
4369
|
+
t = "total size exceeds a limit specified in server config"
|
4370
|
+
t = vn.flags.get("zipmaxt") or t
|
4371
|
+
if maxs and maxn:
|
4372
|
+
for zd in fgen:
|
4373
|
+
nf += 1
|
4374
|
+
nb += zd["st"].st_size
|
4375
|
+
if maxs < nb or maxn < nf:
|
4376
|
+
raise Pebkac(400, t)
|
4377
|
+
elif maxs:
|
4378
|
+
for zd in fgen:
|
4379
|
+
nb += zd["st"].st_size
|
4380
|
+
if maxs < nb:
|
4381
|
+
raise Pebkac(400, t)
|
4382
|
+
elif maxn:
|
4383
|
+
for zd in fgen:
|
4384
|
+
nf += 1
|
4385
|
+
if maxn < nf:
|
4386
|
+
raise Pebkac(400, t)
|
4387
|
+
|
4349
4388
|
safe = (string.ascii_letters + string.digits).replace("%", "")
|
4350
4389
|
afn = "".join([x if x in safe.replace('"', "") else "_" for x in fn])
|
4351
4390
|
bascii = unicode(safe).encode("utf-8")
|
@@ -4991,6 +5030,8 @@ class HttpCli(object):
|
|
4991
5030
|
def get_dls(self) :
|
4992
5031
|
ret = []
|
4993
5032
|
dls = self.conn.hsrv.tdls
|
5033
|
+
enshare = self.args.shr
|
5034
|
+
shrs = enshare[1:]
|
4994
5035
|
for dl_id, (t0, sz, vn, vp, uname) in self.conn.hsrv.tdli.items():
|
4995
5036
|
t1, sent = dls[dl_id]
|
4996
5037
|
if sent > 0x100000: # 1m; buffers 2~4
|
@@ -4999,6 +5040,15 @@ class HttpCli(object):
|
|
4999
5040
|
vp = ""
|
5000
5041
|
elif self.uname not in vn.axs.udot and (vp.startswith(".") or "/." in vp):
|
5001
5042
|
vp = ""
|
5043
|
+
elif (
|
5044
|
+
enshare
|
5045
|
+
and vp.startswith(shrs)
|
5046
|
+
and self.uname != vn.shr_owner
|
5047
|
+
and self.uname not in vn.axs.uadmin
|
5048
|
+
and self.uname not in self.args.shr_adm
|
5049
|
+
and not dl_id.startswith(self.ip + ":")
|
5050
|
+
):
|
5051
|
+
vp = ""
|
5002
5052
|
if self.uname not in vn.axs.uadmin:
|
5003
5053
|
dl_id = uname = ""
|
5004
5054
|
|
@@ -5980,6 +6030,8 @@ class HttpCli(object):
|
|
5980
6030
|
zs = self.gen_fk(2, self.args.dk_salt, abspath, 0, 0)[:add_dk]
|
5981
6031
|
ls_ret["dk"] = cgv["dk"] = zs
|
5982
6032
|
|
6033
|
+
no_zip = bool(self._can_zip(vf))
|
6034
|
+
|
5983
6035
|
dirs = []
|
5984
6036
|
files = []
|
5985
6037
|
ptn_hr = RE_HR
|
@@ -6005,7 +6057,7 @@ class HttpCli(object):
|
|
6005
6057
|
is_dir = stat.S_ISDIR(inf.st_mode)
|
6006
6058
|
if is_dir:
|
6007
6059
|
href += "/"
|
6008
|
-
if
|
6060
|
+
if no_zip:
|
6009
6061
|
margin = "DIR"
|
6010
6062
|
elif add_dk:
|
6011
6063
|
zs = absreal(fspath)
|
@@ -6018,7 +6070,7 @@ class HttpCli(object):
|
|
6018
6070
|
quotep(href),
|
6019
6071
|
)
|
6020
6072
|
elif fn in hist:
|
6021
|
-
margin = '<a href="%s.hist/%s">#%s</a>' % (
|
6073
|
+
margin = '<a href="%s.hist/%s" rel="nofollow">#%s</a>' % (
|
6022
6074
|
base,
|
6023
6075
|
html_escape(hist[fn][2], quot=True, crlf=True),
|
6024
6076
|
hist[fn][0],
|
@@ -6219,6 +6271,10 @@ class HttpCli(object):
|
|
6219
6271
|
|
6220
6272
|
doc = self.uparam.get("doc") if self.can_read else None
|
6221
6273
|
if doc:
|
6274
|
+
zp = self.args.ua_nodoc
|
6275
|
+
if zp and zp.search(self.ua):
|
6276
|
+
t = "this URL contains no valuable information for bots/crawlers"
|
6277
|
+
raise Pebkac(403, t)
|
6222
6278
|
j2a["docname"] = doc
|
6223
6279
|
doctxt = None
|
6224
6280
|
dfn = lnames.get(doc.lower())
|
@@ -6229,9 +6285,7 @@ class HttpCli(object):
|
|
6229
6285
|
docpath = os.path.join(abspath, doc)
|
6230
6286
|
sz = bos.path.getsize(docpath)
|
6231
6287
|
if sz < 1024 * self.args.txt_max:
|
6232
|
-
|
6233
|
-
doctxt = f.read().decode("utf-8", "replace")
|
6234
|
-
|
6288
|
+
doctxt = read_utf8(self.log, fsenc(docpath), False)
|
6235
6289
|
if doc.lower().endswith(".md") and "exp" in vn.flags:
|
6236
6290
|
doctxt = self._expand(doctxt, vn.flags.get("exp_md") or [])
|
6237
6291
|
else:
|
copyparty/mtag.py
CHANGED
@@ -18,6 +18,7 @@ from .util import (
|
|
18
18
|
REKOBO_LKEY,
|
19
19
|
VF_CAREFUL,
|
20
20
|
fsenc,
|
21
|
+
gzip,
|
21
22
|
min_ex,
|
22
23
|
pybin,
|
23
24
|
retchk,
|
@@ -132,8 +133,6 @@ def au_unpk(
|
|
132
133
|
fd, ret = tempfile.mkstemp("." + au)
|
133
134
|
|
134
135
|
if pk == "gz":
|
135
|
-
import gzip
|
136
|
-
|
137
136
|
fi = gzip.GzipFile(abspath, mode="rb")
|
138
137
|
|
139
138
|
elif pk == "xz":
|
copyparty/svchub.py
CHANGED
@@ -3,7 +3,6 @@ from __future__ import print_function, unicode_literals
|
|
3
3
|
|
4
4
|
import argparse
|
5
5
|
import errno
|
6
|
-
import gzip
|
7
6
|
import logging
|
8
7
|
import os
|
9
8
|
import re
|
@@ -57,6 +56,7 @@ from .util import (
|
|
57
56
|
ansi_re,
|
58
57
|
build_netmap,
|
59
58
|
expat_ver,
|
59
|
+
gzip,
|
60
60
|
load_ipu,
|
61
61
|
min_ex,
|
62
62
|
mp,
|
@@ -759,7 +759,8 @@ class SvcHub(object):
|
|
759
759
|
vs = os.path.expandvars(os.path.expanduser(vs))
|
760
760
|
setattr(al, k, vs)
|
761
761
|
|
762
|
-
|
762
|
+
zs = "dav_ua1 sus_urls nonsus_urls ua_nodoc ua_nozip"
|
763
|
+
for k in zs.split(" "):
|
763
764
|
vs = getattr(al, k)
|
764
765
|
if not vs or vs == "no":
|
765
766
|
setattr(al, k, None)
|
@@ -1250,7 +1251,7 @@ class SvcHub(object):
|
|
1250
1251
|
raise
|
1251
1252
|
|
1252
1253
|
def check_mp_support(self) :
|
1253
|
-
if MACOS:
|
1254
|
+
if MACOS and not os.environ.get("PRTY_FORCE_MP"):
|
1254
1255
|
return "multiprocessing is wonky on mac osx;"
|
1255
1256
|
elif sys.version_info < (3, 3):
|
1256
1257
|
return "need python 3.3 or newer for multiprocessing;"
|
@@ -1270,7 +1271,7 @@ class SvcHub(object):
|
|
1270
1271
|
return False
|
1271
1272
|
|
1272
1273
|
try:
|
1273
|
-
if mp.cpu_count() <= 1:
|
1274
|
+
if mp.cpu_count() <= 1 and not os.environ.get("PRTY_FORCE_MP"):
|
1274
1275
|
raise Exception()
|
1275
1276
|
except:
|
1276
1277
|
self.log("svchub", "only one CPU detected; multiprocessing disabled")
|
copyparty/szip.py
CHANGED
@@ -4,12 +4,11 @@ from __future__ import print_function, unicode_literals
|
|
4
4
|
import calendar
|
5
5
|
import stat
|
6
6
|
import time
|
7
|
-
import zlib
|
8
7
|
|
9
8
|
from .authsrv import AuthSrv
|
10
9
|
from .bos import bos
|
11
10
|
from .sutil import StreamArc, errdesc
|
12
|
-
from .util import min_ex, sanitize_fn, spack, sunpack, yieldfile
|
11
|
+
from .util import min_ex, sanitize_fn, spack, sunpack, yieldfile, zlib
|
13
12
|
|
14
13
|
def dostime2unix(buf ) :
|
15
14
|
t, d = sunpack(b"<HH", buf)
|
copyparty/tcpsrv.py
CHANGED
@@ -148,9 +148,15 @@ class TcpSrv(object):
|
|
148
148
|
if just_ll or self.args.ll:
|
149
149
|
ll_ok.add(ip.split("/")[0])
|
150
150
|
|
151
|
+
listening_on = []
|
152
|
+
for ip, ports in sorted(ok.items()):
|
153
|
+
for port in sorted(ports):
|
154
|
+
listening_on.append("%s %s" % (ip, port))
|
155
|
+
|
151
156
|
qr1 = {}
|
152
157
|
qr2 = {}
|
153
158
|
msgs = []
|
159
|
+
accessible_on = []
|
154
160
|
title_tab = {}
|
155
161
|
title_vars = [x[1:] for x in self.args.wintitle.split(" ") if x.startswith("$")]
|
156
162
|
t = "available @ {}://{}:{}/ (\033[33m{}\033[0m)"
|
@@ -166,6 +172,10 @@ class TcpSrv(object):
|
|
166
172
|
):
|
167
173
|
continue
|
168
174
|
|
175
|
+
zs = "%s %s" % (ip, port)
|
176
|
+
if zs not in accessible_on:
|
177
|
+
accessible_on.append(zs)
|
178
|
+
|
169
179
|
proto = " http"
|
170
180
|
if self.args.http_only:
|
171
181
|
pass
|
@@ -216,6 +226,14 @@ class TcpSrv(object):
|
|
216
226
|
else:
|
217
227
|
print("\n", end="")
|
218
228
|
|
229
|
+
for fn, ls in (
|
230
|
+
(self.args.wr_h_eps, listening_on),
|
231
|
+
(self.args.wr_h_aon, accessible_on),
|
232
|
+
):
|
233
|
+
if fn:
|
234
|
+
with open(fn, "wb") as f:
|
235
|
+
f.write(("\n".join(ls)).encode("utf-8"))
|
236
|
+
|
219
237
|
if self.args.qr or self.args.qrs:
|
220
238
|
self.qr = self._qr(qr1, qr2)
|
221
239
|
|
copyparty/tftpd.py
CHANGED
@@ -36,7 +36,19 @@ from partftpy.TftpShared import TftpException
|
|
36
36
|
from .__init__ import EXE, PY2, TYPE_CHECKING
|
37
37
|
from .authsrv import VFS
|
38
38
|
from .bos import bos
|
39
|
-
from .util import
|
39
|
+
from .util import (
|
40
|
+
FN_EMB,
|
41
|
+
UTC,
|
42
|
+
BytesIO,
|
43
|
+
Daemon,
|
44
|
+
ODict,
|
45
|
+
exclude_dotfiles,
|
46
|
+
min_ex,
|
47
|
+
runhook,
|
48
|
+
undot,
|
49
|
+
vjoin,
|
50
|
+
vsplit,
|
51
|
+
)
|
40
52
|
|
41
53
|
if TYPE_CHECKING:
|
42
54
|
from .svchub import SvcHub
|
@@ -241,16 +253,25 @@ class Tftpd(object):
|
|
241
253
|
for srv in srvs:
|
242
254
|
srv.stop()
|
243
255
|
|
244
|
-
def _v2a(
|
256
|
+
def _v2a(
|
257
|
+
self, caller , vpath , perms , *a
|
258
|
+
) :
|
245
259
|
vpath = vpath.replace("\\", "/").lstrip("/")
|
246
260
|
if not perms:
|
247
261
|
perms = [True, True]
|
248
262
|
|
249
263
|
debug('%s("%s", %s) %s\033[K\033[0m', caller, vpath, str(a), perms)
|
250
264
|
vfs, rem = self.asrv.vfs.get(vpath, "*", *perms)
|
265
|
+
if perms[1] and "*" not in vfs.axs.uread and "wo_up_readme" not in vfs.flags:
|
266
|
+
zs, fn = vsplit(vpath)
|
267
|
+
if fn.lower() in FN_EMB:
|
268
|
+
vpath = vjoin(zs, "_wo_" + fn)
|
269
|
+
vfs, rem = self.asrv.vfs.get(vpath, "*", *perms)
|
270
|
+
|
251
271
|
if not vfs.realpath:
|
252
272
|
raise Exception("unmapped vfs")
|
253
|
-
|
273
|
+
|
274
|
+
return vfs, vpath, vfs.canonical(rem)
|
254
275
|
|
255
276
|
def _ls(self, vpath , raddress , rport , force=False) :
|
256
277
|
# generate file listing if vpath is dir.txt and return as file object
|
@@ -328,7 +349,7 @@ class Tftpd(object):
|
|
328
349
|
else:
|
329
350
|
raise Exception("bad mode %s" % (mode,))
|
330
351
|
|
331
|
-
vfs, ap = self._v2a("open", vpath, [rd, wr])
|
352
|
+
vfs, vpath, ap = self._v2a("open", vpath, [rd, wr])
|
332
353
|
if wr:
|
333
354
|
if "*" not in vfs.axs.uwrite:
|
334
355
|
yeet("blocked write; folder not world-writable: /%s" % (vpath,))
|
@@ -365,7 +386,7 @@ class Tftpd(object):
|
|
365
386
|
return open(ap, mode, *a, **ka)
|
366
387
|
|
367
388
|
def _mkdir(self, vpath , *a) :
|
368
|
-
vfs, ap = self._v2a("mkdir", vpath, [])
|
389
|
+
vfs, _, ap = self._v2a("mkdir", vpath, [False, True])
|
369
390
|
if "*" not in vfs.axs.uwrite:
|
370
391
|
yeet("blocked mkdir; folder not world-writable: /%s" % (vpath,))
|
371
392
|
|
@@ -373,7 +394,7 @@ class Tftpd(object):
|
|
373
394
|
|
374
395
|
def _unlink(self, vpath ) :
|
375
396
|
# return bos.unlink(self._v2a("stat", vpath, *a)[1])
|
376
|
-
vfs, ap = self._v2a("delete", vpath, [True, False, False, True])
|
397
|
+
vfs, _, ap = self._v2a("delete", vpath, [True, False, False, True])
|
377
398
|
|
378
399
|
try:
|
379
400
|
inf = bos.stat(ap)
|
@@ -397,7 +418,7 @@ class Tftpd(object):
|
|
397
418
|
|
398
419
|
def _p_exists(self, vpath ) :
|
399
420
|
try:
|
400
|
-
ap = self._v2a("p.exists", vpath, [False, False])[
|
421
|
+
ap = self._v2a("p.exists", vpath, [False, False])[2]
|
401
422
|
bos.stat(ap)
|
402
423
|
return True
|
403
424
|
except:
|
@@ -405,7 +426,7 @@ class Tftpd(object):
|
|
405
426
|
|
406
427
|
def _p_isdir(self, vpath ) :
|
407
428
|
try:
|
408
|
-
st = bos.stat(self._v2a("p.isdir", vpath, [False, False])[
|
429
|
+
st = bos.stat(self._v2a("p.isdir", vpath, [False, False])[2])
|
409
430
|
ret = stat.S_ISDIR(st.st_mode)
|
410
431
|
return ret
|
411
432
|
except:
|
copyparty/up2k.py
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
4
|
import errno
|
5
|
-
import gzip
|
6
5
|
import hashlib
|
7
6
|
import json
|
8
7
|
import math
|
@@ -42,6 +41,7 @@ from .util import (
|
|
42
41
|
fsenc,
|
43
42
|
gen_filekey,
|
44
43
|
gen_filekey_dbg,
|
44
|
+
gzip,
|
45
45
|
hidedir,
|
46
46
|
humansize,
|
47
47
|
min_ex,
|
@@ -1112,7 +1112,7 @@ class Up2k(object):
|
|
1112
1112
|
ft = "\033[0;32m{}{:.0}"
|
1113
1113
|
ff = "\033[0;35m{}{:.0}"
|
1114
1114
|
fv = "\033[0;36m{}:\033[90m{}"
|
1115
|
-
zs = "ext_th_d html_head mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot"
|
1115
|
+
zs = "ext_th_d html_head mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
|
1116
1116
|
fx = set(zs.split())
|
1117
1117
|
fd = vf_bmap()
|
1118
1118
|
fd.update(vf_cmap())
|
@@ -2903,7 +2903,6 @@ class Up2k(object):
|
|
2903
2903
|
if ptop not in self.registry:
|
2904
2904
|
raise Pebkac(410, "location unavailable")
|
2905
2905
|
|
2906
|
-
cj["name"] = sanitize_fn(cj["name"], "")
|
2907
2906
|
cj["poke"] = now = self.db_act = self.vol_act[ptop] = time.time()
|
2908
2907
|
wark = dwark = self._get_wark(cj)
|
2909
2908
|
job = None
|
@@ -3220,6 +3219,7 @@ class Up2k(object):
|
|
3220
3219
|
job["ptop"] = vfs.realpath
|
3221
3220
|
job["vtop"] = vfs.vpath
|
3222
3221
|
job["prel"] = rem
|
3222
|
+
job["name"] = sanitize_fn(job["name"], "")
|
3223
3223
|
if zvfs.vpath != vfs.vpath:
|
3224
3224
|
# print(json.dumps(job, sort_keys=True, indent=4))
|
3225
3225
|
job["hash"] = cj["hash"]
|
@@ -3410,6 +3410,7 @@ class Up2k(object):
|
|
3410
3410
|
rm = False,
|
3411
3411
|
lmod = 0,
|
3412
3412
|
fsrc = None,
|
3413
|
+
is_mv = False,
|
3413
3414
|
) :
|
3414
3415
|
if src == dst or (fsrc and fsrc == dst):
|
3415
3416
|
t = "symlinking a file to itself?? orig(%s) fsrc(%s) link(%s)"
|
@@ -3426,7 +3427,7 @@ class Up2k(object):
|
|
3426
3427
|
|
3427
3428
|
linked = False
|
3428
3429
|
try:
|
3429
|
-
if not flags.get("dedup"):
|
3430
|
+
if not is_mv and not flags.get("dedup"):
|
3430
3431
|
raise Exception("dedup is disabled in config")
|
3431
3432
|
|
3432
3433
|
lsrc = src
|
@@ -3691,8 +3692,9 @@ class Up2k(object):
|
|
3691
3692
|
if self.idx_wark(vflags, *z2):
|
3692
3693
|
del self.registry[ptop][wark]
|
3693
3694
|
else:
|
3694
|
-
for k in "host tnam busy sprs poke
|
3695
|
+
for k in "host tnam busy sprs poke".split():
|
3695
3696
|
del job[k]
|
3697
|
+
job.pop("t0c", None)
|
3696
3698
|
job["t0"] = int(job["t0"])
|
3697
3699
|
job["hash"] = []
|
3698
3700
|
job["done"] = 1
|
@@ -4578,7 +4580,7 @@ class Up2k(object):
|
|
4578
4580
|
dlink = bos.readlink(sabs)
|
4579
4581
|
dlink = os.path.join(os.path.dirname(sabs), dlink)
|
4580
4582
|
dlink = bos.path.abspath(dlink)
|
4581
|
-
self._symlink(dlink, dabs, dvn.flags, lmod=ftime)
|
4583
|
+
self._symlink(dlink, dabs, dvn.flags, lmod=ftime, is_mv=True)
|
4582
4584
|
wunlink(self.log, sabs, svn.flags)
|
4583
4585
|
else:
|
4584
4586
|
atomic_move(self.log, sabs, dabs, svn.flags)
|
@@ -4796,7 +4798,7 @@ class Up2k(object):
|
|
4796
4798
|
flags = self.flags.get(ptop) or {}
|
4797
4799
|
atomic_move(self.log, sabs, slabs, flags)
|
4798
4800
|
bos.utime(slabs, (int(time.time()), int(mt)), False)
|
4799
|
-
self._symlink(slabs, sabs, flags, False)
|
4801
|
+
self._symlink(slabs, sabs, flags, False, is_mv=True)
|
4800
4802
|
full[slabs] = (ptop, rem)
|
4801
4803
|
sabs = slabs
|
4802
4804
|
|
@@ -4855,7 +4857,9 @@ class Up2k(object):
|
|
4855
4857
|
# (for example a volume with symlinked dupes but no --dedup);
|
4856
4858
|
# fsrc=sabs is then a source that currently resolves to copy
|
4857
4859
|
|
4858
|
-
self._symlink(
|
4860
|
+
self._symlink(
|
4861
|
+
dabs, alink, flags, False, lmod=lmod or 0, fsrc=sabs, is_mv=True
|
4862
|
+
)
|
4859
4863
|
|
4860
4864
|
return len(full) + len(links)
|
4861
4865
|
|
@@ -4969,6 +4973,7 @@ class Up2k(object):
|
|
4969
4973
|
job["ptop"] = vfs.realpath
|
4970
4974
|
job["vtop"] = vfs.vpath
|
4971
4975
|
job["prel"] = rem
|
4976
|
+
job["name"] = sanitize_fn(job["name"], "")
|
4972
4977
|
if zvfs.vpath != vfs.vpath:
|
4973
4978
|
self.log("xbu reloc2:%d..." % (depth,), 6)
|
4974
4979
|
return self._handle_json(job, depth + 1)
|