copyparty 1.13.7__py3-none-any.whl → 1.14.0__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 +58 -2
- copyparty/__version__.py +3 -3
- copyparty/authsrv.py +224 -11
- copyparty/httpcli.py +186 -26
- copyparty/httpsrv.py +15 -2
- copyparty/svchub.py +77 -5
- copyparty/tcpsrv.py +22 -1
- copyparty/u2idx.py +19 -3
- copyparty/up2k.py +67 -13
- copyparty/util.py +1 -1
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.html +4 -4
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/browser2.html +2 -2
- copyparty/web/md.html +2 -2
- copyparty/web/shares.css.gz +0 -0
- copyparty/web/shares.html +74 -0
- copyparty/web/shares.js.gz +0 -0
- copyparty/web/splash.css.gz +0 -0
- copyparty/web/splash.html +13 -6
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.13.7.dist-info → copyparty-1.14.0.dist-info}/METADATA +55 -3
- {copyparty-1.13.7.dist-info → copyparty-1.14.0.dist-info}/RECORD +28 -25
- {copyparty-1.13.7.dist-info → copyparty-1.14.0.dist-info}/WHEEL +1 -1
- {copyparty-1.13.7.dist-info → copyparty-1.14.0.dist-info}/LICENSE +0 -0
- {copyparty-1.13.7.dist-info → copyparty-1.14.0.dist-info}/entry_points.txt +0 -0
- {copyparty-1.13.7.dist-info → copyparty-1.14.0.dist-info}/top_level.txt +0 -0
copyparty/httpcli.py
CHANGED
@@ -45,6 +45,7 @@ from .util import unquote # type: ignore
|
|
45
45
|
from .util import (
|
46
46
|
APPLESAN_RE,
|
47
47
|
BITNESS,
|
48
|
+
HAVE_SQLITE3,
|
48
49
|
HTTPCODE,
|
49
50
|
META_NOBOTS,
|
50
51
|
UTC,
|
@@ -108,6 +109,9 @@ from .util import (
|
|
108
109
|
if TYPE_CHECKING:
|
109
110
|
from .httpconn import HttpConn
|
110
111
|
|
112
|
+
if not hasattr(socket, "AF_UNIX"):
|
113
|
+
setattr(socket, "AF_UNIX", -9001)
|
114
|
+
|
111
115
|
_ = (argparse, threading)
|
112
116
|
|
113
117
|
NO_CACHE = {"Cache-Control": "no-cache"}
|
@@ -447,7 +451,7 @@ class HttpCli(object):
|
|
447
451
|
t = "incorrect --rp-loc or webserver config; expected vpath starting with [{}] but got [{}]"
|
448
452
|
self.log(t.format(self.args.R, vpath), 1)
|
449
453
|
|
450
|
-
self.ouparam =
|
454
|
+
self.ouparam = uparam.copy()
|
451
455
|
|
452
456
|
if self.args.rsp_slp:
|
453
457
|
time.sleep(self.args.rsp_slp)
|
@@ -456,6 +460,9 @@ class HttpCli(object):
|
|
456
460
|
|
457
461
|
zso = self.headers.get("cookie")
|
458
462
|
if zso:
|
463
|
+
if len(zso) > 8192:
|
464
|
+
self.loud_reply("cookie header too big", status=400)
|
465
|
+
return False
|
459
466
|
zsll = [x.split("=", 1) for x in zso.split(";") if "=" in x]
|
460
467
|
cookies = {k.strip(): unescape_cookie(zs) for k, zs in zsll}
|
461
468
|
cookie_pw = cookies.get("cppws") or cookies.get("cppwd") or ""
|
@@ -961,10 +968,10 @@ class HttpCli(object):
|
|
961
968
|
status = 200,
|
962
969
|
use302 = False,
|
963
970
|
) :
|
964
|
-
vp = self.args.
|
971
|
+
vp = self.args.SRS + vpath
|
965
972
|
html = self.j2s(
|
966
973
|
"msg",
|
967
|
-
h2='<a href="
|
974
|
+
h2='<a href="{}">{} {}</a>'.format(
|
968
975
|
quotep(vp) + suf, flavor, html_escape(vp, crlf=True) + suf
|
969
976
|
),
|
970
977
|
pre=msg,
|
@@ -972,7 +979,7 @@ class HttpCli(object):
|
|
972
979
|
).encode("utf-8", "replace")
|
973
980
|
|
974
981
|
if use302:
|
975
|
-
self.reply(html, status=302, headers={"Location":
|
982
|
+
self.reply(html, status=302, headers={"Location": vp})
|
976
983
|
else:
|
977
984
|
self.reply(html, status=status)
|
978
985
|
|
@@ -1134,7 +1141,7 @@ class HttpCli(object):
|
|
1134
1141
|
if "move" in self.uparam:
|
1135
1142
|
return self.handle_mv()
|
1136
1143
|
|
1137
|
-
if not self.vpath:
|
1144
|
+
if not self.vpath and self.ouparam:
|
1138
1145
|
if "reload" in self.uparam:
|
1139
1146
|
return self.handle_reload()
|
1140
1147
|
|
@@ -1156,23 +1163,12 @@ class HttpCli(object):
|
|
1156
1163
|
if "hc" in self.uparam:
|
1157
1164
|
return self.tx_svcs()
|
1158
1165
|
|
1166
|
+
if "shares" in self.uparam:
|
1167
|
+
return self.tx_shares()
|
1168
|
+
|
1159
1169
|
if "h" in self.uparam:
|
1160
1170
|
return self.tx_mounts()
|
1161
1171
|
|
1162
|
-
# conditional redirect to single volumes
|
1163
|
-
if not self.vpath and not self.ouparam:
|
1164
|
-
nread = len(self.rvol)
|
1165
|
-
nwrite = len(self.wvol)
|
1166
|
-
if nread + nwrite == 1 or (self.rvol == self.wvol and nread == 1):
|
1167
|
-
if nread == 1:
|
1168
|
-
vpath = self.rvol[0]
|
1169
|
-
else:
|
1170
|
-
vpath = self.wvol[0]
|
1171
|
-
|
1172
|
-
if self.vpath != vpath:
|
1173
|
-
self.redirect(vpath, flavor="redirecting to", use302=True)
|
1174
|
-
return True
|
1175
|
-
|
1176
1172
|
return self.tx_browser()
|
1177
1173
|
|
1178
1174
|
def handle_propfind(self) :
|
@@ -1611,6 +1607,9 @@ class HttpCli(object):
|
|
1611
1607
|
if "delete" in self.uparam:
|
1612
1608
|
return self.handle_rm([])
|
1613
1609
|
|
1610
|
+
if "unshare" in self.uparam:
|
1611
|
+
return self.handle_unshare()
|
1612
|
+
|
1614
1613
|
if "application/octet-stream" in ctype:
|
1615
1614
|
return self.handle_post_binary()
|
1616
1615
|
|
@@ -2082,6 +2081,9 @@ class HttpCli(object):
|
|
2082
2081
|
if act == "zip":
|
2083
2082
|
return self.handle_zip_post()
|
2084
2083
|
|
2084
|
+
if act == "chpw":
|
2085
|
+
return self.handle_chpw()
|
2086
|
+
|
2085
2087
|
raise Pebkac(422, 'invalid action "{}"'.format(act))
|
2086
2088
|
|
2087
2089
|
def handle_zip_post(self) :
|
@@ -2140,6 +2142,9 @@ class HttpCli(object):
|
|
2140
2142
|
if "srch" in self.uparam or "srch" in body:
|
2141
2143
|
return self.handle_search(body)
|
2142
2144
|
|
2145
|
+
if "share" in self.uparam:
|
2146
|
+
return self.handle_share(body)
|
2147
|
+
|
2143
2148
|
if "delete" in self.uparam:
|
2144
2149
|
return self.handle_rm(body)
|
2145
2150
|
|
@@ -2196,7 +2201,9 @@ class HttpCli(object):
|
|
2196
2201
|
def handle_search(self, body ) :
|
2197
2202
|
idx = self.conn.get_u2idx()
|
2198
2203
|
if not idx or not hasattr(idx, "p_end"):
|
2199
|
-
|
2204
|
+
if not HAVE_SQLITE3:
|
2205
|
+
raise Pebkac(500, "sqlite3 not found on server; search is disabled")
|
2206
|
+
raise Pebkac(500, "server busy, cannot search; please retry in a bit")
|
2200
2207
|
|
2201
2208
|
vols = []
|
2202
2209
|
seen = {}
|
@@ -2386,6 +2393,22 @@ class HttpCli(object):
|
|
2386
2393
|
self.reply(b"thank")
|
2387
2394
|
return True
|
2388
2395
|
|
2396
|
+
def handle_chpw(self) :
|
2397
|
+
assert self.parser
|
2398
|
+
pwd = self.parser.require("pw", 64)
|
2399
|
+
self.parser.drop()
|
2400
|
+
|
2401
|
+
ok, msg = self.asrv.chpw(self.conn.hsrv.broker, self.uname, pwd)
|
2402
|
+
if ok:
|
2403
|
+
ok, msg = self.get_pwd_cookie(pwd)
|
2404
|
+
if ok:
|
2405
|
+
msg = "new password OK"
|
2406
|
+
|
2407
|
+
redir = (self.args.SRS + "?h") if ok else ""
|
2408
|
+
html = self.j2s("msg", h1=msg, h2='<a href="/?h">ack</a>', redir=redir)
|
2409
|
+
self.reply(html.encode("utf-8"))
|
2410
|
+
return True
|
2411
|
+
|
2389
2412
|
def handle_login(self) :
|
2390
2413
|
assert self.parser
|
2391
2414
|
pwd = self.parser.require("cppwd", 64)
|
@@ -2410,12 +2433,12 @@ class HttpCli(object):
|
|
2410
2433
|
dst += "&" if "?" in dst else "?"
|
2411
2434
|
dst += "_=1#" + html_escape(uhash, True, True)
|
2412
2435
|
|
2413
|
-
msg = self.get_pwd_cookie(pwd)
|
2436
|
+
_, msg = self.get_pwd_cookie(pwd)
|
2414
2437
|
html = self.j2s("msg", h1=msg, h2='<a href="' + dst + '">ack</a>', redir=dst)
|
2415
2438
|
self.reply(html.encode("utf-8"))
|
2416
2439
|
return True
|
2417
2440
|
|
2418
|
-
def get_pwd_cookie(self, pwd )
|
2441
|
+
def get_pwd_cookie(self, pwd ) :
|
2419
2442
|
hpwd = self.asrv.ah.hash(pwd)
|
2420
2443
|
uname = self.asrv.iacct.get(hpwd)
|
2421
2444
|
if uname:
|
@@ -2447,7 +2470,7 @@ class HttpCli(object):
|
|
2447
2470
|
ck = gencookie(k, pwd, self.args.R, self.is_https, dur, "; HttpOnly")
|
2448
2471
|
self.out_headerlist.append(("Set-Cookie", ck))
|
2449
2472
|
|
2450
|
-
return msg
|
2473
|
+
return dur > 0, msg
|
2451
2474
|
|
2452
2475
|
def handle_mkdir(self) :
|
2453
2476
|
assert self.parser
|
@@ -2486,7 +2509,7 @@ class HttpCli(object):
|
|
2486
2509
|
except:
|
2487
2510
|
raise Pebkac(500, min_ex())
|
2488
2511
|
|
2489
|
-
self.out_headers["X-New-Dir"] = quotep(vpath)
|
2512
|
+
self.out_headers["X-New-Dir"] = quotep(self.args.RS + vpath)
|
2490
2513
|
|
2491
2514
|
if dav:
|
2492
2515
|
self.reply(b"", 201)
|
@@ -3941,6 +3964,7 @@ class HttpCli(object):
|
|
3941
3964
|
k304=self.k304(),
|
3942
3965
|
k304vis=self.args.k304 > 0,
|
3943
3966
|
ver=S_VERSION if self.args.ver else "",
|
3967
|
+
chpw=self.args.chpw and self.uname != "*",
|
3944
3968
|
ahttps="" if self.is_https else "https://" + self.host + self.req,
|
3945
3969
|
)
|
3946
3970
|
self.reply(html.encode("utf-8"))
|
@@ -4075,7 +4099,9 @@ class HttpCli(object):
|
|
4075
4099
|
dst = dst[len(top) + 1 :]
|
4076
4100
|
|
4077
4101
|
ret = self.gen_tree(top, dst, self.uparam.get("k", ""))
|
4078
|
-
if self.is_vproxied:
|
4102
|
+
if self.is_vproxied and not self.uparam["tree"]:
|
4103
|
+
# uparam is '' on initial load, which is
|
4104
|
+
# the only time we gotta fill in the blanks
|
4079
4105
|
parents = self.args.R.split("/")
|
4080
4106
|
for parent in reversed(parents):
|
4081
4107
|
ret = {"k%s" % (parent,): ret, "a": []}
|
@@ -4150,7 +4176,9 @@ class HttpCli(object):
|
|
4150
4176
|
def tx_ups(self) :
|
4151
4177
|
idx = self.conn.get_u2idx()
|
4152
4178
|
if not idx or not hasattr(idx, "p_end"):
|
4153
|
-
|
4179
|
+
if not HAVE_SQLITE3:
|
4180
|
+
raise Pebkac(500, "sqlite3 not found on server; unpost is disabled")
|
4181
|
+
raise Pebkac(500, "server busy, cannot unpost; please retry in a bit")
|
4154
4182
|
|
4155
4183
|
filt = self.uparam.get("filter") or ""
|
4156
4184
|
lm = "ups [{}]".format(filt)
|
@@ -4239,6 +4267,137 @@ class HttpCli(object):
|
|
4239
4267
|
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
4240
4268
|
return True
|
4241
4269
|
|
4270
|
+
def tx_shares(self) :
|
4271
|
+
if self.uname == "*":
|
4272
|
+
self.loud_reply("you're not logged in")
|
4273
|
+
return True
|
4274
|
+
|
4275
|
+
idx = self.conn.get_u2idx()
|
4276
|
+
if not idx or not hasattr(idx, "p_end"):
|
4277
|
+
if not HAVE_SQLITE3:
|
4278
|
+
raise Pebkac(500, "sqlite3 not found on server; sharing is disabled")
|
4279
|
+
raise Pebkac(500, "server busy, cannot list shares; please retry in a bit")
|
4280
|
+
|
4281
|
+
cur = idx.get_shr()
|
4282
|
+
if not cur:
|
4283
|
+
raise Pebkac(400, "huh, sharing must be disabled in the server config...")
|
4284
|
+
|
4285
|
+
rows = cur.execute("select * from sh").fetchall()
|
4286
|
+
rows = [list(x) for x in rows]
|
4287
|
+
|
4288
|
+
if self.uname != self.args.shr_adm:
|
4289
|
+
rows = [x for x in rows if x[5] == self.uname]
|
4290
|
+
|
4291
|
+
for x in rows:
|
4292
|
+
x[1] = "yes" if x[1] else ""
|
4293
|
+
|
4294
|
+
html = self.j2s(
|
4295
|
+
"shares", this=self, shr=self.args.shr, rows=rows, now=int(time.time())
|
4296
|
+
)
|
4297
|
+
self.reply(html.encode("utf-8"), status=200)
|
4298
|
+
return True
|
4299
|
+
|
4300
|
+
def handle_unshare(self) :
|
4301
|
+
idx = self.conn.get_u2idx()
|
4302
|
+
if not idx or not hasattr(idx, "p_end"):
|
4303
|
+
if not HAVE_SQLITE3:
|
4304
|
+
raise Pebkac(500, "sqlite3 not found on server; sharing is disabled")
|
4305
|
+
raise Pebkac(500, "server busy, cannot create share; please retry in a bit")
|
4306
|
+
|
4307
|
+
if self.args.shr_v:
|
4308
|
+
self.log("handle_unshare: " + self.req)
|
4309
|
+
|
4310
|
+
cur = idx.get_shr()
|
4311
|
+
if not cur:
|
4312
|
+
raise Pebkac(400, "huh, sharing must be disabled in the server config...")
|
4313
|
+
|
4314
|
+
skey = self.vpath.split("/")[-1]
|
4315
|
+
|
4316
|
+
uns = cur.execute("select un from sh where k = ?", (skey,)).fetchall()
|
4317
|
+
un = uns[0][0] if uns and uns[0] else ""
|
4318
|
+
|
4319
|
+
if not un:
|
4320
|
+
raise Pebkac(400, "that sharekey didn't match anything")
|
4321
|
+
|
4322
|
+
if un != self.uname and self.uname != self.args.shr_adm:
|
4323
|
+
t = "your username (%r) does not match the sharekey's owner (%r) and you're not admin"
|
4324
|
+
raise Pebkac(400, t % (self.uname, un))
|
4325
|
+
|
4326
|
+
cur.execute("delete from sh where k = ?", (skey,))
|
4327
|
+
cur.connection.commit()
|
4328
|
+
|
4329
|
+
self.redirect(self.args.SRS + "?shares")
|
4330
|
+
return True
|
4331
|
+
|
4332
|
+
def handle_share(self, req ) :
|
4333
|
+
idx = self.conn.get_u2idx()
|
4334
|
+
if not idx or not hasattr(idx, "p_end"):
|
4335
|
+
if not HAVE_SQLITE3:
|
4336
|
+
raise Pebkac(500, "sqlite3 not found on server; sharing is disabled")
|
4337
|
+
raise Pebkac(500, "server busy, cannot create share; please retry in a bit")
|
4338
|
+
|
4339
|
+
if self.args.shr_v:
|
4340
|
+
self.log("handle_share: " + json.dumps(req, indent=4))
|
4341
|
+
|
4342
|
+
skey = req["k"]
|
4343
|
+
vp = req["vp"].strip("/")
|
4344
|
+
if self.is_vproxied and (vp == self.args.R or vp.startswith(self.args.RS)):
|
4345
|
+
vp = vp[len(self.args.RS) :]
|
4346
|
+
|
4347
|
+
m = re.search(r"([^0-9a-zA-Z_\.-]|\.\.|^\.)", skey)
|
4348
|
+
if m:
|
4349
|
+
raise Pebkac(400, "sharekey has illegal character [%s]" % (m[1],))
|
4350
|
+
|
4351
|
+
if vp.startswith(self.args.shr[1:]):
|
4352
|
+
raise Pebkac(400, "yo dawg...")
|
4353
|
+
|
4354
|
+
cur = idx.get_shr()
|
4355
|
+
if not cur:
|
4356
|
+
raise Pebkac(400, "huh, sharing must be disabled in the server config...")
|
4357
|
+
|
4358
|
+
q = "select * from sh where k = ?"
|
4359
|
+
qr = cur.execute(q, (skey,)).fetchall()
|
4360
|
+
if qr and qr[0]:
|
4361
|
+
self.log("sharekey taken by %r" % (qr,))
|
4362
|
+
raise Pebkac(400, "sharekey [%s] is already in use" % (skey,))
|
4363
|
+
|
4364
|
+
# ensure user has requested perms
|
4365
|
+
s_rd = "read" in req["perms"]
|
4366
|
+
s_wr = "write" in req["perms"]
|
4367
|
+
s_mv = "move" in req["perms"]
|
4368
|
+
s_del = "delete" in req["perms"]
|
4369
|
+
try:
|
4370
|
+
vfs, rem = self.asrv.vfs.get(vp, self.uname, s_rd, s_wr, s_mv, s_del)
|
4371
|
+
except:
|
4372
|
+
raise Pebkac(400, "you dont have all the perms you tried to grant")
|
4373
|
+
|
4374
|
+
ap = vfs.canonical(rem)
|
4375
|
+
st = bos.stat(ap)
|
4376
|
+
ist = 2 if stat.S_ISDIR(st.st_mode) else 1
|
4377
|
+
|
4378
|
+
pw = req.get("pw") or ""
|
4379
|
+
now = int(time.time())
|
4380
|
+
sexp = req["exp"]
|
4381
|
+
exp = now + int(sexp) * 60 if sexp else 0
|
4382
|
+
pr = "".join(zc for zc, zb in zip("rwmd", (s_rd, s_wr, s_mv, s_del)) if zb)
|
4383
|
+
|
4384
|
+
q = "insert into sh values (?,?,?,?,?,?,?,?)"
|
4385
|
+
cur.execute(q, (skey, pw, vp, pr, ist, self.uname, now, exp))
|
4386
|
+
cur.connection.commit()
|
4387
|
+
|
4388
|
+
self.conn.hsrv.broker.ask("_reload_blocking", False, False).get()
|
4389
|
+
self.conn.hsrv.broker.ask("up2k.wake_rescanner").get()
|
4390
|
+
|
4391
|
+
surl = "%s://%s%s%s%s" % (
|
4392
|
+
"https" if self.is_https else "http",
|
4393
|
+
self.host,
|
4394
|
+
self.args.SR,
|
4395
|
+
self.args.shr,
|
4396
|
+
skey,
|
4397
|
+
)
|
4398
|
+
self.loud_reply(surl, status=201)
|
4399
|
+
return True
|
4400
|
+
|
4242
4401
|
def handle_rm(self, req ) :
|
4243
4402
|
if not req and not self.can_delete:
|
4244
4403
|
raise Pebkac(403, "not allowed for user " + self.uname)
|
@@ -4637,6 +4796,7 @@ class HttpCli(object):
|
|
4637
4796
|
"have_mv": (not self.args.no_mv),
|
4638
4797
|
"have_del": (not self.args.no_del),
|
4639
4798
|
"have_zip": (not self.args.no_zip),
|
4799
|
+
"have_shr": self.args.shr,
|
4640
4800
|
"have_unpost": int(self.args.unpost),
|
4641
4801
|
"sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
|
4642
4802
|
"dgrid": "grid" in vf,
|
copyparty/httpsrv.py
CHANGED
@@ -84,6 +84,9 @@ if TYPE_CHECKING:
|
|
84
84
|
if PY2:
|
85
85
|
range = xrange # type: ignore
|
86
86
|
|
87
|
+
if not hasattr(socket, "AF_UNIX"):
|
88
|
+
setattr(socket, "AF_UNIX", -9001)
|
89
|
+
|
87
90
|
|
88
91
|
class HttpSrv(object):
|
89
92
|
"""
|
@@ -148,7 +151,17 @@ class HttpSrv(object):
|
|
148
151
|
|
149
152
|
env = jinja2.Environment()
|
150
153
|
env.loader = jinja2.FileSystemLoader(os.path.join(self.E.mod, "web"))
|
151
|
-
jn = [
|
154
|
+
jn = [
|
155
|
+
"splash",
|
156
|
+
"shares",
|
157
|
+
"svcs",
|
158
|
+
"browser",
|
159
|
+
"browser2",
|
160
|
+
"msg",
|
161
|
+
"md",
|
162
|
+
"mde",
|
163
|
+
"cf",
|
164
|
+
]
|
152
165
|
self.j2 = {x: env.get_template(x + ".html") for x in jn}
|
153
166
|
zs = os.path.join(self.E.mod, "web", "deps", "prism.js.gz")
|
154
167
|
self.prism = os.path.exists(zs)
|
@@ -359,7 +372,7 @@ class HttpSrv(object):
|
|
359
372
|
cip = cip[7:]
|
360
373
|
addr = (cip, saddr[1])
|
361
374
|
else:
|
362
|
-
addr = (
|
375
|
+
addr = ("127.8.3.7", sck.fileno())
|
363
376
|
except (OSError, socket.error) as ex:
|
364
377
|
if self.stopping:
|
365
378
|
break
|
copyparty/svchub.py
CHANGED
@@ -202,6 +202,20 @@ class SvcHub(object):
|
|
202
202
|
t = "WARNING: --s-rd-sz (%d) is larger than --iobuf (%d); this may lead to reduced performance"
|
203
203
|
self.log("root", t % (args.s_rd_sz, args.iobuf), 3)
|
204
204
|
|
205
|
+
if args.chpw and args.idp_h_usr:
|
206
|
+
t = "ERROR: user-changeable passwords is incompatible with IdP/identity-providers; you must disable either --chpw or --idp-h-usr"
|
207
|
+
self.log("root", t, 1)
|
208
|
+
raise Exception(t)
|
209
|
+
|
210
|
+
noch = set()
|
211
|
+
for zs in args.chpw_no or []:
|
212
|
+
zsl = [x.strip() for x in zs.split(",")]
|
213
|
+
noch.update([x for x in zsl if x])
|
214
|
+
args.chpw_no = noch
|
215
|
+
|
216
|
+
if args.shr:
|
217
|
+
self.setup_share_db()
|
218
|
+
|
205
219
|
bri = "zy"[args.theme % 2 :][:1]
|
206
220
|
ch = "abcdefghijklmnopqrstuvwx"[int(args.theme / 2)]
|
207
221
|
args.theme = "{0}{1} {0} {1}".format(ch, bri)
|
@@ -347,6 +361,61 @@ class SvcHub(object):
|
|
347
361
|
|
348
362
|
self.broker = Broker(self)
|
349
363
|
|
364
|
+
def setup_share_db(self) :
|
365
|
+
al = self.args
|
366
|
+
if not HAVE_SQLITE3:
|
367
|
+
self.log("root", "sqlite3 not available; disabling --shr", 1)
|
368
|
+
al.shr = ""
|
369
|
+
return
|
370
|
+
|
371
|
+
import sqlite3
|
372
|
+
|
373
|
+
al.shr = "/%s/" % (al.shr.strip("/"))
|
374
|
+
|
375
|
+
create = True
|
376
|
+
db_path = self.args.shr_db
|
377
|
+
self.log("root", "initializing shares-db %s" % (db_path,))
|
378
|
+
for n in range(2):
|
379
|
+
try:
|
380
|
+
db = sqlite3.connect(db_path)
|
381
|
+
cur = db.cursor()
|
382
|
+
try:
|
383
|
+
cur.execute("select count(*) from sh").fetchone()
|
384
|
+
create = False
|
385
|
+
break
|
386
|
+
except:
|
387
|
+
pass
|
388
|
+
except Exception as ex:
|
389
|
+
if n:
|
390
|
+
raise
|
391
|
+
t = "shares-db corrupt; deleting and recreating: %r"
|
392
|
+
self.log("root", t % (ex,), 3)
|
393
|
+
try:
|
394
|
+
cur.close() # type: ignore
|
395
|
+
except:
|
396
|
+
pass
|
397
|
+
try:
|
398
|
+
db.close() # type: ignore
|
399
|
+
except:
|
400
|
+
pass
|
401
|
+
os.unlink(db_path)
|
402
|
+
|
403
|
+
assert db # type: ignore
|
404
|
+
assert cur # type: ignore
|
405
|
+
if create:
|
406
|
+
for cmd in [
|
407
|
+
# sharekey, password, src, perms, type, owner, created, expires
|
408
|
+
r"create table sh (k text, pw text, vp text, pr text, st int, un text, t0 int, t1 int)",
|
409
|
+
r"create table kv (k text, v int)",
|
410
|
+
r"insert into kv values ('sver', {})".format(1),
|
411
|
+
]:
|
412
|
+
cur.execute(cmd)
|
413
|
+
db.commit()
|
414
|
+
self.log("root", "created new shares-db")
|
415
|
+
|
416
|
+
cur.close()
|
417
|
+
db.close()
|
418
|
+
|
350
419
|
def start_ftpd(self) :
|
351
420
|
time.sleep(30)
|
352
421
|
|
@@ -809,18 +878,21 @@ class SvcHub(object):
|
|
809
878
|
Daemon(self._reload, "reloading")
|
810
879
|
return "reload initiated"
|
811
880
|
|
812
|
-
def _reload(self, rescan_all_vols = True) :
|
881
|
+
def _reload(self, rescan_all_vols = True, up2k = True) :
|
813
882
|
with self.up2k.mutex:
|
814
883
|
if self.reloading != 1:
|
815
884
|
return
|
816
885
|
self.reloading = 2
|
817
886
|
self.log("root", "reloading config")
|
818
|
-
self.asrv.reload()
|
819
|
-
|
887
|
+
self.asrv.reload(9 if up2k else 4)
|
888
|
+
if up2k:
|
889
|
+
self.up2k.reload(rescan_all_vols)
|
890
|
+
else:
|
891
|
+
self.log("root", "reload done")
|
820
892
|
self.broker.reload()
|
821
893
|
self.reloading = 0
|
822
894
|
|
823
|
-
def _reload_blocking(self, rescan_all_vols = True) :
|
895
|
+
def _reload_blocking(self, rescan_all_vols = True, up2k = True) :
|
824
896
|
while True:
|
825
897
|
with self.up2k.mutex:
|
826
898
|
if self.reloading < 2:
|
@@ -831,7 +903,7 @@ class SvcHub(object):
|
|
831
903
|
# try to handle multiple pending IdP reloads at once:
|
832
904
|
time.sleep(0.2)
|
833
905
|
|
834
|
-
self._reload(rescan_all_vols=rescan_all_vols)
|
906
|
+
self._reload(rescan_all_vols=rescan_all_vols, up2k=up2k)
|
835
907
|
|
836
908
|
def stop_thr(self) :
|
837
909
|
while not self.stop_req:
|
copyparty/tcpsrv.py
CHANGED
@@ -28,6 +28,9 @@ from .util import (
|
|
28
28
|
if TYPE_CHECKING:
|
29
29
|
from .svchub import SvcHub
|
30
30
|
|
31
|
+
if not hasattr(socket, "AF_UNIX"):
|
32
|
+
setattr(socket, "AF_UNIX", -9001)
|
33
|
+
|
31
34
|
if not hasattr(socket, "IPPROTO_IPV6"):
|
32
35
|
setattr(socket, "IPPROTO_IPV6", 41)
|
33
36
|
|
@@ -220,10 +223,22 @@ class TcpSrv(object):
|
|
220
223
|
self.log("tcpsrv", msg, c)
|
221
224
|
|
222
225
|
def _listen(self, ip , port ) :
|
226
|
+
uds_perm = uds_gid = -1
|
223
227
|
if "unix:" in ip:
|
224
228
|
tcp = False
|
225
229
|
ipv = socket.AF_UNIX
|
226
|
-
|
230
|
+
uds = ip.split(":")
|
231
|
+
ip = uds[-1]
|
232
|
+
if len(uds) > 2:
|
233
|
+
uds_perm = int(uds[1], 8)
|
234
|
+
if len(uds) > 3:
|
235
|
+
try:
|
236
|
+
uds_gid = int(uds[2])
|
237
|
+
except:
|
238
|
+
import grp
|
239
|
+
|
240
|
+
uds_gid = grp.getgrnam(uds[2]).gr_gid
|
241
|
+
|
227
242
|
elif ":" in ip:
|
228
243
|
tcp = True
|
229
244
|
ipv = socket.AF_INET6
|
@@ -259,7 +274,13 @@ class TcpSrv(object):
|
|
259
274
|
srv.bind(ip)
|
260
275
|
else:
|
261
276
|
tf = "%s.%d" % (ip, os.getpid())
|
277
|
+
if os.path.exists(tf):
|
278
|
+
os.unlink(tf)
|
262
279
|
srv.bind(tf)
|
280
|
+
if uds_gid != -1:
|
281
|
+
os.chown(tf, -1, uds_gid)
|
282
|
+
if uds_perm != -1:
|
283
|
+
os.chmod(tf, uds_perm)
|
263
284
|
atomic_move(self.nlog, tf, ip, VF_CAREFUL)
|
264
285
|
|
265
286
|
sport = srv.getsockname()[1] if tcp else port
|
copyparty/u2idx.py
CHANGED
@@ -56,6 +56,8 @@ class U2idx(object):
|
|
56
56
|
self.mem_cur = sqlite3.connect(":memory:", check_same_thread=False).cursor()
|
57
57
|
self.mem_cur.execute(r"create table a (b text)")
|
58
58
|
|
59
|
+
self.sh_cur = None
|
60
|
+
|
59
61
|
self.p_end = 0.0
|
60
62
|
self.p_dur = 0.0
|
61
63
|
|
@@ -92,17 +94,31 @@ class U2idx(object):
|
|
92
94
|
except:
|
93
95
|
raise Pebkac(500, min_ex())
|
94
96
|
|
95
|
-
def
|
96
|
-
if
|
97
|
+
def get_shr(self) :
|
98
|
+
if self.sh_cur:
|
99
|
+
return self.sh_cur
|
100
|
+
|
101
|
+
if not HAVE_SQLITE3 or not self.args.shr:
|
97
102
|
return None
|
98
103
|
|
104
|
+
assert sqlite3 # type: ignore
|
105
|
+
|
106
|
+
db = sqlite3.connect(self.args.shr_db, timeout=2, check_same_thread=False)
|
107
|
+
cur = db.cursor()
|
108
|
+
cur.execute('pragma table_info("sh")').fetchall()
|
109
|
+
self.sh_cur = cur
|
110
|
+
return cur
|
111
|
+
|
112
|
+
def get_cur(self, vn ) :
|
99
113
|
cur = self.cur.get(vn.realpath)
|
100
114
|
if cur:
|
101
115
|
return cur
|
102
116
|
|
103
|
-
if "e2d" not in vn.flags:
|
117
|
+
if not HAVE_SQLITE3 or "e2d" not in vn.flags:
|
104
118
|
return None
|
105
119
|
|
120
|
+
assert sqlite3 # type: ignore
|
121
|
+
|
106
122
|
ptop = vn.realpath
|
107
123
|
histpath = self.asrv.vfs.histtab.get(ptop)
|
108
124
|
if not histpath:
|