copyparty 1.14.1__py3-none-any.whl → 1.14.2__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 +3 -3
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +51 -8
- copyparty/httpcli.py +49 -14
- copyparty/svchub.py +39 -7
- copyparty/up2k.py +2 -2
- copyparty/web/a/u2c.py +5 -3
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/shares.css.gz +0 -0
- copyparty/web/shares.html +8 -8
- copyparty/web/shares.js.gz +0 -0
- copyparty/web/splash.css.gz +0 -0
- copyparty/web/splash.html +33 -15
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.14.1.dist-info → copyparty-1.14.2.dist-info}/METADATA +11 -4
- {copyparty-1.14.1.dist-info → copyparty-1.14.2.dist-info}/RECORD +22 -22
- {copyparty-1.14.1.dist-info → copyparty-1.14.2.dist-info}/WHEEL +1 -1
- {copyparty-1.14.1.dist-info → copyparty-1.14.2.dist-info}/LICENSE +0 -0
- {copyparty-1.14.1.dist-info → copyparty-1.14.2.dist-info}/entry_points.txt +0 -0
- {copyparty-1.14.1.dist-info → copyparty-1.14.2.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -969,8 +969,8 @@ def add_fs(ap):
|
|
969
969
|
def add_share(ap):
|
970
970
|
db_path = os.path.join(E.cfg, "shares.db")
|
971
971
|
ap2 = ap.add_argument_group('share-url options')
|
972
|
-
ap2.add_argument("--shr", metavar="
|
973
|
-
ap2.add_argument("--shr-db", metavar="
|
972
|
+
ap2.add_argument("--shr", metavar="DIR", default="", help="toplevel virtual folder for shared files/folders, for example [\033[32m/share\033[0m]")
|
973
|
+
ap2.add_argument("--shr-db", metavar="FILE", default=db_path, help="database to store shares in")
|
974
974
|
ap2.add_argument("--shr-adm", metavar="U,U", default="", help="comma-separated list of users allowed to view/delete any share")
|
975
975
|
ap2.add_argument("--shr-v", action="store_true", help="debug")
|
976
976
|
|
@@ -1406,7 +1406,7 @@ def add_ui(ap, retry):
|
|
1406
1406
|
ap2 = ap.add_argument_group('ui options')
|
1407
1407
|
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
1408
1408
|
ap2.add_argument("--gsel", action="store_true", help="select files in grid by ctrl-click (volflag=gsel)")
|
1409
|
-
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language; one of the following: \033[32meng nor\033[0m")
|
1409
|
+
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language; one of the following: \033[32meng nor chi\033[0m")
|
1410
1410
|
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..7)")
|
1411
1411
|
ap2.add_argument("--themes", metavar="NUM", type=int, default=8, help="number of themes installed")
|
1412
1412
|
ap2.add_argument("--au-vol", metavar="0-100", type=int, default=50, choices=range(0, 101), help="default audio/video volume percent")
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -35,6 +35,7 @@ from .util import (
|
|
35
35
|
odfusion,
|
36
36
|
relchk,
|
37
37
|
statdir,
|
38
|
+
ub64enc,
|
38
39
|
uncyg,
|
39
40
|
undot,
|
40
41
|
unhumanize,
|
@@ -337,6 +338,7 @@ class VFS(object):
|
|
337
338
|
self.dbv = None # closest full/non-jump parent
|
338
339
|
self.lim = None # upload limits; only set for dbv
|
339
340
|
self.shr_src = None # source vfs+rem of a share
|
341
|
+
self.shr_files = set() # filenames to include from shr_src
|
340
342
|
self.aread = {}
|
341
343
|
self.awrite = {}
|
342
344
|
self.amove = {}
|
@@ -362,6 +364,7 @@ class VFS(object):
|
|
362
364
|
self.all_vps = []
|
363
365
|
|
364
366
|
self.get_dbv = self._get_dbv
|
367
|
+
self.ls = self._ls
|
365
368
|
|
366
369
|
def __repr__(self) :
|
367
370
|
return "VFS(%s)" % (
|
@@ -558,7 +561,26 @@ class VFS(object):
|
|
558
561
|
ad, fn = os.path.split(ap)
|
559
562
|
return os.path.join(absreal(ad), fn)
|
560
563
|
|
561
|
-
def
|
564
|
+
def _ls_nope(
|
565
|
+
self, *a, **ka
|
566
|
+
) :
|
567
|
+
raise Pebkac(500, "nope.avi")
|
568
|
+
|
569
|
+
def _ls_shr(
|
570
|
+
self,
|
571
|
+
rem ,
|
572
|
+
uname ,
|
573
|
+
scandir ,
|
574
|
+
permsets ,
|
575
|
+
lstat = False,
|
576
|
+
) :
|
577
|
+
"""replaces _ls for certain shares (single-file, or file selection)"""
|
578
|
+
vn, rem = self.shr_src # type: ignore
|
579
|
+
abspath, real, _ = vn.ls(rem, "\n", scandir, permsets, lstat)
|
580
|
+
real = [x for x in real if os.path.basename(x[0]) in self.shr_files]
|
581
|
+
return abspath, real, {}
|
582
|
+
|
583
|
+
def _ls(
|
562
584
|
self,
|
563
585
|
rem ,
|
564
586
|
uname ,
|
@@ -1501,14 +1523,14 @@ class AuthSrv(object):
|
|
1501
1523
|
import sqlite3
|
1502
1524
|
|
1503
1525
|
shv = VFS(self.log_func, "", shr, AXS(), {"d2d": True})
|
1504
|
-
par = vfs.all_vols[""]
|
1505
1526
|
|
1506
1527
|
db_path = self.args.shr_db
|
1507
1528
|
db = sqlite3.connect(db_path)
|
1508
1529
|
cur = db.cursor()
|
1530
|
+
cur2 = db.cursor()
|
1509
1531
|
now = time.time()
|
1510
1532
|
for row in cur.execute("select * from sh"):
|
1511
|
-
s_k, s_pw, s_vp, s_pr,
|
1533
|
+
s_k, s_pw, s_vp, s_pr, s_nf, s_un, s_t0, s_t1 = row
|
1512
1534
|
if s_t1 and s_t1 < now:
|
1513
1535
|
continue
|
1514
1536
|
|
@@ -1517,7 +1539,10 @@ class AuthSrv(object):
|
|
1517
1539
|
self.log(t % (s_pr, s_k, s_un, s_vp))
|
1518
1540
|
|
1519
1541
|
if s_pw:
|
1520
|
-
|
1542
|
+
# gotta reuse the "account" for all shares with this pw,
|
1543
|
+
# so do a light scramble as this appears in the web-ui
|
1544
|
+
zs = ub64enc(hashlib.sha512(s_pw.encode("utf-8")).digest())[4:16]
|
1545
|
+
sun = "s_%s" % (zs.decode("utf-8"),)
|
1521
1546
|
acct[sun] = s_pw
|
1522
1547
|
else:
|
1523
1548
|
sun = "*"
|
@@ -1532,13 +1557,14 @@ class AuthSrv(object):
|
|
1532
1557
|
# don't know the abspath yet + wanna ensure the user
|
1533
1558
|
# still has the privs they granted, so nullmap it
|
1534
1559
|
shv.nodes[s_k] = VFS(
|
1535
|
-
self.log_func, "", "%s/%s" % (shr, s_k), s_axs,
|
1560
|
+
self.log_func, "", "%s/%s" % (shr, s_k), s_axs, shv.flags.copy()
|
1536
1561
|
)
|
1537
1562
|
|
1538
1563
|
vfs.nodes[shr] = vfs.all_vols[shr] = shv
|
1539
1564
|
for vol in shv.nodes.values():
|
1540
1565
|
vfs.all_vols[vol.vpath] = vol
|
1541
1566
|
vol.get_dbv = vol._get_share_src
|
1567
|
+
vol.ls = vol._ls_nope
|
1542
1568
|
|
1543
1569
|
zss = set(acct)
|
1544
1570
|
zss.update(self.idp_accs)
|
@@ -2048,6 +2074,9 @@ class AuthSrv(object):
|
|
2048
2074
|
if not self.warn_anonwrite or verbosity < 5:
|
2049
2075
|
break
|
2050
2076
|
|
2077
|
+
if enshare and (zv.vpath == shr or zv.vpath.startswith(shrs)):
|
2078
|
+
continue
|
2079
|
+
|
2051
2080
|
t += '\n\033[36m"/{}" \033[33m{}\033[0m'.format(zv.vpath, zv.realpath)
|
2052
2081
|
for txt, attr in [
|
2053
2082
|
[" read", "uread"],
|
@@ -2154,10 +2183,9 @@ class AuthSrv(object):
|
|
2154
2183
|
if x != shr and not x.startswith(shrs)
|
2155
2184
|
}
|
2156
2185
|
|
2157
|
-
assert cur # type: ignore
|
2158
|
-
assert shv # type: ignore
|
2186
|
+
assert db and cur and cur2 and shv # type: ignore
|
2159
2187
|
for row in cur.execute("select * from sh"):
|
2160
|
-
s_k, s_pw, s_vp, s_pr,
|
2188
|
+
s_k, s_pw, s_vp, s_pr, s_nf, s_un, s_t0, s_t1 = row
|
2161
2189
|
shn = shv.nodes.get(s_k, None)
|
2162
2190
|
if not shn:
|
2163
2191
|
continue
|
@@ -2172,6 +2200,17 @@ class AuthSrv(object):
|
|
2172
2200
|
shv.nodes.pop(s_k)
|
2173
2201
|
continue
|
2174
2202
|
|
2203
|
+
fns = []
|
2204
|
+
if s_nf:
|
2205
|
+
q = "select vp from sf where k = ?"
|
2206
|
+
for (s_fn,) in cur2.execute(q, (s_k,)):
|
2207
|
+
fns.append(s_fn)
|
2208
|
+
|
2209
|
+
shn.shr_files = set(fns)
|
2210
|
+
shn.ls = shn._ls_shr
|
2211
|
+
else:
|
2212
|
+
shn.ls = shn._ls
|
2213
|
+
|
2175
2214
|
shn.shr_src = (s_vfs, s_rem)
|
2176
2215
|
shn.realpath = s_vfs.canonical(s_rem)
|
2177
2216
|
|
@@ -2191,6 +2230,10 @@ class AuthSrv(object):
|
|
2191
2230
|
# hide subvolume
|
2192
2231
|
vn.nodes[zs] = VFS(self.log_func, "", "", AXS(), {})
|
2193
2232
|
|
2233
|
+
cur2.close()
|
2234
|
+
cur.close()
|
2235
|
+
db.close()
|
2236
|
+
|
2194
2237
|
def chpw(self, broker , uname, pw) :
|
2195
2238
|
if not self.args.chpw:
|
2196
2239
|
return False, "feature disabled in server config"
|
copyparty/httpcli.py
CHANGED
@@ -3262,7 +3262,8 @@ class HttpCli(object):
|
|
3262
3262
|
raise Exception("not found in registry")
|
3263
3263
|
self.pipes.set(req_path, job)
|
3264
3264
|
except Exception as ex:
|
3265
|
-
|
3265
|
+
if getattr(ex, "errno", 0) != errno.ENOENT:
|
3266
|
+
self.log("will not pipe [%s]; %s" % (ap_data, ex), 6)
|
3266
3267
|
ptop = None
|
3267
3268
|
|
3268
3269
|
#
|
@@ -3954,6 +3955,7 @@ class HttpCli(object):
|
|
3954
3955
|
rvol=rvol,
|
3955
3956
|
wvol=wvol,
|
3956
3957
|
avol=avol,
|
3958
|
+
in_shr=self.args.shr and self.vpath.startswith(self.args.shr[1:]),
|
3957
3959
|
vstate=vstate,
|
3958
3960
|
scanning=vs["scanning"],
|
3959
3961
|
hashq=vs["hashq"],
|
@@ -4002,10 +4004,10 @@ class HttpCli(object):
|
|
4002
4004
|
def tx_404(self, is_403 = False) :
|
4003
4005
|
rc = 404
|
4004
4006
|
if self.args.vague_403:
|
4005
|
-
t = '<h1 id="n">404 not found ┐( ´ -`)┌</h1><p id="o">or maybe you don\'t have access -- try
|
4006
|
-
pt = "404 not found ┐( ´ -`)┌ (or maybe you don't have access -- try
|
4007
|
+
t = '<h1 id="n">404 not found ┐( ´ -`)┌</h1><p id="o">or maybe you don\'t have access -- try a password or <a href="{}/?h">go home</a></p>'
|
4008
|
+
pt = "404 not found ┐( ´ -`)┌ (or maybe you don't have access -- try a password)"
|
4007
4009
|
elif is_403:
|
4008
|
-
t = '<h1 id="p">403 forbiddena ~┻━┻</h1><p id="q">
|
4010
|
+
t = '<h1 id="p">403 forbiddena ~┻━┻</h1><p id="q">use a password or <a href="{}/?h">go home</a></p>'
|
4009
4011
|
pt = "403 forbiddena ~┻━┻ (you'll have to log in)"
|
4010
4012
|
rc = 403
|
4011
4013
|
else:
|
@@ -4022,7 +4024,8 @@ class HttpCli(object):
|
|
4022
4024
|
|
4023
4025
|
t = t.format(self.args.SR)
|
4024
4026
|
qv = quotep(self.vpaths) + self.ourlq()
|
4025
|
-
|
4027
|
+
in_shr = self.args.shr and self.vpath.startswith(self.args.shr[1:])
|
4028
|
+
html = self.j2s("splash", this=self, qvpath=qv, in_shr=in_shr, msg=t)
|
4026
4029
|
self.reply(html.encode("utf-8"), status=rc)
|
4027
4030
|
return True
|
4028
4031
|
|
@@ -4340,11 +4343,31 @@ class HttpCli(object):
|
|
4340
4343
|
self.log("handle_share: " + json.dumps(req, indent=4))
|
4341
4344
|
|
4342
4345
|
skey = req["k"]
|
4343
|
-
|
4346
|
+
vps = req["vp"]
|
4347
|
+
fns = []
|
4348
|
+
if len(vps) == 1:
|
4349
|
+
vp = vps[0]
|
4350
|
+
if not vp.endswith("/"):
|
4351
|
+
vp, zs = vp.rsplit("/", 1)
|
4352
|
+
fns = [zs]
|
4353
|
+
else:
|
4354
|
+
for zs in vps:
|
4355
|
+
if zs.endswith("/"):
|
4356
|
+
t = "you cannot select more than one folder, or mix flies and folders in one selection"
|
4357
|
+
raise Pebkac(400, t)
|
4358
|
+
vp = vps[0].rsplit("/", 1)[0]
|
4359
|
+
for zs in vps:
|
4360
|
+
vp2, fn = zs.rsplit("/", 1)
|
4361
|
+
fns.append(fn)
|
4362
|
+
if vp != vp2:
|
4363
|
+
t = "mismatching base paths in selection:\n [%s]\n [%s]"
|
4364
|
+
raise Pebkac(400, t % (vp, vp2))
|
4365
|
+
|
4366
|
+
vp = vp.strip("/")
|
4344
4367
|
if self.is_vproxied and (vp == self.args.R or vp.startswith(self.args.RS)):
|
4345
4368
|
vp = vp[len(self.args.RS) :]
|
4346
4369
|
|
4347
|
-
m = re.search(r"([^0-9a-zA-Z_
|
4370
|
+
m = re.search(r"([^0-9a-zA-Z_-])", skey)
|
4348
4371
|
if m:
|
4349
4372
|
raise Pebkac(400, "sharekey has illegal character [%s]" % (m[1],))
|
4350
4373
|
|
@@ -4371,29 +4394,41 @@ class HttpCli(object):
|
|
4371
4394
|
except:
|
4372
4395
|
raise Pebkac(400, "you dont have all the perms you tried to grant")
|
4373
4396
|
|
4374
|
-
ap = vfs.
|
4375
|
-
|
4376
|
-
|
4397
|
+
ap, reals, _ = vfs.ls(
|
4398
|
+
rem, self.uname, not self.args.no_scandir, [[s_rd, s_wr, s_mv, s_del]]
|
4399
|
+
)
|
4400
|
+
rfns = set([x[0] for x in reals])
|
4401
|
+
for fn in fns:
|
4402
|
+
if fn not in rfns:
|
4403
|
+
raise Pebkac(400, "selected file not found on disk: [%s]" % (fn,))
|
4377
4404
|
|
4378
4405
|
pw = req.get("pw") or ""
|
4379
4406
|
now = int(time.time())
|
4380
4407
|
sexp = req["exp"]
|
4381
|
-
exp =
|
4408
|
+
exp = int(sexp) if sexp else 0
|
4409
|
+
exp = now + exp * 60 if exp else 0
|
4382
4410
|
pr = "".join(zc for zc, zb in zip("rwmd", (s_rd, s_wr, s_mv, s_del)) if zb)
|
4383
4411
|
|
4384
4412
|
q = "insert into sh values (?,?,?,?,?,?,?,?)"
|
4385
|
-
cur.execute(q, (skey, pw, vp, pr,
|
4386
|
-
|
4413
|
+
cur.execute(q, (skey, pw, vp, pr, len(fns), self.uname, now, exp))
|
4414
|
+
|
4415
|
+
q = "insert into sf values (?,?)"
|
4416
|
+
for fn in fns:
|
4417
|
+
cur.execute(q, (skey, fn))
|
4387
4418
|
|
4419
|
+
cur.connection.commit()
|
4388
4420
|
self.conn.hsrv.broker.ask("_reload_blocking", False, False).get()
|
4389
4421
|
self.conn.hsrv.broker.ask("up2k.wake_rescanner").get()
|
4390
4422
|
|
4391
|
-
|
4423
|
+
fn = quotep(fns[0]) if len(fns) == 1 else ""
|
4424
|
+
|
4425
|
+
surl = "created share: %s://%s%s%s%s/%s" % (
|
4392
4426
|
"https" if self.is_https else "http",
|
4393
4427
|
self.host,
|
4394
4428
|
self.args.SR,
|
4395
4429
|
self.args.shr,
|
4396
4430
|
skey,
|
4431
|
+
fn,
|
4397
4432
|
)
|
4398
4433
|
self.loud_reply(surl, status=201)
|
4399
4434
|
return True
|
copyparty/svchub.py
CHANGED
@@ -370,11 +370,18 @@ class SvcHub(object):
|
|
370
370
|
|
371
371
|
import sqlite3
|
372
372
|
|
373
|
-
al.shr =
|
373
|
+
al.shr = al.shr.strip("/")
|
374
|
+
if "/" in al.shr or not al.shr:
|
375
|
+
t = "config error: --shr must be the name of a virtual toplevel directory to put shares inside"
|
376
|
+
self.log("root", t, 1)
|
377
|
+
raise Exception(t)
|
378
|
+
|
379
|
+
al.shr = "/%s/" % (al.shr,)
|
374
380
|
|
375
381
|
create = True
|
382
|
+
modified = False
|
376
383
|
db_path = self.args.shr_db
|
377
|
-
self.log("root", "
|
384
|
+
self.log("root", "opening shares-db %s" % (db_path,))
|
378
385
|
for n in range(2):
|
379
386
|
try:
|
380
387
|
db = sqlite3.connect(db_path)
|
@@ -400,18 +407,43 @@ class SvcHub(object):
|
|
400
407
|
pass
|
401
408
|
os.unlink(db_path)
|
402
409
|
|
410
|
+
sch1 = [
|
411
|
+
r"create table kv (k text, v int)",
|
412
|
+
r"create table sh (k text, pw text, vp text, pr text, st int, un text, t0 int, t1 int)",
|
413
|
+
# sharekey, password, src, perms, numFiles, owner, created, expires
|
414
|
+
]
|
415
|
+
sch2 = [
|
416
|
+
r"create table sf (k text, vp text)",
|
417
|
+
r"create index sf_k on sf(k)",
|
418
|
+
r"create index sh_k on sh(k)",
|
419
|
+
r"create index sh_t1 on sh(t1)",
|
420
|
+
]
|
421
|
+
|
403
422
|
assert db # type: ignore
|
404
423
|
assert cur # type: ignore
|
405
424
|
if create:
|
425
|
+
dver = 2
|
426
|
+
modified = True
|
427
|
+
for cmd in sch1 + sch2:
|
428
|
+
cur.execute(cmd)
|
429
|
+
self.log("root", "created new shares-db")
|
430
|
+
else:
|
431
|
+
(dver,) = cur.execute("select v from kv where k = 'sver'").fetchall()[0]
|
432
|
+
|
433
|
+
if dver == 1:
|
434
|
+
modified = True
|
435
|
+
for cmd in sch2:
|
436
|
+
cur.execute(cmd)
|
437
|
+
cur.execute("update sh set st = 0")
|
438
|
+
self.log("root", "shares-db schema upgrade ok")
|
439
|
+
|
440
|
+
if modified:
|
406
441
|
for cmd in [
|
407
|
-
|
408
|
-
r"
|
409
|
-
r"create table kv (k text, v int)",
|
410
|
-
r"insert into kv values ('sver', {})".format(1),
|
442
|
+
r"delete from kv where k = 'sver'",
|
443
|
+
r"insert into kv values ('sver', %d)" % (2,),
|
411
444
|
]:
|
412
445
|
cur.execute(cmd)
|
413
446
|
db.commit()
|
414
|
-
self.log("root", "created new shares-db")
|
415
447
|
|
416
448
|
cur.close()
|
417
449
|
db.close()
|
copyparty/up2k.py
CHANGED
@@ -577,8 +577,8 @@ class Up2k(object):
|
|
577
577
|
rm = [x[0] for x in cur.execute(q, (now,))]
|
578
578
|
if rm:
|
579
579
|
self.log("forgetting expired shares %s" % (rm,))
|
580
|
-
|
581
|
-
cur.executemany(
|
580
|
+
cur.executemany("delete from sh where k=?", [(x,) for x in rm])
|
581
|
+
cur.executemany("delete from sf where k=?", [(x,) for x in rm])
|
582
582
|
db.commit()
|
583
583
|
Daemon(self.hub._reload_blocking, "sharedrop", (False, False))
|
584
584
|
|
copyparty/web/a/u2c.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
|
-
S_VERSION = "1.
|
5
|
-
S_BUILD_DT = "2024-08-
|
4
|
+
S_VERSION = "1.23"
|
5
|
+
S_BUILD_DT = "2024-08-22"
|
6
6
|
|
7
7
|
"""
|
8
8
|
u2c.py: upload to copyparty
|
@@ -1236,7 +1236,7 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1236
1236
|
ap.add_argument("-v", action="store_true", help="verbose")
|
1237
1237
|
ap.add_argument("-a", metavar="PASSWD", help="password or $filepath")
|
1238
1238
|
ap.add_argument("-s", action="store_true", help="file-search (disables upload)")
|
1239
|
-
ap.add_argument("-x", type=unicode, metavar="REGEX",
|
1239
|
+
ap.add_argument("-x", type=unicode, metavar="REGEX", action="append", help="skip file if filesystem-abspath matches REGEX (option can be repeated), example: '.*/\\.hist/.*'")
|
1240
1240
|
ap.add_argument("--ok", action="store_true", help="continue even if some local files are inaccessible")
|
1241
1241
|
ap.add_argument("--touch", action="store_true", help="if last-modified timestamps differ, push local to server (need write+delete perms)")
|
1242
1242
|
ap.add_argument("--ow", action="store_true", help="overwrite existing files instead of autorenaming")
|
@@ -1283,6 +1283,8 @@ source file/folder selection uses rsync syntax, meaning that:
|
|
1283
1283
|
if ar.dr:
|
1284
1284
|
ar.ow = True
|
1285
1285
|
|
1286
|
+
ar.x = "|".join(ar.x or [])
|
1287
|
+
|
1286
1288
|
for k in "dl dr drd".split():
|
1287
1289
|
errs = []
|
1288
1290
|
if ar.safe and getattr(ar, k):
|
copyparty/web/browser.css.gz
CHANGED
Binary file
|
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/shares.css.gz
CHANGED
Binary file
|
copyparty/web/shares.html
CHANGED
@@ -15,25 +15,25 @@
|
|
15
15
|
<body>
|
16
16
|
<div id="wrap">
|
17
17
|
<a id="a" href="{{ r }}/?shares" class="af">refresh</a>
|
18
|
-
<a id="a" href="{{ r }}/?h" class="af">
|
18
|
+
<a id="a" href="{{ r }}/?h" class="af">control-panel</a>
|
19
19
|
|
20
20
|
<span>axs = perms (read,write,move,delet)</span>
|
21
|
-
<span>
|
21
|
+
<span>nf = numFiles (0=dir)</span>
|
22
22
|
<span>min/hrs = time left</span>
|
23
23
|
|
24
|
-
<table><tr>
|
24
|
+
<table id="tab"><thead><tr>
|
25
25
|
<th>delete</th>
|
26
26
|
<th>sharekey</th>
|
27
27
|
<th>pw</th>
|
28
28
|
<th>source</th>
|
29
29
|
<th>axs</th>
|
30
|
-
<th>
|
30
|
+
<th>nf</th>
|
31
31
|
<th>user</th>
|
32
32
|
<th>created</th>
|
33
33
|
<th>expires</th>
|
34
34
|
<th>min</th>
|
35
35
|
<th>hrs</th>
|
36
|
-
</tr>
|
36
|
+
</tr></thead><tbody>
|
37
37
|
{% for k, pw, vp, pr, st, un, t0, t1 in rows %}
|
38
38
|
<tr>
|
39
39
|
<td><a href="#" k="{{ k }}">delete</a></td>
|
@@ -45,11 +45,11 @@
|
|
45
45
|
<td>{{ un|e }}</td>
|
46
46
|
<td>{{ t0 }}</td>
|
47
47
|
<td>{{ t1 }}</td>
|
48
|
-
<td>{{ (t1 - now)
|
49
|
-
<td>{{ (t1 - now)
|
48
|
+
<td>{{ ((t1 - now) / 60) | round(1) if t1 else "inf" }}</td>
|
49
|
+
<td>{{ ((t1 - now) / 3600) | round(1) if t1 else "inf" }}</td>
|
50
50
|
</tr>
|
51
51
|
{% endfor %}
|
52
|
-
</table>
|
52
|
+
</tbody></table>
|
53
53
|
{% if not rows %}
|
54
54
|
(you don't have any active shares btw)
|
55
55
|
{% endif %}
|
copyparty/web/shares.js.gz
CHANGED
Binary file
|
copyparty/web/splash.css.gz
CHANGED
Binary file
|
copyparty/web/splash.html
CHANGED
@@ -14,6 +14,7 @@
|
|
14
14
|
|
15
15
|
<body>
|
16
16
|
<div id="wrap">
|
17
|
+
{%- if not in_shr %}
|
17
18
|
<a id="a" href="{{ r }}/?h" class="af">refresh</a>
|
18
19
|
<a id="v" href="{{ r }}/?hc" class="af">connect</a>
|
19
20
|
|
@@ -23,6 +24,7 @@
|
|
23
24
|
<a id="c" href="{{ r }}/?pw=x" class="logout">logout</a>
|
24
25
|
<p><span id="m">welcome back,</span> <strong>{{ this.uname|e }}</strong></p>
|
25
26
|
{%- endif %}
|
27
|
+
{%- endif %}
|
26
28
|
|
27
29
|
{%- if msg %}
|
28
30
|
<div id="msg">
|
@@ -76,6 +78,37 @@
|
|
76
78
|
</ul>
|
77
79
|
{%- endif %}
|
78
80
|
|
81
|
+
{%- if in_shr %}
|
82
|
+
<h1 id="z">unlock this share:</h1>
|
83
|
+
<div>
|
84
|
+
<form id="lf" method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
|
85
|
+
<input type="hidden" id="la" name="act" value="login" />
|
86
|
+
<input type="password" id="lp" name="cppwd" placeholder=" password" />
|
87
|
+
<input type="hidden" name="uhash" id="uhash" value="x" />
|
88
|
+
<input type="submit" id="ls" value="Unlock" />
|
89
|
+
{% if ahttps %}
|
90
|
+
<a id="w" href="{{ ahttps }}">switch to https</a>
|
91
|
+
{% endif %}
|
92
|
+
</form>
|
93
|
+
</div>
|
94
|
+
{%- else %}
|
95
|
+
<h1 id="l">login for more:</h1>
|
96
|
+
<div>
|
97
|
+
<form id="lf" method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
|
98
|
+
<input type="hidden" id="la" name="act" value="login" />
|
99
|
+
<input type="password" id="lp" name="cppwd" placeholder=" password" />
|
100
|
+
<input type="hidden" name="uhash" id="uhash" value="x" />
|
101
|
+
<input type="submit" id="ls" value="Login" />
|
102
|
+
{% if chpw %}
|
103
|
+
<a id="x" href="#">change password</a>
|
104
|
+
{% endif %}
|
105
|
+
{% if ahttps %}
|
106
|
+
<a id="w" href="{{ ahttps }}">switch to https</a>
|
107
|
+
{% endif %}
|
108
|
+
</form>
|
109
|
+
</div>
|
110
|
+
{%- endif %}
|
111
|
+
|
79
112
|
<h1 id="cc">other stuff:</h1>
|
80
113
|
<ul>
|
81
114
|
{%- if this.uname != '*' and this.args.shr %}
|
@@ -94,21 +127,6 @@
|
|
94
127
|
<li><a id="k" href="{{ r }}/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li>
|
95
128
|
</ul>
|
96
129
|
|
97
|
-
<h1 id="l">login for more:</h1>
|
98
|
-
<div>
|
99
|
-
<form id="lf" method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
|
100
|
-
<input type="hidden" id="la" name="act" value="login" />
|
101
|
-
<input type="password" id="lp" name="cppwd" placeholder=" password" />
|
102
|
-
<input type="hidden" name="uhash" id="uhash" value="x" />
|
103
|
-
<input type="submit" id="ls" value="Login" />
|
104
|
-
{% if chpw %}
|
105
|
-
<a id="x" href="#">change password</a>
|
106
|
-
{% endif %}
|
107
|
-
{% if ahttps %}
|
108
|
-
<a id="w" href="{{ ahttps }}">switch to https</a>
|
109
|
-
{% endif %}
|
110
|
-
</form>
|
111
|
-
</div>
|
112
130
|
</div>
|
113
131
|
<a href="#" id="repl">π</a>
|
114
132
|
{%- if not this.args.nb %}
|
copyparty/web/splash.js.gz
CHANGED
Binary file
|
copyparty/web/util.js.gz
CHANGED
Binary file
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.14.
|
3
|
+
Version: 1.14.2
|
4
4
|
Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
|
5
5
|
Author-email: ed <copyparty@ocv.me>
|
6
6
|
License: MIT
|
@@ -804,14 +804,16 @@ you can move files across browser tabs (cut in one tab, paste in another)
|
|
804
804
|
|
805
805
|
share a file or folder by creating a temporary link
|
806
806
|
|
807
|
-
when enabled in the server settings (`--shr`), click the bottom-right `share` button to share the folder you're currently in, or
|
807
|
+
when enabled in the server settings (`--shr`), click the bottom-right `share` button to share the folder you're currently in, or alternatively:
|
808
|
+
* select a folder first to share that folder instead
|
809
|
+
* select one or more files to share only those files
|
808
810
|
|
809
811
|
this feature was made with [identity providers](#identity-providers) in mind -- configure your reverseproxy to skip the IdP's access-control for a given URL prefix and use that to safely share specific files/folders sans the usual auth checks
|
810
812
|
|
811
813
|
when creating a share, the creator can choose any of the following options:
|
812
814
|
|
813
815
|
* password-protection
|
814
|
-
* expire after a certain time
|
816
|
+
* expire after a certain time; `0` or blank means infinite
|
815
817
|
* allow visitors to upload (if the user who creates the share has write-access)
|
816
818
|
|
817
819
|
semi-intentional limitations:
|
@@ -822,10 +824,15 @@ semi-intentional limitations:
|
|
822
824
|
* when linking something to discord (for example) it'll get accessed by their scraper and that would count as a hit
|
823
825
|
* browsers wouldn't be able to resume a broken download unless the requester's IP gets allowlisted for X minutes (ref. tricky)
|
824
826
|
|
825
|
-
|
827
|
+
specify `--shr /foobar` to enable this feature; a toplevel virtual folder named `foobar` is then created, and that's where all the shares will be served from
|
828
|
+
|
829
|
+
* you can name it whatever, `foobar` is just an example
|
830
|
+
* if you're using config files, put `shr: /foobar` inside the `[global]` section instead
|
826
831
|
|
827
832
|
users can delete their own shares in the controlpanel, and a list of privileged users (`--shr-adm`) are allowed to see and/or delet any share on the server
|
828
833
|
|
834
|
+
**security note:** using this feature does not mean that you can skip the [accounts and volumes](#accounts-and-volumes) section -- you still need to restrict access to volumes that you do not intend to share with unauthenticated users! it is not sufficient to use rules in the reverseproxy to restrict access to just the `/share` folder.
|
835
|
+
|
829
836
|
|
830
837
|
## batch rename
|
831
838
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
copyparty/__init__.py,sha256=fUINM1abqDGzCCH_JcXdOnLdKOV-SrTI2Xo2QgQW2P4,1703
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256=
|
2
|
+
copyparty/__main__.py,sha256=T9fipgzSsr9hc5ApvEpoWMe0CPoYpm1jOE2kNznV9Fg,107828
|
3
|
+
copyparty/__version__.py,sha256=wDXrZl0rzt2I3ONTXVvCQwVk8Dh7I819X_mTUcCs4bI,258
|
4
|
+
copyparty/authsrv.py,sha256=jWXTjZLT8cGymfa9wBwGJqyBIi80aXcvzAMgI74G8iA,95750
|
5
5
|
copyparty/broker_mp.py,sha256=YFe1S6Zziht8Qc__dCLj_ff8z0DDny9lqk_Mi5ajsJk,3868
|
6
6
|
copyparty/broker_mpw.py,sha256=4ZI7bJYOwUibeAJVv9_FPGNmHrr9eOtkj_Kz0JEppTU,3197
|
7
7
|
copyparty/broker_thr.py,sha256=eKr--HJGig5zqvNGwH9UoBG9Nvi9mT2axrRmJwknd0s,1759
|
@@ -11,7 +11,7 @@ copyparty/cfg.py,sha256=i8-bjWgbguQooxiA172RcptqR_SEOwDHJ4cqldrZ8oQ,9792
|
|
11
11
|
copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
|
12
12
|
copyparty/fsutil.py,sha256=hnEHgySI43-XJJKbI8n6t1A6oVHzR_nYdsBcAwtreBk,4610
|
13
13
|
copyparty/ftpd.py,sha256=1vD-KTy07xfEEEk1dx37pUYModpNO2gIhVXvFUr205M,17497
|
14
|
-
copyparty/httpcli.py,sha256=
|
14
|
+
copyparty/httpcli.py,sha256=nlSmnZ6ejFzUGdfyWZ1v2C9ycuDdhDArXL3MZJcxYBI,181984
|
15
15
|
copyparty/httpconn.py,sha256=mwIDup85cBowIfJOse8rla5bqTz7nf-ChgfR-5-V0JM,6938
|
16
16
|
copyparty/httpsrv.py,sha256=8_1Ivg3eco7HJDjqL_rUB58IOUaUnoXGhO62bOMXLBk,17242
|
17
17
|
copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
|
@@ -24,14 +24,14 @@ copyparty/smbd.py,sha256=8zkC9BjVtGiKXMLajbdakxoKeFzACdM75SW0_SvqXJA,14490
|
|
24
24
|
copyparty/ssdp.py,sha256=8iyF5sqIjATJLWcAtnJa8eadHosOn0CP4ywltzJ7bVY,7023
|
25
25
|
copyparty/star.py,sha256=tV5BbX6AiQ7N4UU8DYtSTckNYeoeey4DBqq4LjfymbY,3818
|
26
26
|
copyparty/sutil.py,sha256=JTMrQwcWH85hXB_cKG206eDZ967WZDGaP00AWvl_gB0,3214
|
27
|
-
copyparty/svchub.py,sha256=
|
27
|
+
copyparty/svchub.py,sha256=fiH7d8UXiW_LC7m2MU0AtS6KWwy49QXbSp4gIoyT2SM,38364
|
28
28
|
copyparty/szip.py,sha256=tor4yjdHhEL4Ox-Xg7-cuUFrMO0IwQD29aRX5Cp8MYs,8605
|
29
29
|
copyparty/tcpsrv.py,sha256=jM_Za64O8LEMfMrU4irJluIJZrU494e2b759r_KhaUQ,19881
|
30
30
|
copyparty/tftpd.py,sha256=i1-oZ05DJq2_nDOW3g3PfTkMoUCr2lAcDYFMWArwtKA,13568
|
31
31
|
copyparty/th_cli.py,sha256=o6FMkerYvAXS455z3DUossVztu_nzFlYSQhs6qN6Jt8,4636
|
32
32
|
copyparty/th_srv.py,sha256=27IftjIXUQzRRiUytt-CgXkybEoP3HHHoXaDAvxEmLo,29217
|
33
33
|
copyparty/u2idx.py,sha256=t4mzjj2GDrkjIHt0RM68y1EgT5qOBoz6mkYgjMbqA38,13526
|
34
|
-
copyparty/up2k.py,sha256=
|
34
|
+
copyparty/up2k.py,sha256=HNn5EzOM-pExt3waU-0n6_tqGweI--QAO4gKhNSACsU,153107
|
35
35
|
copyparty/util.py,sha256=S7FuQBPbl2FX7ULIH9VNgiB7Z_rceqTJssXz4SGErwA,88625
|
36
36
|
copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
37
|
copyparty/bos/bos.py,sha256=Wb7eWsXJgR5AFlBR9ZOyKrLTwy-Kct9RrGiOu4Jo37Y,1622
|
@@ -55,9 +55,9 @@ copyparty/stolen/ifaddr/_posix.py,sha256=-67NdfGrCktfQPakT2fLbjl2U00QMvyBGkSvrUu
|
|
55
55
|
copyparty/stolen/ifaddr/_shared.py,sha256=uNC4SdEIgdSLKvuUzsf1aM-H1Xrc_9mpLoOT43YukGs,6206
|
56
56
|
copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8QtZM,4026
|
57
57
|
copyparty/web/baguettebox.js.gz,sha256=4dS8-r4si84ca71l98672ahnRI86Aq95MU-bc5knykk,7962
|
58
|
-
copyparty/web/browser.css.gz,sha256=
|
58
|
+
copyparty/web/browser.css.gz,sha256=PoW_IIwFigZaMo3atpPU0o05Jj5Flbsm1bhW_KfcX-U,11491
|
59
59
|
copyparty/web/browser.html,sha256=vvfWiu_aOFRar8u5lridMRKQSPF4R0YkA41zrsh82Qs,4878
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
60
|
+
copyparty/web/browser.js.gz,sha256=0eqpm-iVRYZc-A9rpNNj8I1S_cV90nBb8VOWq-Lk6Zc,80814
|
61
61
|
copyparty/web/browser2.html,sha256=NRUZ08GH-e2YcGXcoz0UjYg6JIVF42u4IMX4HHwWTmg,1587
|
62
62
|
copyparty/web/cf.html,sha256=lJThtNFNAQT1ClCHHlivAkDGE0LutedwopXD62Z8Nys,589
|
63
63
|
copyparty/web/dbg-audio.js.gz,sha256=Ma-KZtK8LnmiwNvNKFKXMPYl_Nn_3U7GsJ6-DRWC2HE,688
|
@@ -71,21 +71,21 @@ copyparty/web/mde.html,sha256=ImBhQAaEUCke2M85QU_fl4X2XQExRLcEzgCEN8RNe9o,1759
|
|
71
71
|
copyparty/web/mde.js.gz,sha256=kN2eUSvr4mFuksfK4-4LimJmWdwsao39Sea2lWtu8L0,2224
|
72
72
|
copyparty/web/msg.css.gz,sha256=u90fXYAVrMD-jqwf5XFVC1ptSpSHZUe8Mez6PX101P8,300
|
73
73
|
copyparty/web/msg.html,sha256=w9CM3hkLLGJX9fWEaG4gSbTOPe2GcPqW8BpSCDiFzOI,977
|
74
|
-
copyparty/web/shares.css.gz,sha256=
|
75
|
-
copyparty/web/shares.html,sha256=
|
76
|
-
copyparty/web/shares.js.gz,sha256=
|
77
|
-
copyparty/web/splash.css.gz,sha256=
|
78
|
-
copyparty/web/splash.html,sha256=
|
79
|
-
copyparty/web/splash.js.gz,sha256=
|
74
|
+
copyparty/web/shares.css.gz,sha256=m-nRqTGPiU3ohZxvGaROzFr98F_jmohQnjieqEAyjBo,496
|
75
|
+
copyparty/web/shares.html,sha256=j3nXy0cWBXHx0kx-NsFwxbtCSRbRowGy5DhF_n6y7iQ,2276
|
76
|
+
copyparty/web/shares.js.gz,sha256=St2Ep6md8XhpmJhxcppYjHCER5Q1GmIdfHtIM3x4iWU,514
|
77
|
+
copyparty/web/splash.css.gz,sha256=4DOtEKBWyaDKel7fdnwvnc9FrKlkht-ec7R2nRlruPU,1023
|
78
|
+
copyparty/web/splash.html,sha256=dAo4KXKmXUMGcIwetZkFtVxk-mCMNkscD36BxLwRdow,4804
|
79
|
+
copyparty/web/splash.js.gz,sha256=0HMVU21vpeA4Jbi6cGsPtKTwhdtT8-puVOQOiQAHGIc,2592
|
80
80
|
copyparty/web/svcs.html,sha256=v0C3cOFWXYlvp3GEifz1Qj0W3MD8JANT3WTON05GZ9o,11797
|
81
81
|
copyparty/web/svcs.js.gz,sha256=k81ZvZ3I-f4fMHKrNGGOgOlvXnCBz0mVjD-8mieoWCA,520
|
82
82
|
copyparty/web/ui.css.gz,sha256=GnR_PxnZGcNs2IJnb5hFffnhlW3cUHkPad3tNIm-7DQ,2637
|
83
83
|
copyparty/web/up2k.js.gz,sha256=KufMtRViAZQo2rVj67iEWbdPxlVeXW85emRYVJoY3aA,22946
|
84
|
-
copyparty/web/util.js.gz,sha256=
|
84
|
+
copyparty/web/util.js.gz,sha256=dPuhXEBJ_T-d2tYUUufGTUul4FYIbuh6GQmtK7iBkEo,14682
|
85
85
|
copyparty/web/w.hash.js.gz,sha256=7wP9EZQNXQxwZnCCFUVsi_-6TM9PLZJeZ9krutXRRj8,1060
|
86
86
|
copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
87
87
|
copyparty/web/a/partyfuse.py,sha256=MuRkaSuYsdfWfBFMOkbPwDXqSvNTw3sd7QhhlKCDZ8I,32311
|
88
|
-
copyparty/web/a/u2c.py,sha256=
|
88
|
+
copyparty/web/a/u2c.py,sha256=WG9njRxY9g9xjO93-fas9Wo-AM8vtrWrUTwpJ5Afmvk,42482
|
89
89
|
copyparty/web/a/webdav-cfg.bat,sha256=Y4NoGZlksAIg4cBMb7KdJrpKC6Nx97onaTl6yMjaimk,1449
|
90
90
|
copyparty/web/dd/2.png,sha256=gJ14XFPzaw95L6z92fSq9eMPikSQyu-03P1lgiGe0_I,258
|
91
91
|
copyparty/web/dd/3.png,sha256=4lho8Koz5tV7jJ4ODo6GMTScZfkqsT05yp48EDFIlyg,252
|
@@ -105,9 +105,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
|
|
105
105
|
copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
|
106
106
|
copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
|
107
107
|
copyparty/web/deps/sha512.hw.js.gz,sha256=vqoXeracj-99Z5MfY3jK2N4WiSzYQdfjy0RnUlQDhSU,8110
|
108
|
-
copyparty-1.14.
|
109
|
-
copyparty-1.14.
|
110
|
-
copyparty-1.14.
|
111
|
-
copyparty-1.14.
|
112
|
-
copyparty-1.14.
|
113
|
-
copyparty-1.14.
|
108
|
+
copyparty-1.14.2.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
109
|
+
copyparty-1.14.2.dist-info/METADATA,sha256=fDuptsXfw2sOmf6nms6NIZRX5cgtutScqpw_UA0DqmE,131543
|
110
|
+
copyparty-1.14.2.dist-info/WHEEL,sha256=Mdi9PDNwEZptOjTlUcAth7XJDFtKrHYaQMPulZeBCiQ,91
|
111
|
+
copyparty-1.14.2.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
112
|
+
copyparty-1.14.2.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
113
|
+
copyparty-1.14.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|