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/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 = {k: zs for k, zs in uparam.items()}
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.RS + vpath
971
+ vp = self.args.SRS + vpath
965
972
  html = self.j2s(
966
973
  "msg",
967
- h2='<a href="/{}">{} /{}</a>'.format(
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": "/" + vpath})
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
- raise Pebkac(500, "server busy, or sqlite3 not available; cannot search")
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
- raise Pebkac(500, "sqlite3 is not available on the server; cannot unpost")
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 = ["splash", "svcs", "browser", "browser2", "msg", "md", "mde", "cf"]
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 = (ip, sck.fileno())
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
- self.up2k.reload(rescan_all_vols)
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
- ip = ip.split("unix:")[1]
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 get_cur(self, vn ) :
96
- if not HAVE_SQLITE3:
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: