copyparty 1.19.6__py3-none-any.whl → 1.19.8__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/__init__.py +2 -0
- copyparty/__main__.py +48 -19
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +96 -8
- copyparty/bos/bos.py +38 -2
- copyparty/cfg.py +4 -0
- copyparty/ftpd.py +4 -25
- copyparty/httpcli.py +62 -24
- copyparty/httpsrv.py +1 -1
- copyparty/smbd.py +1 -1
- copyparty/svchub.py +27 -1
- copyparty/tcpsrv.py +4 -0
- copyparty/up2k.py +49 -25
- copyparty/util.py +8 -5
- copyparty/web/a/partyfuse.py +19 -11
- copyparty/web/a/u2c.py +6 -4
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.html +1 -1
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/idp.html +5 -5
- copyparty/web/md.html +1 -1
- copyparty/web/md.js.gz +0 -0
- copyparty/web/md2.js.gz +0 -0
- copyparty/web/mde.html +1 -1
- copyparty/web/msg.html +1 -1
- copyparty/web/rups.html +1 -1
- copyparty/web/shares.html +5 -5
- copyparty/web/splash.css.gz +0 -0
- copyparty/web/splash.html +54 -38
- copyparty/web/splash.js.gz +0 -0
- copyparty/web/svcs.html +80 -39
- copyparty/web/svcs.js.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.19.6.dist-info → copyparty-1.19.8.dist-info}/METADATA +8 -1
- {copyparty-1.19.6.dist-info → copyparty-1.19.8.dist-info}/RECORD +41 -41
- {copyparty-1.19.6.dist-info → copyparty-1.19.8.dist-info}/WHEEL +0 -0
- {copyparty-1.19.6.dist-info → copyparty-1.19.8.dist-info}/entry_points.txt +0 -0
- {copyparty-1.19.6.dist-info → copyparty-1.19.8.dist-info}/licenses/LICENSE +0 -0
- {copyparty-1.19.6.dist-info → copyparty-1.19.8.dist-info}/top_level.txt +0 -0
copyparty/__init__.py
CHANGED
copyparty/__main__.py
CHANGED
@@ -36,6 +36,7 @@ from .__init__ import (
|
|
36
36
|
)
|
37
37
|
from .__version__ import CODENAME, S_BUILD_DT, S_VERSION
|
38
38
|
from .authsrv import expand_config_file, split_cfg_ln, upgrade_cfg_fmt
|
39
|
+
from .bos import bos
|
39
40
|
from .cfg import flagcats, onedash
|
40
41
|
from .svchub import SvcHub
|
41
42
|
from .util import (
|
@@ -180,7 +181,7 @@ def init_E(EE ) :
|
|
180
181
|
|
181
182
|
E = EE # pylint: disable=redefined-outer-name
|
182
183
|
|
183
|
-
def get_unixdir()
|
184
|
+
def get_unixdir() :
|
184
185
|
paths = [
|
185
186
|
(os.environ.get, "XDG_CONFIG_HOME"),
|
186
187
|
(os.path.expanduser, "~/.config"),
|
@@ -191,6 +192,8 @@ def init_E(EE ) :
|
|
191
192
|
]
|
192
193
|
errs = []
|
193
194
|
for npath, (pf, pa) in enumerate(paths):
|
195
|
+
priv = npath < 2 # private/trusted location
|
196
|
+
ram = npath > 1 # "nonvolatile"; not semantically same as `not priv`
|
194
197
|
p = ""
|
195
198
|
try:
|
196
199
|
p = pf(pa)
|
@@ -198,17 +201,23 @@ def init_E(EE ) :
|
|
198
201
|
continue
|
199
202
|
|
200
203
|
p = os.path.normpath(p)
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
mkdir = True
|
205
|
-
os.mkdir(p)
|
204
|
+
mkdir = not os.path.isdir(p)
|
205
|
+
if mkdir:
|
206
|
+
os.mkdir(p, 0o700)
|
206
207
|
|
207
208
|
p = os.path.join(p, "copyparty")
|
208
|
-
if not os.path.isdir(p):
|
209
|
-
os.
|
210
|
-
|
211
|
-
|
209
|
+
if not priv and os.path.isdir(p):
|
210
|
+
uid = os.geteuid()
|
211
|
+
if os.stat(p).st_uid != uid:
|
212
|
+
p += ".%s" % (uid,)
|
213
|
+
if os.path.isdir(p) and os.stat(p).st_uid != uid:
|
214
|
+
raise Exception("filesystem has broken unix permissions")
|
215
|
+
try:
|
216
|
+
os.listdir(p)
|
217
|
+
except:
|
218
|
+
os.mkdir(p, 0o700)
|
219
|
+
|
220
|
+
if ram:
|
212
221
|
t = "Using %s/copyparty [%s] for config; filekeys/dirkeys will change on every restart. Consider setting XDG_CONFIG_HOME or giving the unix-user a ~/.config/"
|
213
222
|
errs.append(t % (pa, p))
|
214
223
|
elif mkdir:
|
@@ -220,17 +229,19 @@ def init_E(EE ) :
|
|
220
229
|
if errs:
|
221
230
|
warn(". ".join(errs))
|
222
231
|
|
223
|
-
return p
|
232
|
+
return p, priv
|
224
233
|
except Exception as ex:
|
225
|
-
if p
|
234
|
+
if p:
|
226
235
|
t = "Unable to store config in %s [%s] due to %r"
|
227
236
|
errs.append(t % (pa, p, ex))
|
228
237
|
|
229
|
-
|
238
|
+
t = "could not find a writable path for runtime state:\n> %s"
|
239
|
+
raise Exception(t % ("\n> ".join(errs)))
|
230
240
|
|
231
241
|
E.mod = os.path.dirname(os.path.realpath(__file__))
|
232
242
|
if E.mod.endswith("__init__"):
|
233
243
|
E.mod = os.path.dirname(E.mod)
|
244
|
+
E.mod_ = os.path.join(E.mod, "")
|
234
245
|
|
235
246
|
try:
|
236
247
|
p = os.environ.get("XDG_CONFIG_HOME")
|
@@ -241,7 +252,7 @@ def init_E(EE ) :
|
|
241
252
|
p = os.path.abspath(os.path.realpath(p))
|
242
253
|
p = os.path.join(p, "copyparty")
|
243
254
|
if not os.path.isdir(p):
|
244
|
-
os.mkdir(p)
|
255
|
+
os.mkdir(p, 0o700)
|
245
256
|
os.listdir(p)
|
246
257
|
except:
|
247
258
|
p = ""
|
@@ -254,11 +265,11 @@ def init_E(EE ) :
|
|
254
265
|
elif sys.platform == "darwin":
|
255
266
|
E.cfg = os.path.expanduser("~/Library/Preferences/copyparty")
|
256
267
|
else:
|
257
|
-
E.cfg = get_unixdir()
|
268
|
+
E.cfg, E.scfg = get_unixdir()
|
258
269
|
|
259
270
|
E.cfg = E.cfg.replace("\\", "/")
|
260
271
|
try:
|
261
|
-
|
272
|
+
bos.makedirs(E.cfg, bos.MKD_700)
|
262
273
|
except:
|
263
274
|
if not os.path.isdir(E.cfg):
|
264
275
|
raise
|
@@ -879,6 +890,11 @@ def get_sects():
|
|
879
890
|
middleware and not by clients! and, as an extra precaution,
|
880
891
|
send a header named '\033[36mfinalmasterspark\033[0m' (a secret keyword)
|
881
892
|
and then \033[36m--idp-h-key finalmasterspark\033[0m to require that
|
893
|
+
|
894
|
+
the login/logout links/buttons can be replaced with links
|
895
|
+
going to your IdP's UI; \033[36m--idp-login /login/?redir={dst}\033[0m
|
896
|
+
will expand \033[36m{dst}\033[0m to the URL of the current page, so
|
897
|
+
the IdP can redirect the user back to where they were
|
882
898
|
"""
|
883
899
|
),
|
884
900
|
],
|
@@ -1126,6 +1142,7 @@ def add_general(ap, nc, srvname):
|
|
1126
1142
|
ap2.add_argument("-v", metavar="VOL", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add volume, \033[33mSRC\033[0m:\033[33mDST\033[0m:\033[33mFLAG\033[0m; examples [\033[32m.::r\033[0m], [\033[32m/mnt/nas/music:/music:r:aed\033[0m], see --help-accounts")
|
1127
1143
|
ap2.add_argument("--grp", metavar="G:N,N", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add group, \033[33mNAME\033[0m:\033[33mUSER1\033[0m,\033[33mUSER2\033[0m,\033[33m...\033[0m; example [\033[32madmins:ed,foo,bar\033[0m]")
|
1128
1144
|
ap2.add_argument("--usernames", action="store_true", help="require username and password for login; default is just password")
|
1145
|
+
ap2.add_argument("--chdir", metavar="PATH", type=u, help="change working-directory to \033[33mPATH\033[0m before mapping volumes")
|
1129
1146
|
ap2.add_argument("-ed", action="store_true", help="enable the ?dots url parameter / client option which allows clients to see dotfiles / hidden files (volflag=dots)")
|
1130
1147
|
ap2.add_argument("--urlform", metavar="MODE", type=u, default="print,xm", help="how to handle url-form POSTs; see \033[33m--help-urlform\033[0m")
|
1131
1148
|
ap2.add_argument("--wintitle", metavar="TXT", type=u, default="cpp @ $pub", help="server terminal title, for example [\033[32m$ip-10.1.2.\033[0m] or [\033[32m$ip-]")
|
@@ -1168,6 +1185,7 @@ def add_share(ap):
|
|
1168
1185
|
ap2 = ap.add_argument_group("share-url options")
|
1169
1186
|
ap2.add_argument("--shr", metavar="DIR", type=u, default="", help="toplevel virtual folder for shared files/folders, for example [\033[32m/share\033[0m]")
|
1170
1187
|
ap2.add_argument("--shr-db", metavar="FILE", type=u, default=db_path, help="database to store shares in")
|
1188
|
+
ap2.add_argument("--shr-who", metavar="TXT", type=u, default="auth", help="who can create a share? [\033[32mno\033[0m]=nobody, [\033[32ma\033[0m]=admin-permission, [\033[32mauth\033[0m]=authenticated (volflag=shr_who)")
|
1171
1189
|
ap2.add_argument("--shr-adm", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to view/delete any share")
|
1172
1190
|
ap2.add_argument("--shr-rt", metavar="MIN", type=int, default=1440, help="shares can be revived by their owner if they expired less than MIN minutes ago; [\033[32m60\033[0m]=hour, [\033[32m1440\033[0m]=day, [\033[32m10080\033[0m]=week")
|
1173
1191
|
ap2.add_argument("--shr-v", action="store_true", help="debug")
|
@@ -1284,6 +1302,9 @@ def add_auth(ap):
|
|
1284
1302
|
ap2.add_argument("--idp-store", metavar="N", type=int, default=1, help="how to use \033[33m--idp-db\033[0m; [\033[32m0\033[0m] = entirely disable, [\033[32m1\033[0m] = write-only (effectively disabled), [\033[32m2\033[0m] = remember users, [\033[32m3\033[0m] = remember users and groups.\nNOTE: Will remember and restore the IdP-volumes of all users for all eternity if set to 2 or 3, even when user is deleted from your IdP")
|
1285
1303
|
ap2.add_argument("--idp-adm", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to use /?idp (the cache management UI)")
|
1286
1304
|
ap2.add_argument("--idp-cookie", metavar="S", type=int, default=0, help="generate a session-token for IdP users which is written to cookie \033[33mcppws\033[0m (or \033[33mcppwd\033[0m if plaintext), to reduce the load on the IdP server, lifetime \033[33mS\033[0m seconds.\n └─note: The expiration time is a client hint only; the actual lifetime of the session-token is infinite (until next restart with \033[33m--ses-db\033[0m wiped)")
|
1305
|
+
ap2.add_argument("--idp-login", metavar="L", type=u, default="", help="replace all login-buttons with a link to URL \033[33mL\033[0m (unless \033[32mpw\033[0m is in \033[33m--auth-ord\033[0m then both will be shown); [\033[32m{dst}\033[0m] expands to url of current page")
|
1306
|
+
ap2.add_argument("--idp-login-t", metavar="T", type=u, default="Login with SSO", help="the label/text for the idp-login button")
|
1307
|
+
ap2.add_argument("--idp-logout", metavar="L", type=u, default="", help="replace all logout-buttons with a link to URL \033[33mL\033[0m")
|
1287
1308
|
ap2.add_argument("--auth-ord", metavar="TXT", type=u, default="idp,ipu", help="controls auth precedence; examples: [\033[32mpw,idp,ipu\033[0m], [\033[32mipu,pw,idp\033[0m], see --help-auth-ord")
|
1288
1309
|
ap2.add_argument("--no-bauth", action="store_true", help="disable basic-authentication support; do not accept passwords from the 'Authenticate' header at all. NOTE: This breaks support for the android app")
|
1289
1310
|
ap2.add_argument("--bauth-last", action="store_true", help="keeps basic-authentication enabled, but only as a last-resort; if a cookie is also provided then the cookie wins")
|
@@ -1298,7 +1319,7 @@ def add_auth(ap):
|
|
1298
1319
|
ap2.add_argument("--ao-idp-before-pw", type=u, default="", help=argparse.SUPPRESS)
|
1299
1320
|
ap2.add_argument("--ao-h-before-hm", type=u, default="", help=argparse.SUPPRESS)
|
1300
1321
|
ap2.add_argument("--ao-ipu-wins", type=u, default="", help=argparse.SUPPRESS)
|
1301
|
-
ap2.add_argument("--ao-
|
1322
|
+
ap2.add_argument("--ao-have-pw", type=u, default="", help=argparse.SUPPRESS)
|
1302
1323
|
|
1303
1324
|
|
1304
1325
|
def add_chpw(ap):
|
@@ -1444,6 +1465,7 @@ def add_yolo(ap):
|
|
1444
1465
|
ap2.add_argument("--no-fnugg", action="store_true", help="disable the smoketest for caching-related issues in the web-UI")
|
1445
1466
|
ap2.add_argument("--getmod", action="store_true", help="permit ?move=[...] and ?delete as GET")
|
1446
1467
|
ap2.add_argument("--wo-up-readme", action="store_true", help="allow users with write-only access to upload logues and readmes without adding the _wo_ filename prefix (volflag=wo_up_readme)")
|
1468
|
+
ap2.add_argument("--unsafe-state", action="store_true", help="when one of the emergency fallback locations are used for runtime state ($TMPDIR, /tmp), certain features will be force-disabled for security reasons by default. This option overrides that safeguard and allows unsafe storage of secrets")
|
1447
1469
|
|
1448
1470
|
|
1449
1471
|
def add_optouts(ap):
|
@@ -1457,7 +1479,7 @@ def add_optouts(ap):
|
|
1457
1479
|
ap2.add_argument("--no-fs-abrt", action="store_true", help="disable ability to abort ongoing copy/move")
|
1458
1480
|
ap2.add_argument("-nth", action="store_true", help="no title hostname; don't show \033[33m--name\033[0m in <title>")
|
1459
1481
|
ap2.add_argument("-nih", action="store_true", help="no info hostname -- don't show in UI")
|
1460
|
-
ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI")
|
1482
|
+
ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI. This is the same as --du-who no")
|
1461
1483
|
ap2.add_argument("-nb", action="store_true", help="no powered-by-copyparty branding in UI")
|
1462
1484
|
ap2.add_argument("--zipmaxn", metavar="N", type=u, default="0", help="reject download-as-zip if more than \033[33mN\033[0m files in total; optionally takes a unit suffix: [\033[32m256\033[0m], [\033[32m9K\033[0m], [\033[32m4G\033[0m] (volflag=zipmaxn)")
|
1463
1485
|
ap2.add_argument("--zipmaxs", metavar="SZ", type=u, default="0", help="reject download-as-zip if total download size exceeds \033[33mSZ\033[0m bytes; optionally takes a unit suffix: [\033[32m256M\033[0m], [\033[32m4G\033[0m], [\033[32m2T\033[0m] (volflag=zipmaxs)")
|
@@ -1735,7 +1757,11 @@ def add_ui(ap, retry):
|
|
1735
1757
|
ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty @ --name", help="title / service-name to show in html documents")
|
1736
1758
|
ap2.add_argument("--bname", metavar="TXT", type=u, default="--name", help="server name (displayed in filebrowser document title)")
|
1737
1759
|
ap2.add_argument("--pb-url", metavar="URL", type=u, default=URL_PRJ, help="powered-by link; disable with \033[33m-nb\033[0m")
|
1738
|
-
ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m)")
|
1760
|
+
ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m). This is the same as --ver-who all")
|
1761
|
+
ap2.add_argument("--ver-who", metavar="TXT", type=u, default="no", help="only show version for: [\033[32ma\033[0m]=admin-permission-anywhere, [\033[32mauth\033[0m]=authenticated, [\033[32mall\033[0m]=anyone")
|
1762
|
+
ap2.add_argument("--du-who", metavar="TXT", type=u, default="all", help="only show disk usage for: [\033[32mno\033[0m]=nobody, [\033[32ma\033[0m]=admin-permission, [\033[32mrw\033[0m]=read-write, [\033[32mw\033[0m]=write, [\033[32mauth\033[0m]=authenticated, [\033[32mall\033[0m]=anyone (volflag=du_who)")
|
1763
|
+
ap2.add_argument("--ver-iwho", type=int, default=0, help=argparse.SUPPRESS)
|
1764
|
+
ap2.add_argument("--du-iwho", type=int, default=0, help=argparse.SUPPRESS)
|
1739
1765
|
ap2.add_argument("--k304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable k304 on the controlpanel (workaround for buggy reverse-proxies); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
|
1740
1766
|
ap2.add_argument("--no304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable no304 on the controlpanel (workaround for buggy caching in browsers); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on")
|
1741
1767
|
ap2.add_argument("--ctl-re", metavar="SEC", type=int, default=1, help="the controlpanel Refresh-button will autorefresh every SEC; [\033[32m0\033[0m] = just once")
|
@@ -1983,6 +2009,9 @@ def main(argv = None) :
|
|
1983
2009
|
except:
|
1984
2010
|
sys.exit(1)
|
1985
2011
|
|
2012
|
+
if al.chdir:
|
2013
|
+
os.chdir(al.chdir)
|
2014
|
+
|
1986
2015
|
if al.ansi:
|
1987
2016
|
al.no_ansi = False
|
1988
2017
|
elif not al.no_ansi:
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -424,6 +424,8 @@ class VFS(object):
|
|
424
424
|
|
425
425
|
self.get_dbv = self._get_dbv
|
426
426
|
self.ls = self._ls
|
427
|
+
self.canonical = self._canonical
|
428
|
+
self.dcanonical = self._dcanonical
|
427
429
|
|
428
430
|
def __repr__(self) :
|
429
431
|
return "VFS(%s)" % (
|
@@ -617,7 +619,7 @@ class VFS(object):
|
|
617
619
|
vrem = vjoin(self.vpath[len(dbv.vpath) :].lstrip("/"), vrem)
|
618
620
|
return dbv, vrem
|
619
621
|
|
620
|
-
def
|
622
|
+
def _canonical(self, rem , resolve = True) :
|
621
623
|
"""returns the canonical path (fully-resolved absolute fs path)"""
|
622
624
|
ap = self.realpath
|
623
625
|
if rem:
|
@@ -625,7 +627,7 @@ class VFS(object):
|
|
625
627
|
|
626
628
|
return absreal(ap) if resolve else ap
|
627
629
|
|
628
|
-
def
|
630
|
+
def _dcanonical(self, rem ) :
|
629
631
|
"""resolves until the final component (filename)"""
|
630
632
|
ap = self.realpath
|
631
633
|
if rem:
|
@@ -634,6 +636,42 @@ class VFS(object):
|
|
634
636
|
ad, fn = os.path.split(ap)
|
635
637
|
return os.path.join(absreal(ad), fn)
|
636
638
|
|
639
|
+
def _canonical_shr(self, rem , resolve = True) :
|
640
|
+
"""returns the canonical path (fully-resolved absolute fs path)"""
|
641
|
+
ap = self.realpath
|
642
|
+
if rem:
|
643
|
+
ap += "/" + rem
|
644
|
+
|
645
|
+
rap = absreal(ap)
|
646
|
+
if self.shr_files:
|
647
|
+
vn, rem = self.shr_src
|
648
|
+
chk = absreal(os.path.join(vn.realpath, rem))
|
649
|
+
if chk != rap:
|
650
|
+
# not the dir itself; assert file allowed
|
651
|
+
ad, fn = os.path.split(rap)
|
652
|
+
if chk != ad or fn not in self.shr_files:
|
653
|
+
return "\n\n"
|
654
|
+
|
655
|
+
return rap if resolve else ap
|
656
|
+
|
657
|
+
def _dcanonical_shr(self, rem ) :
|
658
|
+
"""resolves until the final component (filename)"""
|
659
|
+
ap = self.realpath
|
660
|
+
if rem:
|
661
|
+
ap += "/" + rem
|
662
|
+
|
663
|
+
ad, fn = os.path.split(ap)
|
664
|
+
ad = absreal(ad)
|
665
|
+
if self.shr_files:
|
666
|
+
vn, rem = self.shr_src
|
667
|
+
chk = absreal(os.path.join(vn.realpath, rem))
|
668
|
+
if chk != absreal(ap):
|
669
|
+
# not the dir itself; assert file allowed
|
670
|
+
if ad != chk or fn not in self.shr_files:
|
671
|
+
return "\n\n"
|
672
|
+
|
673
|
+
return os.path.join(ad, fn)
|
674
|
+
|
637
675
|
def _ls_nope(
|
638
676
|
self, *a, **ka
|
639
677
|
) :
|
@@ -969,6 +1007,14 @@ class AuthSrv(object):
|
|
969
1007
|
self.indent = ""
|
970
1008
|
self.is_lxc = args.c == ["/z/initcfg"]
|
971
1009
|
|
1010
|
+
self._vf0b = {
|
1011
|
+
"tcolor": self.args.tcolor,
|
1012
|
+
"du_iwho": self.args.du_iwho,
|
1013
|
+
"shr_who": self.args.shr_who if self.args.shr else "no",
|
1014
|
+
}
|
1015
|
+
self._vf0 = self._vf0b.copy()
|
1016
|
+
self._vf0["d2d"] = True
|
1017
|
+
|
972
1018
|
# fwd-decl
|
973
1019
|
self.vfs = VFS(log_func, "", "", "", AXS(), {})
|
974
1020
|
self.acct = {} # uname->pw
|
@@ -1007,7 +1053,10 @@ class AuthSrv(object):
|
|
1007
1053
|
yield prev, True
|
1008
1054
|
|
1009
1055
|
def vf0(self):
|
1010
|
-
return
|
1056
|
+
return self._vf0.copy()
|
1057
|
+
|
1058
|
+
def vf0b(self):
|
1059
|
+
return self._vf0b.copy()
|
1011
1060
|
|
1012
1061
|
def idp_checkin(
|
1013
1062
|
self, broker , uname , gname
|
@@ -1743,12 +1792,15 @@ class AuthSrv(object):
|
|
1743
1792
|
files = os.listdir(E.cfg)
|
1744
1793
|
except:
|
1745
1794
|
files = []
|
1746
|
-
hits = [
|
1795
|
+
hits = [
|
1796
|
+
x
|
1797
|
+
for x in files
|
1798
|
+
if x.lower().endswith(".conf") and not x.startswith(".")
|
1799
|
+
]
|
1747
1800
|
if hits:
|
1748
1801
|
t = "Hint: Found some config files in [%s], but these were not automatically loaded because they are in the wrong place%s %s\n"
|
1749
1802
|
self.log(t % (E.cfg, ehint, ", ".join(hits)), 3)
|
1750
|
-
|
1751
|
-
vfs = VFS(self.log_func, absreal("."), "", "", axs, zvf)
|
1803
|
+
vfs = VFS(self.log_func, absreal("."), "", "", axs, self.vf0b())
|
1752
1804
|
if not axs.uread:
|
1753
1805
|
self.badcfg1 = True
|
1754
1806
|
elif "" not in mount:
|
@@ -2281,6 +2333,11 @@ class AuthSrv(object):
|
|
2281
2333
|
vol.lim.uid = vol.flags["uid"]
|
2282
2334
|
vol.lim.gid = vol.flags["gid"]
|
2283
2335
|
|
2336
|
+
vol.flags["du_iwho"] = n_du_who(vol.flags["du_who"])
|
2337
|
+
|
2338
|
+
if not enshare:
|
2339
|
+
vol.flags["shr_who"] = "no"
|
2340
|
+
|
2284
2341
|
if vol.flags.get("og"):
|
2285
2342
|
self.args.uqe = True
|
2286
2343
|
|
@@ -2723,6 +2780,8 @@ class AuthSrv(object):
|
|
2723
2780
|
|
2724
2781
|
shn.shr_files = set(fns)
|
2725
2782
|
shn.ls = shn._ls_shr
|
2783
|
+
shn.canonical = shn._canonical_shr
|
2784
|
+
shn.dcanonical = shn._dcanonical_shr
|
2726
2785
|
else:
|
2727
2786
|
shn.ls = shn._ls
|
2728
2787
|
|
@@ -2787,6 +2846,7 @@ class AuthSrv(object):
|
|
2787
2846
|
"dcrop": vf["crop"],
|
2788
2847
|
"dth3x": vf["th3x"],
|
2789
2848
|
"u2ts": vf["u2ts"],
|
2849
|
+
"shr_who": vf["shr_who"],
|
2790
2850
|
"frand": bool(vf.get("rand")),
|
2791
2851
|
"lifetime": vf.get("lifetime") or 0,
|
2792
2852
|
"unlist": vf.get("unlist") or "",
|
@@ -2795,11 +2855,13 @@ class AuthSrv(object):
|
|
2795
2855
|
js_htm = {
|
2796
2856
|
"SPINNER": self.args.spinner,
|
2797
2857
|
"s_name": self.args.bname,
|
2858
|
+
"idp_login": self.args.idp_login,
|
2798
2859
|
"have_up2k_idx": "e2d" in vf,
|
2799
2860
|
"have_acode": not self.args.no_acode,
|
2800
2861
|
"have_c2flac": self.args.allow_flac,
|
2801
2862
|
"have_c2wav": self.args.allow_wav,
|
2802
2863
|
"have_shr": self.args.shr,
|
2864
|
+
"shr_who": vf["shr_who"],
|
2803
2865
|
"have_zip": not self.args.no_zip,
|
2804
2866
|
"have_mv": not self.args.no_mv,
|
2805
2867
|
"have_del": not self.args.no_del,
|
@@ -2865,7 +2927,7 @@ class AuthSrv(object):
|
|
2865
2927
|
self.args.ao_idp_before_pw = min(h, hm) < pw
|
2866
2928
|
self.args.ao_h_before_hm = h < hm
|
2867
2929
|
self.args.ao_ipu_wins = ipu == 0
|
2868
|
-
self.args.ao_have_pw = pw < 99
|
2930
|
+
self.args.ao_have_pw = pw < 99 or not self.args.have_idp_hdrs
|
2869
2931
|
|
2870
2932
|
def load_idp_db(self, quiet=False) :
|
2871
2933
|
# mutex me
|
@@ -3456,6 +3518,30 @@ class AuthSrv(object):
|
|
3456
3518
|
self.log("generated config:\n\n" + "\n".join(ret))
|
3457
3519
|
|
3458
3520
|
|
3521
|
+
def n_du_who(s ) :
|
3522
|
+
if s == "all":
|
3523
|
+
return 9
|
3524
|
+
if s == "auth":
|
3525
|
+
return 7
|
3526
|
+
if s == "w":
|
3527
|
+
return 5
|
3528
|
+
if s == "rw":
|
3529
|
+
return 4
|
3530
|
+
if s == "a":
|
3531
|
+
return 3
|
3532
|
+
return 0
|
3533
|
+
|
3534
|
+
|
3535
|
+
def n_ver_who(s ) :
|
3536
|
+
if s == "all":
|
3537
|
+
return 9
|
3538
|
+
if s == "auth":
|
3539
|
+
return 6
|
3540
|
+
if s == "a":
|
3541
|
+
return 3
|
3542
|
+
return 0
|
3543
|
+
|
3544
|
+
|
3459
3545
|
def split_cfg_ln(ln ) :
|
3460
3546
|
# "a, b, c: 3" => {a:true, b:true, c:3}
|
3461
3547
|
ret = {}
|
@@ -3488,7 +3574,9 @@ def expand_config_file(
|
|
3488
3574
|
|
3489
3575
|
if os.path.isdir(fp):
|
3490
3576
|
names = list(sorted(os.listdir(fp)))
|
3491
|
-
cnames = [
|
3577
|
+
cnames = [
|
3578
|
+
x for x in names if x.lower().endswith(".conf") and not x.startswith(".")
|
3579
|
+
]
|
3492
3580
|
if not cnames:
|
3493
3581
|
t = "warning: tried to read config-files from folder '%s' but it does not contain any "
|
3494
3582
|
if names:
|
copyparty/bos/bos.py
CHANGED
@@ -2,15 +2,17 @@
|
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
4
|
import os
|
5
|
+
import time
|
5
6
|
|
6
7
|
from ..util import SYMTIME, fsdec, fsenc
|
7
8
|
from . import path as path
|
8
9
|
|
9
10
|
MKD_755 = {"chmod_d": 0o755}
|
10
11
|
MKD_700 = {"chmod_d": 0o700}
|
12
|
+
UTIME_CLAMPS = ((max, -2147483647), (max, 1), (min, 4294967294), (min, 2147483646))
|
11
13
|
|
12
|
-
_ = (path, MKD_755, MKD_700)
|
13
|
-
__all__ = ["path", "MKD_755", "MKD_700"]
|
14
|
+
_ = (path, MKD_755, MKD_700, UTIME_CLAMPS)
|
15
|
+
__all__ = ["path", "MKD_755", "MKD_700", "UTIME_CLAMPS"]
|
14
16
|
|
15
17
|
# grep -hRiE '(^|[^a-zA-Z_\.-])os\.' . | gsed -r 's/ /\n/g;s/\(/(\n/g' | grep -hRiE '(^|[^a-zA-Z_\.-])os\.' | sort | uniq -c
|
16
18
|
# printf 'os\.(%s)' "$(grep ^def bos/__init__.py | gsed -r 's/^def //;s/\(.*//' | tr '\n' '|' | gsed -r 's/.$//')"
|
@@ -96,6 +98,40 @@ def utime(
|
|
96
98
|
return os.utime(fsenc(p), times)
|
97
99
|
|
98
100
|
|
101
|
+
def utime_c(
|
102
|
+
log , p , ts , follow_symlinks = True, throw = False
|
103
|
+
) :
|
104
|
+
clamp = 0
|
105
|
+
ov = ts
|
106
|
+
bp = fsenc(p)
|
107
|
+
now = int(time.time())
|
108
|
+
while True:
|
109
|
+
try:
|
110
|
+
if SYMTIME:
|
111
|
+
os.utime(bp, (now, ts), follow_symlinks=follow_symlinks)
|
112
|
+
else:
|
113
|
+
os.utime(bp, (now, ts))
|
114
|
+
if clamp:
|
115
|
+
t = "filesystem rejected utime(%r); clamped %s to %s"
|
116
|
+
log(t % (p, ov, ts))
|
117
|
+
return ts
|
118
|
+
except Exception as ex:
|
119
|
+
pv = ts
|
120
|
+
while clamp < len(UTIME_CLAMPS):
|
121
|
+
fun, cv = UTIME_CLAMPS[clamp]
|
122
|
+
ts = fun(ts, cv)
|
123
|
+
clamp += 1
|
124
|
+
if ts != pv:
|
125
|
+
break
|
126
|
+
if clamp >= len(UTIME_CLAMPS):
|
127
|
+
if throw:
|
128
|
+
raise
|
129
|
+
else:
|
130
|
+
t = "could not utime(%r) to %s; %s, %r"
|
131
|
+
log(t % (p, ov, ex, ex))
|
132
|
+
return None
|
133
|
+
|
134
|
+
|
99
135
|
if hasattr(os, "lstat"):
|
100
136
|
|
101
137
|
def lstat(p ) :
|
copyparty/cfg.py
CHANGED
@@ -84,6 +84,7 @@ def vf_vmap() :
|
|
84
84
|
"chmod_d",
|
85
85
|
"chmod_f",
|
86
86
|
"dbd",
|
87
|
+
"du_who",
|
87
88
|
"forget_ip",
|
88
89
|
"hsortn",
|
89
90
|
"html_head",
|
@@ -107,6 +108,7 @@ def vf_vmap() :
|
|
107
108
|
"put_name",
|
108
109
|
"mv_retry",
|
109
110
|
"rm_retry",
|
111
|
+
"shr_who",
|
110
112
|
"sort",
|
111
113
|
"tail_fd",
|
112
114
|
"tail_rate",
|
@@ -296,6 +298,7 @@ flagcats = {
|
|
296
298
|
"html_head=TXT": "includes TXT in the <head>, or @PATH for file at PATH",
|
297
299
|
"tcolor=#fc0": "theme color (a hint for webbrowsers, discord, etc.)",
|
298
300
|
"nodirsz": "don't show total folder size",
|
301
|
+
"du_who=all": "show disk-usage info to everyone",
|
299
302
|
"robots": "allows indexing by search engines (default)",
|
300
303
|
"norobots": "kindly asks search engines to leave",
|
301
304
|
"unlistcr": "don't list read-access in controlpanel",
|
@@ -348,6 +351,7 @@ flagcats = {
|
|
348
351
|
"dky": 'allow seeing files (not folders) inside a specific folder\nwith "g" perm, and does not require a valid dirkey to do so',
|
349
352
|
"rss": "allow '?rss' URL suffix (experimental)",
|
350
353
|
"rmagic": "expensive analysis for mimetype accuracy",
|
354
|
+
"shr_who=auth": "who can create shares? no/auth/a",
|
351
355
|
"unp_who=2": "unpost only if same... 1=ip+name, 2=ip, 3=name",
|
352
356
|
"ups_who=2": "restrict viewing the list of recent uploads",
|
353
357
|
"zip_who=2": "restrict access to download-as-zip/tar",
|
copyparty/ftpd.py
CHANGED
@@ -148,10 +148,6 @@ class FtpFs(AbstractedFS):
|
|
148
148
|
self.cwd = "/" # pyftpdlib convention of leading slash
|
149
149
|
self.root = "/var/lib/empty"
|
150
150
|
|
151
|
-
self.can_read = self.can_write = self.can_move = False
|
152
|
-
self.can_delete = self.can_get = self.can_upget = False
|
153
|
-
self.can_admin = self.can_dot = False
|
154
|
-
|
155
151
|
self.listdirinfo = self.listdir
|
156
152
|
self.chdir(".")
|
157
153
|
|
@@ -214,7 +210,7 @@ class FtpFs(AbstractedFS):
|
|
214
210
|
m = False,
|
215
211
|
d = False,
|
216
212
|
) :
|
217
|
-
return self.v2a(
|
213
|
+
return self.v2a(join(self.cwd, vpath), r, w, m, d)
|
218
214
|
|
219
215
|
def ftp2fs(self, ftppath ) :
|
220
216
|
# return self.v2a(ftppath)
|
@@ -293,16 +289,6 @@ class FtpFs(AbstractedFS):
|
|
293
289
|
avfs = vfs
|
294
290
|
|
295
291
|
self.cwd = nwd
|
296
|
-
(
|
297
|
-
self.can_read,
|
298
|
-
self.can_write,
|
299
|
-
self.can_move,
|
300
|
-
self.can_delete,
|
301
|
-
self.can_get,
|
302
|
-
self.can_upget,
|
303
|
-
self.can_admin,
|
304
|
-
self.can_dot,
|
305
|
-
) = avfs.can_access("", self.h.uname)
|
306
292
|
|
307
293
|
def mkdir(self, path ) :
|
308
294
|
ap, vfs, _ = self.rv2a(path, w=True)
|
@@ -325,7 +311,7 @@ class FtpFs(AbstractedFS):
|
|
325
311
|
vfs_ls = [x[0] for x in vfs_ls1]
|
326
312
|
vfs_ls.extend(vfs_virt.keys())
|
327
313
|
|
328
|
-
if not
|
314
|
+
if self.uname not in vfs.axs.udot:
|
329
315
|
vfs_ls = exclude_dotfiles(vfs_ls)
|
330
316
|
|
331
317
|
vfs_ls.sort()
|
@@ -373,9 +359,6 @@ class FtpFs(AbstractedFS):
|
|
373
359
|
raise FSE(str(ex))
|
374
360
|
|
375
361
|
def rename(self, src , dst ) :
|
376
|
-
if not self.can_move:
|
377
|
-
raise FSE("Not allowed for user " + self.h.uname)
|
378
|
-
|
379
362
|
if self.args.no_mv:
|
380
363
|
raise FSE("The rename/move feature is disabled in server config")
|
381
364
|
|
@@ -405,12 +388,8 @@ class FtpFs(AbstractedFS):
|
|
405
388
|
return st
|
406
389
|
|
407
390
|
def utime(self, path , timeval ) :
|
408
|
-
|
409
|
-
|
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
|
391
|
+
ap = self.rv2a(path, w=True)[0]
|
392
|
+
bos.utime_c(logging.warning, ap, int(timeval), False)
|
414
393
|
|
415
394
|
def lstat(self, path ) :
|
416
395
|
ap = self.rv2a(path)[0]
|