copyparty 1.19.1__py3-none-any.whl → 1.19.3__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 +116 -28
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +33 -7
- copyparty/cfg.py +7 -1
- copyparty/dxml.py +3 -0
- copyparty/ftpd.py +21 -6
- copyparty/httpcli.py +81 -16
- copyparty/httpsrv.py +6 -0
- copyparty/mtag.py +88 -6
- copyparty/svchub.py +76 -5
- copyparty/tcpsrv.py +6 -0
- copyparty/th_cli.py +5 -1
- copyparty/th_srv.py +160 -51
- copyparty/u2idx.py +1 -1
- copyparty/up2k.py +80 -39
- copyparty/util.py +25 -1
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/rups.js.gz +0 -0
- copyparty/web/splash.css.gz +0 -0
- copyparty/web/splash.html +8 -1
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/svcs.html +1 -1
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.19.1.dist-info → copyparty-1.19.3.dist-info}/METADATA +39 -3
- {copyparty-1.19.1.dist-info → copyparty-1.19.3.dist-info}/RECORD +32 -32
- {copyparty-1.19.1.dist-info → copyparty-1.19.3.dist-info}/WHEEL +0 -0
- {copyparty-1.19.1.dist-info → copyparty-1.19.3.dist-info}/entry_points.txt +0 -0
- {copyparty-1.19.1.dist-info → copyparty-1.19.3.dist-info}/licenses/LICENSE +0 -0
- {copyparty-1.19.1.dist-info → copyparty-1.19.3.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -428,6 +428,29 @@ def args_from_cfg(cfg_path ) :
|
|
428
428
|
return ret
|
429
429
|
|
430
430
|
|
431
|
+
def expand_cfg(argv) :
|
432
|
+
if CFG_DEF:
|
433
|
+
supp = args_from_cfg(CFG_DEF[0])
|
434
|
+
argv = supp + argv
|
435
|
+
|
436
|
+
n = 0
|
437
|
+
while n < len(argv):
|
438
|
+
v1 = argv[n]
|
439
|
+
v1v = v1[2:].lstrip("=")
|
440
|
+
try:
|
441
|
+
v2 = argv[n + 1]
|
442
|
+
except:
|
443
|
+
v2 = ""
|
444
|
+
|
445
|
+
n += 1
|
446
|
+
if v1 == "-c" and v2 and os.path.isfile(v2):
|
447
|
+
n += 1
|
448
|
+
argv = argv[:n] + args_from_cfg(v2) + argv[n:]
|
449
|
+
elif v1.startswith("-c") and v1v and os.path.isfile(v1v):
|
450
|
+
argv = argv[:n] + args_from_cfg(v1v) + argv[n:]
|
451
|
+
return argv
|
452
|
+
|
453
|
+
|
431
454
|
def sighandler(sig = None, frame = None) :
|
432
455
|
msg = [""] * 5
|
433
456
|
for th in threading.enumerate():
|
@@ -601,8 +624,41 @@ def get_sects():
|
|
601
624
|
if no accounts or volumes are configured,
|
602
625
|
current folder will be read/write for everyone
|
603
626
|
|
627
|
+
the group @acct will always have every user with an account
|
628
|
+
(the name of that group can be changed with --grp-all)
|
629
|
+
|
604
630
|
consider the config file for more flexible account/volume management,
|
605
631
|
including dynamic reload at runtime (and being more readable w)
|
632
|
+
|
633
|
+
see \033[32m--help-auth\033[0m for ways to provide the password in requests;
|
634
|
+
see \033[32m--help-idp\033[0m for replacing it with SSO and auth-middlewares
|
635
|
+
"""
|
636
|
+
),
|
637
|
+
],
|
638
|
+
[
|
639
|
+
"auth",
|
640
|
+
"how to login from a client",
|
641
|
+
dedent(
|
642
|
+
"""
|
643
|
+
different ways to provide the password so you become authenticated:
|
644
|
+
|
645
|
+
login with the ui:
|
646
|
+
go to \033[36mhttp://127.0.0.1:3923/?h\033[0m and login there
|
647
|
+
|
648
|
+
send the password in the '\033[36mPW\033[0m' http-header:
|
649
|
+
\033[36mPW: \033[35mhunter2\033[0m
|
650
|
+
or if you have \033[33m--accounts\033[0m enabled,
|
651
|
+
\033[36mPW: \033[35med:hunter2\033[0m
|
652
|
+
|
653
|
+
send the password in the URL itself:
|
654
|
+
\033[36mhttp://127.0.0.1:3923/\033[35m?pw=hunter2\033[0m
|
655
|
+
or if you have \033[33m--accounts\033[0m enabled,
|
656
|
+
\033[36mhttp://127.0.0.1:3923/\033[35m?pw=ed:hunter2\033[0m
|
657
|
+
|
658
|
+
use basic-authentication:
|
659
|
+
\033[36mhttp://\033[35med:hunter2\033[36m@127.0.0.1:3923/\033[0m
|
660
|
+
which should be the same as this header:
|
661
|
+
\033[36mAuthorization: Basic \033[35mZWQ6aHVudGVyMg==\033[0m
|
606
662
|
"""
|
607
663
|
),
|
608
664
|
],
|
@@ -754,6 +810,36 @@ def get_sects():
|
|
754
810
|
the upload speed can easily drop to 10% for small files)"""
|
755
811
|
),
|
756
812
|
],
|
813
|
+
[
|
814
|
+
"idp",
|
815
|
+
"replacing the login system with fancy middleware",
|
816
|
+
dedent(
|
817
|
+
"""
|
818
|
+
if you already have a centralized service which handles
|
819
|
+
user-authentication for other services already, you can
|
820
|
+
integrate copyparty with that for automatic login
|
821
|
+
|
822
|
+
if the middleware is providing the username in an http-header
|
823
|
+
named '\033[35mtheUsername\033[0m' then do this: \033[36m--idp-h-usr theUsername\033[0m
|
824
|
+
|
825
|
+
if the middleware is providing a list of groups in the header
|
826
|
+
named '\033[35mtheGroups\033[0m' then do this: \033[36m--idp-h-grp theGroup\033[0m
|
827
|
+
|
828
|
+
if the list of groups is separated by '\033[35m%\033[0m' then \033[36m--idp-gsep %\033[0m
|
829
|
+
|
830
|
+
if the middleware is providing a header named '\033[35mAccount\033[0m'
|
831
|
+
and the value is '\033[35malice@forest.net\033[0m' but the username is
|
832
|
+
actually '\033[35mmarisa\033[0m' then do this for each user:
|
833
|
+
\033[36m--idp-hm-usr ^Account^alice@forest.net^marisa\033[0m
|
834
|
+
(the separator '\033[35m^\033[0m' can be any character)
|
835
|
+
|
836
|
+
make ABSOLUTELY SURE that the header can only be set by your
|
837
|
+
middleware and not by clients! and, as an extra precaution,
|
838
|
+
send a header named '\033[36mfinalmasterspark\033[0m' (a secret keyword)
|
839
|
+
and then \033[36m--idp-h-key finalmasterspark\033[0m to require that
|
840
|
+
"""
|
841
|
+
),
|
842
|
+
],
|
757
843
|
[
|
758
844
|
"urlform",
|
759
845
|
"how to handle url-form POSTs",
|
@@ -1011,14 +1097,15 @@ def add_general(ap, nc, srvname):
|
|
1011
1097
|
|
1012
1098
|
def add_qr(ap, tty):
|
1013
1099
|
ap2 = ap.add_argument_group("qr options")
|
1014
|
-
ap2.add_argument("--qr", action="store_true", help="show
|
1015
|
-
ap2.add_argument("--qrs", action="store_true", help="
|
1100
|
+
ap2.add_argument("--qr", action="store_true", help="show QR-code on startup")
|
1101
|
+
ap2.add_argument("--qrs", action="store_true", help="change the QR-code URL to https://")
|
1016
1102
|
ap2.add_argument("--qrl", metavar="PATH", type=u, default="", help="location to include in the url, for example [\033[32mpriv/?pw=hunter2\033[0m]")
|
1017
1103
|
ap2.add_argument("--qri", metavar="PREFIX", type=u, default="", help="select IP which starts with \033[33mPREFIX\033[0m; [\033[32m.\033[0m] to force default IP when mDNS URL would have been used instead")
|
1018
|
-
ap2.add_argument("--qr-fg", metavar="COLOR", type=int, default=0 if tty else 16, help="foreground; try [\033[32m0\033[0m] if the qr-code is unreadable")
|
1104
|
+
ap2.add_argument("--qr-fg", metavar="COLOR", type=int, default=0 if tty else 16, help="foreground; try [\033[32m0\033[0m] or [\033[32m-1\033[0m] if the qr-code is unreadable")
|
1019
1105
|
ap2.add_argument("--qr-bg", metavar="COLOR", type=int, default=229, help="background (white=255)")
|
1020
1106
|
ap2.add_argument("--qrp", metavar="CELLS", type=int, default=4, help="padding (spec says 4 or more, but 1 is usually fine)")
|
1021
1107
|
ap2.add_argument("--qrz", metavar="N", type=int, default=0, help="[\033[32m1\033[0m]=1x, [\033[32m2\033[0m]=2x, [\033[32m0\033[0m]=auto (try [\033[32m2\033[0m] on broken fonts)")
|
1108
|
+
ap2.add_argument("--qr-pin", metavar="N", type=int, default=0, help="sticky/pin the qr-code to always stay on-screen; [\033[32m0\033[0m]=disabled, [\033[32m1\033[0m]=with-url, [\033[32m2\033[0m]=just-qr")
|
1022
1109
|
|
1023
1110
|
|
1024
1111
|
def add_fs(ap):
|
@@ -1048,6 +1135,7 @@ def add_upload(ap):
|
|
1048
1135
|
ap2.add_argument("--put-ck", metavar="ALG", type=u, default="sha512", help="default checksum-hasher for PUT/WebDAV uploads: no / md5 / sha1 / sha256 / sha512 / b2 / blake2 / b2s / blake2s (volflag=put_ck)")
|
1049
1136
|
ap2.add_argument("--bup-ck", metavar="ALG", type=u, default="sha512", help="default checksum-hasher for bup/basic-uploader: no / md5 / sha1 / sha256 / sha512 / b2 / blake2 / b2s / blake2s (volflag=bup_ck)")
|
1050
1137
|
ap2.add_argument("--unpost", metavar="SEC", type=int, default=3600*12, help="grace period where uploads can be deleted by the uploader, even without delete permissions; 0=disabled, default=12h")
|
1138
|
+
ap2.add_argument("--unp-who", metavar="NUM", type=int, default=1, help="clients can undo recent uploads by using the unpost tab (requires \033[33m-e2d\033[0m). [\033[32m0\033[0m] = never allowed (disable feature), [\033[32m1\033[0m] = allow if client has the same IP as the upload AND is using the same account, [\033[32m2\033[0m] = just check the IP, [\033[32m3\033[0m] = just check account-name (volflag=unp_who)")
|
1051
1139
|
ap2.add_argument("--u2abort", metavar="NUM", type=int, default=1, help="clients can abort incomplete uploads by using the unpost tab (requires \033[33m-e2d\033[0m). [\033[32m0\033[0m] = never allowed (disable feature), [\033[32m1\033[0m] = allow if client has the same IP as the upload AND is using the same account, [\033[32m2\033[0m] = just check the IP, [\033[32m3\033[0m] = just check account-name (volflag=u2abort)")
|
1052
1140
|
ap2.add_argument("--blank-wt", metavar="SEC", type=int, default=300, help="file write grace period (any client can write to a blank file last-modified more recently than \033[33mSEC\033[0m seconds ago)")
|
1053
1141
|
ap2.add_argument("--reg-cap", metavar="N", type=int, default=38400, help="max number of uploads to keep in memory when running without \033[33m-e2d\033[0m; roughly 1 MiB RAM per 600")
|
@@ -1141,7 +1229,8 @@ def add_auth(ap):
|
|
1141
1229
|
idp_db = os.path.join(E.cfg, "idp.db")
|
1142
1230
|
ses_db = os.path.join(E.cfg, "sessions.db")
|
1143
1231
|
ap2 = ap.add_argument_group("IdP / identity provider / user authentication options")
|
1144
|
-
ap2.add_argument("--idp-h-usr", metavar="HN", type=u,
|
1232
|
+
ap2.add_argument("--idp-h-usr", metavar="HN", type=u, action="append", help="\033[34mREPEATABLE:\033[0m bypass the copyparty authentication checks if the request-header \033[33mHN\033[0m contains a username to associate the request with (for use with authentik/oauth/...)\n\033[1;31mWARNING:\033[0m if you enable this, make sure clients are unable to specify this header themselves; must be washed away and replaced by a reverse-proxy")
|
1233
|
+
ap2.add_argument("--idp-hm-usr", metavar="TXT", type=u, action="append", help="\033[34mREPEATABLE:\033[0m bypass the copyparty authentication checks if the request-header \033[33mHN\033[0m is provided, and its value exists in a mapping defined by this option; see --help-idp")
|
1145
1234
|
ap2.add_argument("--idp-h-grp", metavar="HN", type=u, default="", help="assume the request-header \033[33mHN\033[0m contains the groupname of the requesting user; can be referenced in config files for group-based access control")
|
1146
1235
|
ap2.add_argument("--idp-h-key", metavar="HN", type=u, default="", help="optional but recommended safeguard; your reverse-proxy will insert a secret header named \033[33mHN\033[0m into all requests, and the other IdP headers will be ignored if this header is not present")
|
1147
1236
|
ap2.add_argument("--idp-gsep", metavar="RE", type=u, default="|:;+,", help="if there are multiple groups in \033[33m--idp-h-grp\033[0m, they are separated by one of the characters in \033[33mRE\033[0m")
|
@@ -1154,7 +1243,11 @@ def add_auth(ap):
|
|
1154
1243
|
ap2.add_argument("--ses-db", metavar="PATH", type=u, default=ses_db, help="where to store the sessions database (if you run multiple copyparty instances, make sure they use different DBs)")
|
1155
1244
|
ap2.add_argument("--ses-len", metavar="CHARS", type=int, default=20, help="session key length; default is 120 bits ((20//4)*4*6)")
|
1156
1245
|
ap2.add_argument("--no-ses", action="store_true", help="disable sessions; use plaintext passwords in cookies")
|
1246
|
+
ap2.add_argument("--grp-all", metavar="NAME", type=u, default="acct", help="the name of the auto-generated group which contains every username which is known")
|
1157
1247
|
ap2.add_argument("--ipu", metavar="CIDR=USR", type=u, action="append", help="\033[34mREPEATABLE:\033[0m users with IP matching \033[33mCIDR\033[0m are auto-authenticated as username \033[33mUSR\033[0m; example: [\033[32m172.16.24.0/24=dave]")
|
1248
|
+
ap2.add_argument("--ipr", metavar="CIDR=USR", type=u, action="append", help="\033[34mREPEATABLE:\033[0m username \033[33mUSR\033[0m can only connect from an IP matching one or more \033[33mCIDR\033[0m (comma-sep.); example: [\033[32m192.168.123.0/24,172.16.0.0/16=dave]")
|
1249
|
+
ap2.add_argument("--have-idp-hdrs", type=u, default="", help=argparse.SUPPRESS)
|
1250
|
+
ap2.add_argument("--have-ipu-or-ipr", type=u, default="", help=argparse.SUPPRESS)
|
1158
1251
|
|
1159
1252
|
|
1160
1253
|
def add_chpw(ap):
|
@@ -1310,6 +1403,7 @@ def add_optouts(ap):
|
|
1310
1403
|
ap2.add_argument("--no-del", action="store_true", help="disable delete operations")
|
1311
1404
|
ap2.add_argument("--no-mv", action="store_true", help="disable move/rename operations")
|
1312
1405
|
ap2.add_argument("--no-cp", action="store_true", help="disable copy operations")
|
1406
|
+
ap2.add_argument("--no-fs-abrt", action="store_true", help="disable ability to abort ongoing copy/move")
|
1313
1407
|
ap2.add_argument("-nth", action="store_true", help="no title hostname; don't show \033[33m--name\033[0m in <title>")
|
1314
1408
|
ap2.add_argument("-nih", action="store_true", help="no info hostname -- don't show in UI")
|
1315
1409
|
ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI")
|
@@ -1331,7 +1425,7 @@ def add_optouts(ap):
|
|
1331
1425
|
def add_safety(ap):
|
1332
1426
|
ap2 = ap.add_argument_group("safety options")
|
1333
1427
|
ap2.add_argument("-s", action="count", default=0, help="increase safety: Disable thumbnails / potentially dangerous software (ffmpeg/pillow/vips), hide partial uploads, avoid crawlers.\n └─Alias of\033[32m --dotpart --no-thumb --no-mtag-ff --no-robots --force-js")
|
1334
|
-
ap2.add_argument("-ss", action="store_true", help="further increase safety: Prevent js-injection, accidental move/delete, broken symlinks, webdav, 404 on 403, ban on excessive 404s.\n └─Alias of\033[32m -s --unpost=0 --no-del --no-mv --hardlink --vague-403 -nih")
|
1428
|
+
ap2.add_argument("-ss", action="store_true", help="further increase safety: Prevent js-injection, accidental move/delete, broken symlinks, webdav requires login, 404 on 403, ban on excessive 404s.\n └─Alias of\033[32m -s --unpost=0 --no-del --no-mv --hardlink --dav-auth --vague-403 -nih")
|
1335
1429
|
ap2.add_argument("-sss", action="store_true", help="further increase safety: Enable logging to disk, scan for dangerous symlinks.\n └─Alias of\033[32m -ss --no-dav --no-logues --no-readme -lo=cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz --ls=**,*,ln,p,r")
|
1336
1430
|
ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, default="", help="do a sanity/safety check of all volumes on startup; arguments \033[33mUSER\033[0m,\033[33mVOL\033[0m,\033[33mFLAGS\033[0m (see \033[33m--help-ls\033[0m); example [\033[32m**,*,ln,p,r\033[0m]")
|
1337
1431
|
ap2.add_argument("--xvol", action="store_true", help="never follow symlinks leaving the volume root, unless the link is into another volume where the user has similar access (volflag=xvol)")
|
@@ -1353,6 +1447,8 @@ def add_safety(ap):
|
|
1353
1447
|
ap2.add_argument("--sus-urls", metavar="R", type=u, default=r"\.php$|(^|/)wp-(admin|content|includes)/", help="URLs which are considered sus / eligible for banning; disable with blank or [\033[32mno\033[0m]")
|
1354
1448
|
ap2.add_argument("--nonsus-urls", metavar="R", type=u, default=r"^(favicon\.ico|robots\.txt)$|^apple-touch-icon|^\.well-known", help="harmless URLs ignored from 404-bans; disable with blank or [\033[32mno\033[0m]")
|
1355
1449
|
ap2.add_argument("--early-ban", action="store_true", help="if a client is banned, reject its connection as soon as possible; not a good idea to enable when proxied behind cloudflare since it could ban your reverse-proxy")
|
1450
|
+
ap2.add_argument("--cookie-nmax", metavar="N", type=int, default=50, help="reject HTTP-request from client if they send more than N cookies")
|
1451
|
+
ap2.add_argument("--cookie-cmax", metavar="N", type=int, default=8192, help="reject HTTP-request from client if more than N characters in Cookie header")
|
1356
1452
|
ap2.add_argument("--aclose", metavar="MIN", type=int, default=10, help="if a client maxes out the server connection limit, downgrade it from connection:keep-alive to connection:close for \033[33mMIN\033[0m minutes (and also kill its active connections) -- disable with 0")
|
1357
1453
|
ap2.add_argument("--loris", metavar="B", type=int, default=60, help="if a client maxes out the server connection limit without sending headers, ban it for \033[33mB\033[0m minutes; disable with [\033[32m0\033[0m]")
|
1358
1454
|
ap2.add_argument("--acao", metavar="V[,V]", type=u, default="*", help="Access-Control-Allow-Origin; list of origins (domains/IPs without port) to accept requests from; [\033[32mhttps://1.2.3.4\033[0m]. Default [\033[32m*\033[0m] allows requests from all sites but removes cookies and http-auth; only ?pw=hunter2 survives")
|
@@ -1419,11 +1515,12 @@ def add_thumbnail(ap):
|
|
1419
1515
|
ap2.add_argument("--no-athumb", action="store_true", help="disable audio thumbnails (spectrograms) (volflag=dathumb)")
|
1420
1516
|
ap2.add_argument("--th-size", metavar="WxH", default="320x256", help="thumbnail res (volflag=thsize)")
|
1421
1517
|
ap2.add_argument("--th-mt", metavar="CORES", type=int, default=CORES, help="num cpu cores to use for generating thumbnails")
|
1422
|
-
ap2.add_argument("--th-convt", metavar="SEC", type=float, default=60.0, help="
|
1518
|
+
ap2.add_argument("--th-convt", metavar="SEC", type=float, default=60.0, help="convert-to-image timeout in seconds (volflag=convt)")
|
1519
|
+
ap2.add_argument("--ac-convt", metavar="SEC", type=float, default=150.0, help="convert-to-audio timeout in seconds (volflag=aconvt)")
|
1423
1520
|
ap2.add_argument("--th-ram-max", metavar="GB", type=float, default=th_ram, help="max memory usage (GiB) permitted by thumbnailer; not very accurate")
|
1424
1521
|
ap2.add_argument("--th-crop", metavar="TXT", type=u, default="y", help="crop thumbnails to 4:3 or keep dynamic height; client can override in UI unless force. [\033[32my\033[0m]=crop, [\033[32mn\033[0m]=nocrop, [\033[32mfy\033[0m]=force-y, [\033[32mfn\033[0m]=force-n (volflag=crop)")
|
1425
1522
|
ap2.add_argument("--th-x3", metavar="TXT", type=u, default="n", help="show thumbs at 3x resolution; client can override in UI unless force. [\033[32my\033[0m]=yes, [\033[32mn\033[0m]=no, [\033[32mfy\033[0m]=force-yes, [\033[32mfn\033[0m]=force-no (volflag=th3x)")
|
1426
|
-
ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,ff", help="image decoders, in order of preference")
|
1523
|
+
ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,raw,ff", help="image decoders, in order of preference")
|
1427
1524
|
ap2.add_argument("--th-no-jpg", action="store_true", help="disable jpg output")
|
1428
1525
|
ap2.add_argument("--th-no-webp", action="store_true", help="disable webp output")
|
1429
1526
|
ap2.add_argument("--th-ff-jpg", action="store_true", help="force jpg output for video thumbs (avoids issues on some FFmpeg builds)")
|
@@ -1432,16 +1529,19 @@ def add_thumbnail(ap):
|
|
1432
1529
|
ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval; 0=disabled")
|
1433
1530
|
ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age -- folders which haven't been poked for longer than \033[33m--th-poke\033[0m seconds will get deleted every \033[33m--th-clean\033[0m seconds")
|
1434
1531
|
ap2.add_argument("--th-covers", metavar="N,N", type=u, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat/look for; enabling \033[33m-e2d\033[0m will make these case-insensitive, and try them as dotfiles (.folder.jpg), and also automatically select thumbnails for all folders that contain pics, even if none match this pattern")
|
1532
|
+
ap2.add_argument("--th-spec-p", metavar="N", type=u, default=1, help="for music, do spectrograms or embedded coverart? [\033[32m0\033[0m]=only-art, [\033[32m1\033[0m]=prefer-art, [\033[32m2\033[0m]=only-spec")
|
1435
1533
|
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
|
1436
1534
|
# https://github.com/libvips/libvips
|
1535
|
+
# https://stackoverflow.com/a/47612661
|
1437
1536
|
# ffmpeg -hide_banner -demuxers | awk '/^ D /{print$2}' | while IFS= read -r x; do ffmpeg -hide_banner -h demuxer=$x; done | grep -E '^Demuxer |extensions:'
|
1438
|
-
ap2.add_argument("--th-r-pil", metavar="T,T", type=u, default="avif,avifs,blp,bmp,cbz,dcx,dds,dib,emf,eps,fits,flc,fli,fpx,gif,heic,heics,heif,heifs,icns,ico,im,j2p,j2k,jp2,jpeg,jpg,jpx,pbm,pcx,pgm,png,pnm,ppm,psd,qoi,sgi,spi,tga,tif,tiff,webp,wmf,xbm,xpm", help="image formats to decode using pillow")
|
1439
|
-
ap2.add_argument("--th-r-vips", metavar="T,T", type=u, default="avif,exr,fit,fits,fts,gif,hdr,heic,jp2,jpeg,jpg,jpx,jxl,nii,pfm,pgm,png,ppm,svg,tif,tiff,webp", help="image formats to decode using pyvips")
|
1440
|
-
ap2.add_argument("--th-r-
|
1537
|
+
ap2.add_argument("--th-r-pil", metavar="T,T", type=u, default="avif,avifs,blp,bmp,cbz,dcx,dds,dib,emf,eps,epub,fits,flc,fli,fpx,gif,heic,heics,heif,heifs,icns,ico,im,j2p,j2k,jp2,jpeg,jpg,jpx,pbm,pcx,pgm,png,pnm,ppm,psd,qoi,sgi,spi,tga,tif,tiff,webp,wmf,xbm,xpm", help="image formats to decode using pillow")
|
1538
|
+
ap2.add_argument("--th-r-vips", metavar="T,T", type=u, default="avif,exr,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,jp2,jpeg,jpg,jpx,jxl,nii,pfm,pgm,png,ppm,svg,tif,tiff,webp", help="image formats to decode using pyvips")
|
1539
|
+
ap2.add_argument("--th-r-raw", metavar="T,T", type=u, default="arw,cr2,cr3,crw,dcr,dng,erf,k25,kdc,mrw,nef,orf,pef,raf,raw,sr2,srf,x3f", help="image formats to decode using rawpy")
|
1540
|
+
ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,cbz,dds,dib,epub,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg")
|
1441
1541
|
ap2.add_argument("--th-r-ffv", metavar="T,T", type=u, default="3gp,asf,av1,avc,avi,flv,h264,h265,hevc,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,ts,vob,webm,wmv", help="video formats to decode using ffmpeg")
|
1442
1542
|
ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bonk,dfpwm,dts,flac,gsm,ilbc,it,itgz,itxz,itz,m4a,mdgz,mdxz,mdz,mo3,mod,mp2,mp3,mpc,mptm,mt2,mulaw,oga,ogg,okt,opus,ra,s3m,s3gz,s3xz,s3z,tak,tta,ulaw,wav,wma,wv,xm,xmgz,xmxz,xmz,xpk", help="audio formats to decode using ffmpeg")
|
1443
1543
|
ap2.add_argument("--th-spec-cnv", metavar="T", type=u, default="it,itgz,itxz,itz,mdgz,mdxz,mdz,mo3,mod,s3m,s3gz,s3xz,s3z,xm,xmgz,xmxz,xmz,xpk", help="audio formats which provoke https://trac.ffmpeg.org/ticket/10797 (huge ram usage for s3xmodit spectrograms)")
|
1444
|
-
ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz, cbz=jpg.cbz", help="audio/image formats to decompress before passing to ffmpeg")
|
1544
|
+
ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz, cbz=jpg.cbz, epub=jpg.epub", help="audio/image formats to decompress before passing to ffmpeg")
|
1445
1545
|
|
1446
1546
|
|
1447
1547
|
def add_transcoding(ap):
|
@@ -1553,13 +1653,14 @@ def add_og(ap):
|
|
1553
1653
|
|
1554
1654
|
|
1555
1655
|
def add_ui(ap, retry):
|
1656
|
+
THEMES = 10
|
1556
1657
|
ap2 = ap.add_argument_group("ui options")
|
1557
1658
|
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
1558
1659
|
ap2.add_argument("--gsel", action="store_true", help="select files in grid by ctrl-click (volflag=gsel)")
|
1559
1660
|
ap2.add_argument("--localtime", action="store_true", help="default to local timezone instead of UTC")
|
1560
|
-
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language
|
1561
|
-
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0
|
1562
|
-
ap2.add_argument("--themes", metavar="NUM", type=int, default=
|
1661
|
+
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language, for example \033[32meng\033[0m / \033[32mnor\033[0m / ...")
|
1662
|
+
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..%d)" % (THEMES - 1,))
|
1663
|
+
ap2.add_argument("--themes", metavar="NUM", type=int, default=THEMES, help="number of themes installed")
|
1563
1664
|
ap2.add_argument("--au-vol", metavar="0-100", type=int, default=50, choices=range(0, 101), help="default audio/video volume percent")
|
1564
1665
|
ap2.add_argument("--sort", metavar="C,C,C", type=u, default="href", help="default sort order, comma-separated column IDs (see header tooltips), prefix with '-' for descending. Examples: \033[32mhref -href ext sz ts tags/Album tags/.tn\033[0m (volflag=sort)")
|
1565
1666
|
ap2.add_argument("--nsort", action="store_true", help="default-enable natural sort of filenames with leading numbers (volflag=nsort)")
|
@@ -1760,20 +1861,7 @@ def main(argv = None) :
|
|
1760
1861
|
|
1761
1862
|
ensure_webdeps()
|
1762
1863
|
|
1763
|
-
|
1764
|
-
supp = args_from_cfg(CFG_DEF[0])
|
1765
|
-
argv.extend(supp)
|
1766
|
-
|
1767
|
-
for k, v in zip(argv[1:], argv[2:]):
|
1768
|
-
if k == "-c" and os.path.isfile(v):
|
1769
|
-
supp = args_from_cfg(v)
|
1770
|
-
argv.extend(supp)
|
1771
|
-
|
1772
|
-
for k in argv[1:]:
|
1773
|
-
v = k[2:]
|
1774
|
-
if k.startswith("-c") and v and os.path.isfile(v):
|
1775
|
-
supp = args_from_cfg(v)
|
1776
|
-
argv.extend(supp)
|
1864
|
+
argv = expand_cfg(argv)
|
1777
1865
|
|
1778
1866
|
deprecated = [
|
1779
1867
|
("--salt", "--warksalt"),
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -874,6 +874,15 @@ class VFS(object):
|
|
874
874
|
return None
|
875
875
|
|
876
876
|
if "xvol" in self.flags:
|
877
|
+
self_ap = self.realpath + os.sep
|
878
|
+
if aps.startswith(self_ap):
|
879
|
+
vp = aps[len(self_ap) :]
|
880
|
+
if ANYWIN:
|
881
|
+
vp = vp.replace(os.sep, "/")
|
882
|
+
vn2, _ = self._find(vp)
|
883
|
+
if self == vn2:
|
884
|
+
return self
|
885
|
+
|
877
886
|
all_aps = self.shr_all_aps or self.root.all_aps
|
878
887
|
|
879
888
|
for vap, vns in all_aps:
|
@@ -1091,6 +1100,9 @@ class AuthSrv(object):
|
|
1091
1100
|
if rejected:
|
1092
1101
|
continue
|
1093
1102
|
|
1103
|
+
if gn == self.args.grp_all:
|
1104
|
+
gn = ""
|
1105
|
+
|
1094
1106
|
# if ap/vp has a user/group placeholder, make sure to keep
|
1095
1107
|
# track so the same user/group is mapped when setting perms;
|
1096
1108
|
# otherwise clear un/gn to indicate it's a regular volume
|
@@ -1200,6 +1212,7 @@ class AuthSrv(object):
|
|
1200
1212
|
self.load_idp_db(bool(self.idp_accs))
|
1201
1213
|
ret = {un: gns[:] for un, gns in self.idp_accs.items()}
|
1202
1214
|
ret.update({zs: [""] for zs in acct if zs not in ret})
|
1215
|
+
grps[self.args.grp_all] = list(ret.keys())
|
1203
1216
|
for gn, uns in grps.items():
|
1204
1217
|
for un in uns:
|
1205
1218
|
try:
|
@@ -1677,6 +1690,9 @@ class AuthSrv(object):
|
|
1677
1690
|
self.log("\n{0}\n{1}{0}".format(t, "\n".join(slns)))
|
1678
1691
|
raise
|
1679
1692
|
|
1693
|
+
self.args.have_idp_hdrs = bool(self.args.idp_h_usr or self.args.idp_hm_usr)
|
1694
|
+
self.args.have_ipu_or_ipr = bool(self.args.ipu or self.args.ipr)
|
1695
|
+
|
1680
1696
|
self.setup_pwhash(acct)
|
1681
1697
|
defpw = acct.copy()
|
1682
1698
|
self.setup_chpw(acct)
|
@@ -1689,7 +1705,7 @@ class AuthSrv(object):
|
|
1689
1705
|
|
1690
1706
|
mount = cased
|
1691
1707
|
|
1692
|
-
if not mount and not self.args.
|
1708
|
+
if not mount and not self.args.have_idp_hdrs:
|
1693
1709
|
# -h says our defaults are CWD at root and read/write for everyone
|
1694
1710
|
axs = AXS(["*"], ["*"], None, None)
|
1695
1711
|
ehint = ""
|
@@ -1861,7 +1877,7 @@ class AuthSrv(object):
|
|
1861
1877
|
|
1862
1878
|
if missing_users:
|
1863
1879
|
zs = ", ".join(k for k in sorted(missing_users))
|
1864
|
-
if self.args.
|
1880
|
+
if self.args.have_idp_hdrs:
|
1865
1881
|
t = "the following users are unknown, and assumed to come from IdP: "
|
1866
1882
|
self.log(t + zs, c=6)
|
1867
1883
|
else:
|
@@ -1872,6 +1888,16 @@ class AuthSrv(object):
|
|
1872
1888
|
if LEELOO_DALLAS in all_users:
|
1873
1889
|
raise Exception("sorry, reserved username: " + LEELOO_DALLAS)
|
1874
1890
|
|
1891
|
+
zsl = []
|
1892
|
+
for usr in list(acct)[:]:
|
1893
|
+
zs = acct[usr].strip()
|
1894
|
+
if not zs:
|
1895
|
+
zs = ub64enc(os.urandom(48)).decode("ascii")
|
1896
|
+
zsl.append(usr)
|
1897
|
+
acct[usr] = zs
|
1898
|
+
if zsl:
|
1899
|
+
self.log("generated random passwords for users %r" % (zsl,), 6)
|
1900
|
+
|
1875
1901
|
seenpwds = {}
|
1876
1902
|
for usr, pwd in acct.items():
|
1877
1903
|
if pwd in seenpwds:
|
@@ -2192,12 +2218,12 @@ class AuthSrv(object):
|
|
2192
2218
|
if vf not in vol.flags:
|
2193
2219
|
vol.flags[vf] = getattr(self.args, ga)
|
2194
2220
|
|
2195
|
-
zs = "forget_ip gid nrand tail_who u2abort u2ow uid ups_who zip_who"
|
2221
|
+
zs = "forget_ip gid nrand tail_who th_spec_p u2abort u2ow uid unp_who ups_who zip_who"
|
2196
2222
|
for k in zs.split():
|
2197
2223
|
if k in vol.flags:
|
2198
2224
|
vol.flags[k] = int(vol.flags[k])
|
2199
2225
|
|
2200
|
-
zs = "convt tail_fd tail_rate tail_tmax"
|
2226
|
+
zs = "aconvt convt tail_fd tail_rate tail_tmax"
|
2201
2227
|
for k in zs.split():
|
2202
2228
|
if k in vol.flags:
|
2203
2229
|
vol.flags[k] = float(vol.flags[k])
|
@@ -2528,7 +2554,7 @@ class AuthSrv(object):
|
|
2528
2554
|
if not self.args.no_voldump:
|
2529
2555
|
self.log(t)
|
2530
2556
|
|
2531
|
-
if have_e2d or self.args.
|
2557
|
+
if have_e2d or self.args.have_idp_hdrs:
|
2532
2558
|
t = self.chk_sqlite_threadsafe()
|
2533
2559
|
if t:
|
2534
2560
|
self.log("\n\033[{}\033[0m\n".format(t))
|
@@ -2817,7 +2843,7 @@ class AuthSrv(object):
|
|
2817
2843
|
def load_idp_db(self, quiet=False) :
|
2818
2844
|
# mutex me
|
2819
2845
|
level = self.args.idp_store
|
2820
|
-
if level < 2 or not self.args.
|
2846
|
+
if level < 2 or not self.args.have_idp_hdrs:
|
2821
2847
|
return
|
2822
2848
|
|
2823
2849
|
|
@@ -2872,7 +2898,7 @@ class AuthSrv(object):
|
|
2872
2898
|
n = []
|
2873
2899
|
q = "insert into us values (?,?,?)"
|
2874
2900
|
accs = list(self.acct)
|
2875
|
-
if self.args.
|
2901
|
+
if self.args.have_idp_hdrs and self.args.idp_cookie:
|
2876
2902
|
accs.extend(self.idp_accs.keys())
|
2877
2903
|
for uname in accs:
|
2878
2904
|
if uname not in ases:
|
copyparty/cfg.py
CHANGED
@@ -68,6 +68,7 @@ def vf_bmap() :
|
|
68
68
|
def vf_vmap() :
|
69
69
|
"""argv-to-volflag: simple values"""
|
70
70
|
ret = {
|
71
|
+
"ac_convt": "aconvt",
|
71
72
|
"no_hash": "nohash",
|
72
73
|
"no_idx": "noidx",
|
73
74
|
"re_maxage": "scan",
|
@@ -111,12 +112,14 @@ def vf_vmap() :
|
|
111
112
|
"tail_tmax",
|
112
113
|
"tail_who",
|
113
114
|
"tcolor",
|
115
|
+
"th_spec_p",
|
114
116
|
"txt_eol",
|
115
117
|
"unlist",
|
116
118
|
"u2abort",
|
117
119
|
"u2ts",
|
118
120
|
"uid",
|
119
121
|
"gid",
|
122
|
+
"unp_who",
|
120
123
|
"ups_who",
|
121
124
|
"zip_who",
|
122
125
|
"zipmaxn",
|
@@ -260,7 +263,9 @@ flagcats = {
|
|
260
263
|
"thsize": "thumbnail res; WxH",
|
261
264
|
"crop": "center-cropping (y/n/fy/fn)",
|
262
265
|
"th3x": "3x resolution (y/n/fy/fn)",
|
263
|
-
"convt": "
|
266
|
+
"convt": "convert-to-image timeout in seconds",
|
267
|
+
"aconvt": "convert-to-audio timeout in seconds",
|
268
|
+
"th_spec_p=1": "make spectrograms? 0=never 1=fallback 2=always",
|
264
269
|
"ext_th=s=/b.png": "use /b.png as thumbnail for file-extension s",
|
265
270
|
},
|
266
271
|
"handlers\n(better explained in --help-handlers)": {
|
@@ -341,6 +346,7 @@ flagcats = {
|
|
341
346
|
"dky": 'allow seeing files (not folders) inside a specific folder\nwith "g" perm, and does not require a valid dirkey to do so',
|
342
347
|
"rss": "allow '?rss' URL suffix (experimental)",
|
343
348
|
"rmagic": "expensive analysis for mimetype accuracy",
|
349
|
+
"unp_who=2": "unpost only if same... 1=ip+name, 2=ip, 3=name",
|
344
350
|
"ups_who=2": "restrict viewing the list of recent uploads",
|
345
351
|
"zip_who=2": "restrict access to download-as-zip/tar",
|
346
352
|
"zipmaxn=9k": "reject download-as-zip if more than 9000 files",
|
copyparty/dxml.py
CHANGED
copyparty/ftpd.py
CHANGED
@@ -92,6 +92,10 @@ class FtpAuth(DummyAuthorizer):
|
|
92
92
|
|
93
93
|
if args.ipu and uname == "*":
|
94
94
|
uname = args.ipu_iu[args.ipu_nm.map(ip)]
|
95
|
+
if args.ipr and uname in args.ipr_u:
|
96
|
+
if not args.ipr_u[uname].map(ip):
|
97
|
+
logging.warning("username [%s] rejected by --ipr", uname)
|
98
|
+
uname = "*"
|
95
99
|
|
96
100
|
if not uname or not (asrv.vfs.aread.get(uname) or asrv.vfs.awrite.get(uname)):
|
97
101
|
g = self.hub.gpwd
|
@@ -281,9 +285,12 @@ class FtpFs(AbstractedFS):
|
|
281
285
|
# returning 550 is library-default and suitable
|
282
286
|
raise FSE("No such file or directory")
|
283
287
|
|
284
|
-
|
285
|
-
|
286
|
-
|
288
|
+
if vfs.realpath:
|
289
|
+
avfs = vfs.chk_ap(ap, st)
|
290
|
+
if not avfs:
|
291
|
+
raise FSE("Permission denied", 1)
|
292
|
+
else:
|
293
|
+
avfs = vfs
|
287
294
|
|
288
295
|
self.cwd = nwd
|
289
296
|
(
|
@@ -398,8 +405,12 @@ class FtpFs(AbstractedFS):
|
|
398
405
|
return st
|
399
406
|
|
400
407
|
def utime(self, path , timeval ) :
|
401
|
-
|
402
|
-
|
408
|
+
try:
|
409
|
+
ap = self.rv2a(path, w=True)[0]
|
410
|
+
return bos.utime(ap, (int(time.time()), int(timeval)))
|
411
|
+
except Exception as ex:
|
412
|
+
logging.error("ftp.utime: %s, %r", ex, ex)
|
413
|
+
raise
|
403
414
|
|
404
415
|
def lstat(self, path ) :
|
405
416
|
ap = self.rv2a(path)[0]
|
@@ -488,7 +499,11 @@ class FtpHandler(FTPHandler):
|
|
488
499
|
def ftp_STOR(self, file , mode = "w") :
|
489
500
|
# Optional[str]
|
490
501
|
vp = join(self.fs.cwd, file).lstrip("/")
|
491
|
-
|
502
|
+
try:
|
503
|
+
ap, vfs, rem = self.fs.v2a(vp, w=True)
|
504
|
+
except Exception as ex:
|
505
|
+
self.respond("550 %s" % (ex,), logging.info)
|
506
|
+
return
|
492
507
|
self.vfs_map[ap] = vp
|
493
508
|
xbu = vfs.flags.get("xbu")
|
494
509
|
if xbu and not runhook(
|