copyparty 1.13.8__py3-none-any.whl → 1.14.1__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 +183 -26
- copyparty/httpsrv.py +12 -2
- copyparty/svchub.py +77 -5
- copyparty/tcpsrv.py +19 -1
- copyparty/u2idx.py +19 -3
- copyparty/up2k.py +68 -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.8.dist-info → copyparty-1.14.1.dist-info}/METADATA +55 -3
- {copyparty-1.13.8.dist-info → copyparty-1.14.1.dist-info}/RECORD +28 -25
- {copyparty-1.13.8.dist-info → copyparty-1.14.1.dist-info}/WHEEL +1 -1
- {copyparty-1.13.8.dist-info → copyparty-1.14.1.dist-info}/LICENSE +0 -0
- {copyparty-1.13.8.dist-info → copyparty-1.14.1.dist-info}/entry_points.txt +0 -0
- {copyparty-1.13.8.dist-info → copyparty-1.14.1.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,
|
@@ -450,7 +451,7 @@ class HttpCli(object):
|
|
450
451
|
t = "incorrect --rp-loc or webserver config; expected vpath starting with [{}] but got [{}]"
|
451
452
|
self.log(t.format(self.args.R, vpath), 1)
|
452
453
|
|
453
|
-
self.ouparam =
|
454
|
+
self.ouparam = uparam.copy()
|
454
455
|
|
455
456
|
if self.args.rsp_slp:
|
456
457
|
time.sleep(self.args.rsp_slp)
|
@@ -459,6 +460,9 @@ class HttpCli(object):
|
|
459
460
|
|
460
461
|
zso = self.headers.get("cookie")
|
461
462
|
if zso:
|
463
|
+
if len(zso) > 8192:
|
464
|
+
self.loud_reply("cookie header too big", status=400)
|
465
|
+
return False
|
462
466
|
zsll = [x.split("=", 1) for x in zso.split(";") if "=" in x]
|
463
467
|
cookies = {k.strip(): unescape_cookie(zs) for k, zs in zsll}
|
464
468
|
cookie_pw = cookies.get("cppws") or cookies.get("cppwd") or ""
|
@@ -964,10 +968,10 @@ class HttpCli(object):
|
|
964
968
|
status = 200,
|
965
969
|
use302 = False,
|
966
970
|
) :
|
967
|
-
vp = self.args.
|
971
|
+
vp = self.args.SRS + vpath
|
968
972
|
html = self.j2s(
|
969
973
|
"msg",
|
970
|
-
h2='<a href="
|
974
|
+
h2='<a href="{}">{} {}</a>'.format(
|
971
975
|
quotep(vp) + suf, flavor, html_escape(vp, crlf=True) + suf
|
972
976
|
),
|
973
977
|
pre=msg,
|
@@ -975,7 +979,7 @@ class HttpCli(object):
|
|
975
979
|
).encode("utf-8", "replace")
|
976
980
|
|
977
981
|
if use302:
|
978
|
-
self.reply(html, status=302, headers={"Location":
|
982
|
+
self.reply(html, status=302, headers={"Location": vp})
|
979
983
|
else:
|
980
984
|
self.reply(html, status=status)
|
981
985
|
|
@@ -1137,7 +1141,7 @@ class HttpCli(object):
|
|
1137
1141
|
if "move" in self.uparam:
|
1138
1142
|
return self.handle_mv()
|
1139
1143
|
|
1140
|
-
if not self.vpath:
|
1144
|
+
if not self.vpath and self.ouparam:
|
1141
1145
|
if "reload" in self.uparam:
|
1142
1146
|
return self.handle_reload()
|
1143
1147
|
|
@@ -1159,23 +1163,12 @@ class HttpCli(object):
|
|
1159
1163
|
if "hc" in self.uparam:
|
1160
1164
|
return self.tx_svcs()
|
1161
1165
|
|
1166
|
+
if "shares" in self.uparam:
|
1167
|
+
return self.tx_shares()
|
1168
|
+
|
1162
1169
|
if "h" in self.uparam:
|
1163
1170
|
return self.tx_mounts()
|
1164
1171
|
|
1165
|
-
# conditional redirect to single volumes
|
1166
|
-
if not self.vpath and not self.ouparam:
|
1167
|
-
nread = len(self.rvol)
|
1168
|
-
nwrite = len(self.wvol)
|
1169
|
-
if nread + nwrite == 1 or (self.rvol == self.wvol and nread == 1):
|
1170
|
-
if nread == 1:
|
1171
|
-
vpath = self.rvol[0]
|
1172
|
-
else:
|
1173
|
-
vpath = self.wvol[0]
|
1174
|
-
|
1175
|
-
if self.vpath != vpath:
|
1176
|
-
self.redirect(vpath, flavor="redirecting to", use302=True)
|
1177
|
-
return True
|
1178
|
-
|
1179
1172
|
return self.tx_browser()
|
1180
1173
|
|
1181
1174
|
def handle_propfind(self) :
|
@@ -1614,6 +1607,9 @@ class HttpCli(object):
|
|
1614
1607
|
if "delete" in self.uparam:
|
1615
1608
|
return self.handle_rm([])
|
1616
1609
|
|
1610
|
+
if "unshare" in self.uparam:
|
1611
|
+
return self.handle_unshare()
|
1612
|
+
|
1617
1613
|
if "application/octet-stream" in ctype:
|
1618
1614
|
return self.handle_post_binary()
|
1619
1615
|
|
@@ -2085,6 +2081,9 @@ class HttpCli(object):
|
|
2085
2081
|
if act == "zip":
|
2086
2082
|
return self.handle_zip_post()
|
2087
2083
|
|
2084
|
+
if act == "chpw":
|
2085
|
+
return self.handle_chpw()
|
2086
|
+
|
2088
2087
|
raise Pebkac(422, 'invalid action "{}"'.format(act))
|
2089
2088
|
|
2090
2089
|
def handle_zip_post(self) :
|
@@ -2143,6 +2142,9 @@ class HttpCli(object):
|
|
2143
2142
|
if "srch" in self.uparam or "srch" in body:
|
2144
2143
|
return self.handle_search(body)
|
2145
2144
|
|
2145
|
+
if "share" in self.uparam:
|
2146
|
+
return self.handle_share(body)
|
2147
|
+
|
2146
2148
|
if "delete" in self.uparam:
|
2147
2149
|
return self.handle_rm(body)
|
2148
2150
|
|
@@ -2199,7 +2201,9 @@ class HttpCli(object):
|
|
2199
2201
|
def handle_search(self, body ) :
|
2200
2202
|
idx = self.conn.get_u2idx()
|
2201
2203
|
if not idx or not hasattr(idx, "p_end"):
|
2202
|
-
|
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")
|
2203
2207
|
|
2204
2208
|
vols = []
|
2205
2209
|
seen = {}
|
@@ -2389,6 +2393,22 @@ class HttpCli(object):
|
|
2389
2393
|
self.reply(b"thank")
|
2390
2394
|
return True
|
2391
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
|
+
|
2392
2412
|
def handle_login(self) :
|
2393
2413
|
assert self.parser
|
2394
2414
|
pwd = self.parser.require("cppwd", 64)
|
@@ -2413,12 +2433,12 @@ class HttpCli(object):
|
|
2413
2433
|
dst += "&" if "?" in dst else "?"
|
2414
2434
|
dst += "_=1#" + html_escape(uhash, True, True)
|
2415
2435
|
|
2416
|
-
msg = self.get_pwd_cookie(pwd)
|
2436
|
+
_, msg = self.get_pwd_cookie(pwd)
|
2417
2437
|
html = self.j2s("msg", h1=msg, h2='<a href="' + dst + '">ack</a>', redir=dst)
|
2418
2438
|
self.reply(html.encode("utf-8"))
|
2419
2439
|
return True
|
2420
2440
|
|
2421
|
-
def get_pwd_cookie(self, pwd )
|
2441
|
+
def get_pwd_cookie(self, pwd ) :
|
2422
2442
|
hpwd = self.asrv.ah.hash(pwd)
|
2423
2443
|
uname = self.asrv.iacct.get(hpwd)
|
2424
2444
|
if uname:
|
@@ -2450,7 +2470,7 @@ class HttpCli(object):
|
|
2450
2470
|
ck = gencookie(k, pwd, self.args.R, self.is_https, dur, "; HttpOnly")
|
2451
2471
|
self.out_headerlist.append(("Set-Cookie", ck))
|
2452
2472
|
|
2453
|
-
return msg
|
2473
|
+
return dur > 0, msg
|
2454
2474
|
|
2455
2475
|
def handle_mkdir(self) :
|
2456
2476
|
assert self.parser
|
@@ -2489,7 +2509,7 @@ class HttpCli(object):
|
|
2489
2509
|
except:
|
2490
2510
|
raise Pebkac(500, min_ex())
|
2491
2511
|
|
2492
|
-
self.out_headers["X-New-Dir"] = quotep(vpath)
|
2512
|
+
self.out_headers["X-New-Dir"] = quotep(self.args.RS + vpath)
|
2493
2513
|
|
2494
2514
|
if dav:
|
2495
2515
|
self.reply(b"", 201)
|
@@ -3944,6 +3964,7 @@ class HttpCli(object):
|
|
3944
3964
|
k304=self.k304(),
|
3945
3965
|
k304vis=self.args.k304 > 0,
|
3946
3966
|
ver=S_VERSION if self.args.ver else "",
|
3967
|
+
chpw=self.args.chpw and self.uname != "*",
|
3947
3968
|
ahttps="" if self.is_https else "https://" + self.host + self.req,
|
3948
3969
|
)
|
3949
3970
|
self.reply(html.encode("utf-8"))
|
@@ -4078,7 +4099,9 @@ class HttpCli(object):
|
|
4078
4099
|
dst = dst[len(top) + 1 :]
|
4079
4100
|
|
4080
4101
|
ret = self.gen_tree(top, dst, self.uparam.get("k", ""))
|
4081
|
-
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
|
4082
4105
|
parents = self.args.R.split("/")
|
4083
4106
|
for parent in reversed(parents):
|
4084
4107
|
ret = {"k%s" % (parent,): ret, "a": []}
|
@@ -4153,7 +4176,9 @@ class HttpCli(object):
|
|
4153
4176
|
def tx_ups(self) :
|
4154
4177
|
idx = self.conn.get_u2idx()
|
4155
4178
|
if not idx or not hasattr(idx, "p_end"):
|
4156
|
-
|
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")
|
4157
4182
|
|
4158
4183
|
filt = self.uparam.get("filter") or ""
|
4159
4184
|
lm = "ups [{}]".format(filt)
|
@@ -4242,6 +4267,137 @@ class HttpCli(object):
|
|
4242
4267
|
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
4243
4268
|
return True
|
4244
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
|
+
|
4245
4401
|
def handle_rm(self, req ) :
|
4246
4402
|
if not req and not self.can_delete:
|
4247
4403
|
raise Pebkac(403, "not allowed for user " + self.uname)
|
@@ -4640,6 +4796,7 @@ class HttpCli(object):
|
|
4640
4796
|
"have_mv": (not self.args.no_mv),
|
4641
4797
|
"have_del": (not self.args.no_del),
|
4642
4798
|
"have_zip": (not self.args.no_zip),
|
4799
|
+
"have_shr": self.args.shr,
|
4643
4800
|
"have_unpost": int(self.args.unpost),
|
4644
4801
|
"sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
|
4645
4802
|
"dgrid": "grid" in vf,
|
copyparty/httpsrv.py
CHANGED
@@ -151,7 +151,17 @@ class HttpSrv(object):
|
|
151
151
|
|
152
152
|
env = jinja2.Environment()
|
153
153
|
env.loader = jinja2.FileSystemLoader(os.path.join(self.E.mod, "web"))
|
154
|
-
jn = [
|
154
|
+
jn = [
|
155
|
+
"splash",
|
156
|
+
"shares",
|
157
|
+
"svcs",
|
158
|
+
"browser",
|
159
|
+
"browser2",
|
160
|
+
"msg",
|
161
|
+
"md",
|
162
|
+
"mde",
|
163
|
+
"cf",
|
164
|
+
]
|
155
165
|
self.j2 = {x: env.get_template(x + ".html") for x in jn}
|
156
166
|
zs = os.path.join(self.E.mod, "web", "deps", "prism.js.gz")
|
157
167
|
self.prism = os.path.exists(zs)
|
@@ -362,7 +372,7 @@ class HttpSrv(object):
|
|
362
372
|
cip = cip[7:]
|
363
373
|
addr = (cip, saddr[1])
|
364
374
|
else:
|
365
|
-
addr = (
|
375
|
+
addr = ("127.8.3.7", sck.fileno())
|
366
376
|
except (OSError, socket.error) as ex:
|
367
377
|
if self.stopping:
|
368
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
@@ -223,10 +223,22 @@ class TcpSrv(object):
|
|
223
223
|
self.log("tcpsrv", msg, c)
|
224
224
|
|
225
225
|
def _listen(self, ip , port ) :
|
226
|
+
uds_perm = uds_gid = -1
|
226
227
|
if "unix:" in ip:
|
227
228
|
tcp = False
|
228
229
|
ipv = socket.AF_UNIX
|
229
|
-
|
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
|
+
|
230
242
|
elif ":" in ip:
|
231
243
|
tcp = True
|
232
244
|
ipv = socket.AF_INET6
|
@@ -262,7 +274,13 @@ class TcpSrv(object):
|
|
262
274
|
srv.bind(ip)
|
263
275
|
else:
|
264
276
|
tf = "%s.%d" % (ip, os.getpid())
|
277
|
+
if os.path.exists(tf):
|
278
|
+
os.unlink(tf)
|
265
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)
|
266
284
|
atomic_move(self.nlog, tf, ip, VF_CAREFUL)
|
267
285
|
|
268
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:
|