copyparty 1.16.15__tar.gz → 1.16.17__tar.gz
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-1.16.15 → copyparty-1.16.17}/PKG-INFO +12 -2
- {copyparty-1.16.15 → copyparty-1.16.17}/README.md +11 -1
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/__main__.py +15 -40
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/__version__.py +2 -2
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/authsrv.py +145 -63
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/cfg.py +11 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/fsutil.py +7 -5
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/httpcli.py +68 -32
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/svchub.py +2 -2
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/up2k.py +14 -6
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/util.py +32 -0
- copyparty-1.16.17/copyparty/web/browser.js.gz +0 -0
- copyparty-1.16.17/copyparty/web/up2k.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/util.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty.egg-info/PKG-INFO +12 -2
- copyparty-1.16.15/copyparty/web/browser.js.gz +0 -0
- copyparty-1.16.15/copyparty/web/up2k.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/LICENSE +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/__init__.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/bos/__init__.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/bos/bos.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/bos/path.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/broker_mp.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/broker_mpw.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/broker_thr.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/broker_util.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/cert.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/dxml.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/ftpd.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/httpconn.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/httpsrv.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/ico.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/mdns.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/metrics.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/mtag.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/multicast.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/pwhash.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/res/COPYING.txt +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/res/__init__.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/res/insecure.pem +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/smbd.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/ssdp.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/star.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/__init__.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/dnslib/__init__.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/dnslib/bimap.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/dnslib/bit.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/dnslib/buffer.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/dnslib/dns.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/dnslib/label.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/dnslib/lex.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/dnslib/ranges.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/ifaddr/__init__.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/ifaddr/_posix.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/ifaddr/_shared.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/ifaddr/_win32.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/qrcodegen.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/stolen/surrogateescape.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/sutil.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/szip.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/tcpsrv.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/tftpd.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/th_cli.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/th_srv.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/u2idx.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/a/__init__.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/a/partyfuse.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/a/u2c.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/a/webdav-cfg.bat +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/baguettebox.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/browser.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/browser.html +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/browser2.html +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/cf.html +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/dbg-audio.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/dd/2.png +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/dd/3.png +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/dd/4.png +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/dd/5.png +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/dd/__init__.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/__init__.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/busy.mp3.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/easymde.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/easymde.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/fuse.py +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/marked.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/mini-fa.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/mini-fa.woff +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/prism.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/prism.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/prismd.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/scp.woff2 +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/sha512.ac.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/deps/sha512.hw.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/md.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/md.html +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/md.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/md2.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/md2.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/mde.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/mde.html +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/mde.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/msg.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/msg.html +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/rups.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/rups.html +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/rups.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/shares.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/shares.html +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/shares.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/splash.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/splash.html +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/splash.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/svcs.html +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/svcs.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/ui.css.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty/web/w.hash.js.gz +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty.egg-info/SOURCES.txt +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty.egg-info/dependency_links.txt +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty.egg-info/entry_points.txt +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty.egg-info/requires.txt +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/copyparty.egg-info/top_level.txt +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/pyproject.toml +0 -0
- {copyparty-1.16.15 → copyparty-1.16.17}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.16.
|
3
|
+
Version: 1.16.17
|
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
|
@@ -157,6 +157,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
157
157
|
* [custom mimetypes](#custom-mimetypes) - change the association of a file extension
|
158
158
|
* [GDPR compliance](#GDPR-compliance) - imagine using copyparty professionally...
|
159
159
|
* [feature chickenbits](#feature-chickenbits) - buggy feature? rip it out
|
160
|
+
* [feature beefybits](#feature-beefybits) - force-enable incompatible features
|
160
161
|
* [packages](#packages) - the party might be closer than you think
|
161
162
|
* [arch package](#arch-package) - now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
|
162
163
|
* [fedora package](#fedora-package) - does not exist yet
|
@@ -1893,7 +1894,7 @@ tell search engines you don't wanna be indexed, either using the good old [robo
|
|
1893
1894
|
* volflag `[...]:c,norobots` does the same thing for that single volume
|
1894
1895
|
* volflag `[...]:c,robots` ALLOWS search-engine crawling for that volume, even if `--no-robots` is set globally
|
1895
1896
|
|
1896
|
-
also, `--force-js` disables the plain HTML folder listing, making things harder to parse for search engines
|
1897
|
+
also, `--force-js` disables the plain HTML folder listing, making things harder to parse for *some* search engines -- note that crawlers which understand javascript (such as google) will not be affected
|
1897
1898
|
|
1898
1899
|
|
1899
1900
|
## themes
|
@@ -2194,6 +2195,15 @@ buggy feature? rip it out by setting any of the following environment variables
|
|
2194
2195
|
example: `PRTY_NO_IFADDR=1 python3 copyparty-sfx.py`
|
2195
2196
|
|
2196
2197
|
|
2198
|
+
### feature beefybits
|
2199
|
+
|
2200
|
+
force-enable features with known issues on your OS/env by setting any of the following environment variables, also affectionately known as `fuckitbits` or `hail-mary-bits`
|
2201
|
+
|
2202
|
+
| env-var | what it does |
|
2203
|
+
| ------------------------ | ------------ |
|
2204
|
+
| `PRTY_FORCE_MP` | force-enable multiprocessing (real multithreading) on MacOS and other broken platforms |
|
2205
|
+
|
2206
|
+
|
2197
2207
|
# packages
|
2198
2208
|
|
2199
2209
|
the party might be closer than you think
|
@@ -100,6 +100,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
100
100
|
* [custom mimetypes](#custom-mimetypes) - change the association of a file extension
|
101
101
|
* [GDPR compliance](#GDPR-compliance) - imagine using copyparty professionally...
|
102
102
|
* [feature chickenbits](#feature-chickenbits) - buggy feature? rip it out
|
103
|
+
* [feature beefybits](#feature-beefybits) - force-enable incompatible features
|
103
104
|
* [packages](#packages) - the party might be closer than you think
|
104
105
|
* [arch package](#arch-package) - now [available on aur](https://aur.archlinux.org/packages/copyparty) maintained by [@icxes](https://github.com/icxes)
|
105
106
|
* [fedora package](#fedora-package) - does not exist yet
|
@@ -1836,7 +1837,7 @@ tell search engines you don't wanna be indexed, either using the good old [robo
|
|
1836
1837
|
* volflag `[...]:c,norobots` does the same thing for that single volume
|
1837
1838
|
* volflag `[...]:c,robots` ALLOWS search-engine crawling for that volume, even if `--no-robots` is set globally
|
1838
1839
|
|
1839
|
-
also, `--force-js` disables the plain HTML folder listing, making things harder to parse for search engines
|
1840
|
+
also, `--force-js` disables the plain HTML folder listing, making things harder to parse for *some* search engines -- note that crawlers which understand javascript (such as google) will not be affected
|
1840
1841
|
|
1841
1842
|
|
1842
1843
|
## themes
|
@@ -2137,6 +2138,15 @@ buggy feature? rip it out by setting any of the following environment variables
|
|
2137
2138
|
example: `PRTY_NO_IFADDR=1 python3 copyparty-sfx.py`
|
2138
2139
|
|
2139
2140
|
|
2141
|
+
### feature beefybits
|
2142
|
+
|
2143
|
+
force-enable features with known issues on your OS/env by setting any of the following environment variables, also affectionately known as `fuckitbits` or `hail-mary-bits`
|
2144
|
+
|
2145
|
+
| env-var | what it does |
|
2146
|
+
| ------------------------ | ------------ |
|
2147
|
+
| `PRTY_FORCE_MP` | force-enable multiprocessing (real multithreading) on MacOS and other broken platforms |
|
2148
|
+
|
2149
|
+
|
2140
2150
|
# packages
|
2141
2151
|
|
2142
2152
|
the party might be closer than you think
|
@@ -65,6 +65,7 @@ from .util import (
|
|
65
65
|
load_resource,
|
66
66
|
min_ex,
|
67
67
|
pybin,
|
68
|
+
read_utf8,
|
68
69
|
termsize,
|
69
70
|
wrap,
|
70
71
|
)
|
@@ -249,8 +250,7 @@ def get_srvname(verbose) :
|
|
249
250
|
if verbose:
|
250
251
|
lprint("using hostname from {}\n".format(fp))
|
251
252
|
try:
|
252
|
-
|
253
|
-
ret = f.read().decode("utf-8", "replace").strip()
|
253
|
+
return read_utf8(None, fp, True).strip()
|
254
254
|
except:
|
255
255
|
ret = ""
|
256
256
|
namelen = 5
|
@@ -259,47 +259,18 @@ def get_srvname(verbose) :
|
|
259
259
|
ret = re.sub("[234567=]", "", ret)[:namelen]
|
260
260
|
with open(fp, "wb") as f:
|
261
261
|
f.write(ret.encode("utf-8") + b"\n")
|
262
|
-
|
263
|
-
return ret
|
264
|
-
|
265
|
-
|
266
|
-
def get_fk_salt() :
|
267
|
-
fp = os.path.join(E.cfg, "fk-salt.txt")
|
268
|
-
try:
|
269
|
-
with open(fp, "rb") as f:
|
270
|
-
ret = f.read().strip()
|
271
|
-
except:
|
272
|
-
ret = b64enc(os.urandom(18))
|
273
|
-
with open(fp, "wb") as f:
|
274
|
-
f.write(ret + b"\n")
|
275
|
-
|
276
|
-
return ret.decode("utf-8")
|
277
|
-
|
278
|
-
|
279
|
-
def get_dk_salt() :
|
280
|
-
fp = os.path.join(E.cfg, "dk-salt.txt")
|
281
|
-
try:
|
282
|
-
with open(fp, "rb") as f:
|
283
|
-
ret = f.read().strip()
|
284
|
-
except:
|
285
|
-
ret = b64enc(os.urandom(30))
|
286
|
-
with open(fp, "wb") as f:
|
287
|
-
f.write(ret + b"\n")
|
288
|
-
|
289
|
-
return ret.decode("utf-8")
|
262
|
+
return ret
|
290
263
|
|
291
264
|
|
292
|
-
def
|
293
|
-
fp = os.path.join(E.cfg, "
|
265
|
+
def get_salt(name , nbytes ) :
|
266
|
+
fp = os.path.join(E.cfg, "%s-salt.txt" % (name,))
|
294
267
|
try:
|
295
|
-
|
296
|
-
ret = f.read().strip()
|
268
|
+
return read_utf8(None, fp, True).strip()
|
297
269
|
except:
|
298
|
-
ret = b64enc(os.urandom(
|
270
|
+
ret = b64enc(os.urandom(nbytes))
|
299
271
|
with open(fp, "wb") as f:
|
300
272
|
f.write(ret + b"\n")
|
301
|
-
|
302
|
-
return ret.decode("utf-8")
|
273
|
+
return ret.decode("utf-8")
|
303
274
|
|
304
275
|
|
305
276
|
def ensure_locale() :
|
@@ -1257,6 +1228,10 @@ def add_optouts(ap):
|
|
1257
1228
|
ap2.add_argument("-nih", action="store_true", help="no info hostname -- don't show in UI")
|
1258
1229
|
ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI")
|
1259
1230
|
ap2.add_argument("-nb", action="store_true", help="no powered-by-copyparty branding in UI")
|
1231
|
+
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)")
|
1232
|
+
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)")
|
1233
|
+
ap2.add_argument("--zipmaxt", metavar="TXT", type=u, default="", help="custom errormessage when download size exceeds max (volflag=zipmaxt)")
|
1234
|
+
ap2.add_argument("--zipmaxu", action="store_true", help="authenticated users bypass the zip size limit (volflag=zipmaxu)")
|
1260
1235
|
ap2.add_argument("--zip-who", metavar="LVL", type=int, default=3, help="who can download as zip/tar? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=authenticated-with-read-access, [\033[32m3\033[0m]=everyone-with-read-access (volflag=zip_who)\n\033[1;31mWARNING:\033[0m if a nested volume has a more restrictive value than a parent volume, then this will be \033[33mignored\033[0m if the download is initiated from the parent, more lenient volume")
|
1261
1236
|
ap2.add_argument("--no-zip", action="store_true", help="disable download as zip/tar; same as \033[33m--zip-who=0\033[0m")
|
1262
1237
|
ap2.add_argument("--no-tarcmp", action="store_true", help="disable download as compressed tar (?tar=gz, ?tar=bz2, ?tar=xz, ?tar=gz:9, ...)")
|
@@ -1544,9 +1519,9 @@ def run_argparse(
|
|
1544
1519
|
|
1545
1520
|
cert_path = os.path.join(E.cfg, "cert.pem")
|
1546
1521
|
|
1547
|
-
fk_salt =
|
1548
|
-
dk_salt =
|
1549
|
-
ah_salt =
|
1522
|
+
fk_salt = get_salt("fk", 18)
|
1523
|
+
dk_salt = get_salt("dk", 30)
|
1524
|
+
ah_salt = get_salt("ah", 18)
|
1550
1525
|
|
1551
1526
|
# alpine peaks at 5 threads for some reason,
|
1552
1527
|
# all others scale past that (but try to avoid SMT),
|
@@ -33,6 +33,7 @@ from .util import (
|
|
33
33
|
get_df,
|
34
34
|
humansize,
|
35
35
|
odfusion,
|
36
|
+
read_utf8,
|
36
37
|
relchk,
|
37
38
|
statdir,
|
38
39
|
ub64enc,
|
@@ -64,6 +65,8 @@ SSEELOG = " ({})".format(SEE_LOG)
|
|
64
65
|
BAD_CFG = "invalid config; {}".format(SEE_LOG)
|
65
66
|
SBADCFG = " ({})".format(BAD_CFG)
|
66
67
|
|
68
|
+
PTN_U_GRP = re.compile(r"\$\{u%([+-])([^}]+)\}")
|
69
|
+
|
67
70
|
|
68
71
|
class CfgEx(Exception):
|
69
72
|
pass
|
@@ -335,22 +338,26 @@ class VFS(object):
|
|
335
338
|
log ,
|
336
339
|
realpath ,
|
337
340
|
vpath ,
|
341
|
+
vpath0 ,
|
338
342
|
axs ,
|
339
343
|
flags ,
|
340
344
|
) :
|
341
345
|
self.log = log
|
342
346
|
self.realpath = realpath # absolute path on host filesystem
|
343
347
|
self.vpath = vpath # absolute path in the virtual filesystem
|
348
|
+
self.vpath0 = vpath0 # original vpath (before idp expansion)
|
344
349
|
self.axs = axs
|
345
350
|
self.flags = flags # config options
|
346
351
|
self.root = self
|
347
352
|
self.dev = 0 # st_dev
|
353
|
+
self.badcfg1 = False
|
348
354
|
self.nodes = {} # child nodes
|
349
355
|
self.histtab = {} # all realpath->histpath
|
350
356
|
self.dbv = None # closest full/non-jump parent
|
351
357
|
self.lim = None # upload limits; only set for dbv
|
352
358
|
self.shr_src = None # source vfs+rem of a share
|
353
359
|
self.shr_files = set() # filenames to include from shr_src
|
360
|
+
self.shr_owner = "" # uname
|
354
361
|
self.aread = {}
|
355
362
|
self.awrite = {}
|
356
363
|
self.amove = {}
|
@@ -368,7 +375,7 @@ class VFS(object):
|
|
368
375
|
vp = vpath + ("/" if vpath else "")
|
369
376
|
self.histpath = os.path.join(realpath, ".hist") # db / thumbcache
|
370
377
|
self.all_vols = {vpath: self} # flattened recursive
|
371
|
-
self.all_nodes = {vpath: self} # also jumpvols
|
378
|
+
self.all_nodes = {vpath: self} # also jumpvols/shares
|
372
379
|
self.all_aps = [(rp, self)]
|
373
380
|
self.all_vps = [(vp, self)]
|
374
381
|
else:
|
@@ -408,7 +415,7 @@ class VFS(object):
|
|
408
415
|
for v in self.nodes.values():
|
409
416
|
v.get_all_vols(vols, nodes, aps, vps)
|
410
417
|
|
411
|
-
def add(self, src , dst ) :
|
418
|
+
def add(self, src , dst , dst0 ) :
|
412
419
|
"""get existing, or add new path to the vfs"""
|
413
420
|
assert src == "/" or not src.endswith("/") # nosec
|
414
421
|
assert not dst.endswith("/") # nosec
|
@@ -416,20 +423,22 @@ class VFS(object):
|
|
416
423
|
if "/" in dst:
|
417
424
|
# requires breadth-first population (permissions trickle down)
|
418
425
|
name, dst = dst.split("/", 1)
|
426
|
+
name0, dst0 = dst0.split("/", 1)
|
419
427
|
if name in self.nodes:
|
420
428
|
# exists; do not manipulate permissions
|
421
|
-
return self.nodes[name].add(src, dst)
|
429
|
+
return self.nodes[name].add(src, dst, dst0)
|
422
430
|
|
423
431
|
vn = VFS(
|
424
432
|
self.log,
|
425
433
|
os.path.join(self.realpath, name) if self.realpath else "",
|
426
434
|
"{}/{}".format(self.vpath, name).lstrip("/"),
|
435
|
+
"{}/{}".format(self.vpath0, name0).lstrip("/"),
|
427
436
|
self.axs,
|
428
437
|
self._copy_flags(name),
|
429
438
|
)
|
430
439
|
vn.dbv = self.dbv or self
|
431
440
|
self.nodes[name] = vn
|
432
|
-
return vn.add(src, dst)
|
441
|
+
return vn.add(src, dst, dst0)
|
433
442
|
|
434
443
|
if dst in self.nodes:
|
435
444
|
# leaf exists; return as-is
|
@@ -437,7 +446,8 @@ class VFS(object):
|
|
437
446
|
|
438
447
|
# leaf does not exist; create and keep permissions blank
|
439
448
|
vp = "{}/{}".format(self.vpath, dst).lstrip("/")
|
440
|
-
|
449
|
+
vp0 = "{}/{}".format(self.vpath0, dst0).lstrip("/")
|
450
|
+
vn = VFS(self.log, src, vp, vp0, AXS(), {})
|
441
451
|
vn.dbv = self.dbv or self
|
442
452
|
self.nodes[dst] = vn
|
443
453
|
return vn
|
@@ -854,7 +864,7 @@ class AuthSrv(object):
|
|
854
864
|
self.indent = ""
|
855
865
|
|
856
866
|
# fwd-decl
|
857
|
-
self.vfs = VFS(log_func, "", "", AXS(), {})
|
867
|
+
self.vfs = VFS(log_func, "", "", "", AXS(), {})
|
858
868
|
self.acct = {} # uname->pw
|
859
869
|
self.iacct = {} # pw->uname
|
860
870
|
self.ases = {} # uname->session
|
@@ -922,7 +932,7 @@ class AuthSrv(object):
|
|
922
932
|
self,
|
923
933
|
src ,
|
924
934
|
dst ,
|
925
|
-
mount
|
935
|
+
mount ,
|
926
936
|
daxs ,
|
927
937
|
mflags ,
|
928
938
|
un_gns ,
|
@@ -938,12 +948,24 @@ class AuthSrv(object):
|
|
938
948
|
un_gn = [("", "")]
|
939
949
|
|
940
950
|
for un, gn in un_gn:
|
951
|
+
m = PTN_U_GRP.search(dst0)
|
952
|
+
if m:
|
953
|
+
req, gnc = m.groups()
|
954
|
+
hit = gnc in (un_gns.get(un) or [])
|
955
|
+
if req == "+":
|
956
|
+
if not hit:
|
957
|
+
continue
|
958
|
+
elif hit:
|
959
|
+
continue
|
960
|
+
|
941
961
|
# if ap/vp has a user/group placeholder, make sure to keep
|
942
962
|
# track so the same user/group is mapped when setting perms;
|
943
963
|
# otherwise clear un/gn to indicate it's a regular volume
|
944
964
|
|
945
965
|
src1 = src0.replace("${u}", un or "\n")
|
946
966
|
dst1 = dst0.replace("${u}", un or "\n")
|
967
|
+
src1 = PTN_U_GRP.sub(un or "\n", src1)
|
968
|
+
dst1 = PTN_U_GRP.sub(un or "\n", dst1)
|
947
969
|
if src0 == src1 and dst0 == dst1:
|
948
970
|
un = ""
|
949
971
|
|
@@ -960,7 +982,7 @@ class AuthSrv(object):
|
|
960
982
|
continue
|
961
983
|
visited.add(label)
|
962
984
|
|
963
|
-
src, dst = self._map_volume(src, dst, mount, daxs, mflags)
|
985
|
+
src, dst = self._map_volume(src, dst, dst0, mount, daxs, mflags)
|
964
986
|
if src:
|
965
987
|
ret.append((src, dst, un, gn))
|
966
988
|
if un or gn:
|
@@ -972,7 +994,8 @@ class AuthSrv(object):
|
|
972
994
|
self,
|
973
995
|
src ,
|
974
996
|
dst ,
|
975
|
-
|
997
|
+
dst0 ,
|
998
|
+
mount ,
|
976
999
|
daxs ,
|
977
1000
|
mflags ,
|
978
1001
|
) :
|
@@ -982,13 +1005,13 @@ class AuthSrv(object):
|
|
982
1005
|
|
983
1006
|
if dst in mount:
|
984
1007
|
t = "multiple filesystem-paths mounted at [/{}]:\n [{}]\n [{}]"
|
985
|
-
self.log(t.format(dst, mount[dst], src), c=1)
|
1008
|
+
self.log(t.format(dst, mount[dst][0], src), c=1)
|
986
1009
|
raise Exception(BAD_CFG)
|
987
1010
|
|
988
1011
|
if src in mount.values():
|
989
1012
|
t = "filesystem-path [{}] mounted in multiple locations:"
|
990
1013
|
t = t.format(src)
|
991
|
-
for v in [k for k, v in mount.items() if v == src] + [dst]:
|
1014
|
+
for v in [k for k, v in mount.items() if v[0] == src] + [dst]:
|
992
1015
|
t += "\n /{}".format(v)
|
993
1016
|
|
994
1017
|
self.log(t, c=3)
|
@@ -997,7 +1020,7 @@ class AuthSrv(object):
|
|
997
1020
|
if not bos.path.isdir(src):
|
998
1021
|
self.log("warning: filesystem-path does not exist: {}".format(src), 3)
|
999
1022
|
|
1000
|
-
mount[dst] = src
|
1023
|
+
mount[dst] = (src, dst0)
|
1001
1024
|
daxs[dst] = AXS()
|
1002
1025
|
mflags[dst] = {}
|
1003
1026
|
return (src, dst)
|
@@ -1058,7 +1081,7 @@ class AuthSrv(object):
|
|
1058
1081
|
grps ,
|
1059
1082
|
daxs ,
|
1060
1083
|
mflags ,
|
1061
|
-
mount
|
1084
|
+
mount ,
|
1062
1085
|
) :
|
1063
1086
|
self.line_ctr = 0
|
1064
1087
|
|
@@ -1083,7 +1106,7 @@ class AuthSrv(object):
|
|
1083
1106
|
grps ,
|
1084
1107
|
daxs ,
|
1085
1108
|
mflags ,
|
1086
|
-
mount
|
1109
|
+
mount ,
|
1087
1110
|
npass ,
|
1088
1111
|
) :
|
1089
1112
|
self.line_ctr = 0
|
@@ -1442,8 +1465,8 @@ class AuthSrv(object):
|
|
1442
1465
|
acct = {} # username:password
|
1443
1466
|
grps = {} # groupname:usernames
|
1444
1467
|
daxs = {}
|
1445
|
-
mflags = {} #
|
1446
|
-
mount
|
1468
|
+
mflags = {} # vpath:flags
|
1469
|
+
mount = {} # dst:src (vp:(ap,vp0))
|
1447
1470
|
|
1448
1471
|
self.idp_vols = {} # yolo
|
1449
1472
|
|
@@ -1522,8 +1545,8 @@ class AuthSrv(object):
|
|
1522
1545
|
# case-insensitive; normalize
|
1523
1546
|
if WINDOWS:
|
1524
1547
|
cased = {}
|
1525
|
-
for
|
1526
|
-
cased[
|
1548
|
+
for vp, (ap, vp0) in mount.items():
|
1549
|
+
cased[vp] = (absreal(ap), vp0)
|
1527
1550
|
|
1528
1551
|
mount = cased
|
1529
1552
|
|
@@ -1538,25 +1561,28 @@ class AuthSrv(object):
|
|
1538
1561
|
t = "Read-access has been disabled due to failsafe: No volumes were defined by the config-file. This failsafe is to prevent unintended access if this is due to accidental loss of config. You can override this safeguard and allow read/write to the working-directory by adding the following arguments: -v .::rw"
|
1539
1562
|
self.log(t, 1)
|
1540
1563
|
axs = AXS()
|
1541
|
-
vfs = VFS(self.log_func, absreal("."), "", axs, {})
|
1564
|
+
vfs = VFS(self.log_func, absreal("."), "", "", axs, {})
|
1565
|
+
if not axs.uread:
|
1566
|
+
vfs.badcfg1 = True
|
1542
1567
|
elif "" not in mount:
|
1543
1568
|
# there's volumes but no root; make root inaccessible
|
1544
1569
|
zsd = {"d2d": True, "tcolor": self.args.tcolor}
|
1545
|
-
vfs = VFS(self.log_func, "", "", AXS(), zsd)
|
1570
|
+
vfs = VFS(self.log_func, "", "", "", AXS(), zsd)
|
1546
1571
|
|
1547
1572
|
maxdepth = 0
|
1548
1573
|
for dst in sorted(mount.keys(), key=lambda x: (x.count("/"), len(x))):
|
1549
1574
|
depth = dst.count("/")
|
1550
1575
|
assert maxdepth <= depth # nosec
|
1551
1576
|
maxdepth = depth
|
1577
|
+
src, dst0 = mount[dst]
|
1552
1578
|
|
1553
1579
|
if dst == "":
|
1554
1580
|
# rootfs was mapped; fully replaces the default CWD vfs
|
1555
|
-
vfs = VFS(self.log_func,
|
1581
|
+
vfs = VFS(self.log_func, src, dst, dst0, daxs[dst], mflags[dst])
|
1556
1582
|
continue
|
1557
1583
|
|
1558
1584
|
assert vfs # type: ignore
|
1559
|
-
zv = vfs.add(
|
1585
|
+
zv = vfs.add(src, dst, dst0)
|
1560
1586
|
zv.axs = daxs[dst]
|
1561
1587
|
zv.flags = mflags[dst]
|
1562
1588
|
zv.dbv = None
|
@@ -1577,12 +1603,8 @@ class AuthSrv(object):
|
|
1577
1603
|
for vol in vfs.all_vols.values():
|
1578
1604
|
unknown_flags = set()
|
1579
1605
|
for k, v in vol.flags.items():
|
1580
|
-
|
1581
|
-
if
|
1582
|
-
t = "WARNING: the config for volume [/%s] tried to remove volflag [%s] by specifying [%s] but that volflag was not already set"
|
1583
|
-
self.log(t % (vol.vpath, stripped, k), 3)
|
1584
|
-
k = stripped
|
1585
|
-
if k not in flagdescs and k not in k_ign:
|
1606
|
+
ks = k.lstrip("-")
|
1607
|
+
if ks not in flagdescs and ks not in k_ign:
|
1586
1608
|
unknown_flags.add(k)
|
1587
1609
|
if unknown_flags:
|
1588
1610
|
t = "WARNING: the config for volume [/%s] has unrecognized volflags; will ignore: '%s'"
|
@@ -1594,7 +1616,8 @@ class AuthSrv(object):
|
|
1594
1616
|
if enshare:
|
1595
1617
|
import sqlite3
|
1596
1618
|
|
1597
|
-
|
1619
|
+
zsd = {"d2d": True, "tcolor": self.args.tcolor}
|
1620
|
+
shv = VFS(self.log_func, "", shr, shr, AXS(), zsd)
|
1598
1621
|
|
1599
1622
|
db_path = self.args.shr_db
|
1600
1623
|
db = sqlite3.connect(db_path)
|
@@ -1628,9 +1651,8 @@ class AuthSrv(object):
|
|
1628
1651
|
|
1629
1652
|
# don't know the abspath yet + wanna ensure the user
|
1630
1653
|
# still has the privs they granted, so nullmap it
|
1631
|
-
|
1632
|
-
|
1633
|
-
)
|
1654
|
+
vp = "%s/%s" % (shr, s_k)
|
1655
|
+
shv.nodes[s_k] = VFS(self.log_func, "", vp, vp, s_axs, shv.flags.copy())
|
1634
1656
|
|
1635
1657
|
vfs.nodes[shr] = vfs.all_vols[shr] = shv
|
1636
1658
|
for vol in shv.nodes.values():
|
@@ -1791,6 +1813,24 @@ class AuthSrv(object):
|
|
1791
1813
|
rhisttab[histp] = zv
|
1792
1814
|
vfs.histtab[zv.realpath] = histp
|
1793
1815
|
|
1816
|
+
for vol in vfs.all_vols.values():
|
1817
|
+
use = False
|
1818
|
+
for k in ["zipmaxn", "zipmaxs"]:
|
1819
|
+
try:
|
1820
|
+
zs = vol.flags[k]
|
1821
|
+
except:
|
1822
|
+
zs = getattr(self.args, k)
|
1823
|
+
if zs in ("", "0"):
|
1824
|
+
vol.flags[k] = 0
|
1825
|
+
continue
|
1826
|
+
|
1827
|
+
zf = unhumanize(zs)
|
1828
|
+
vol.flags[k + "_v"] = zf
|
1829
|
+
if zf:
|
1830
|
+
use = True
|
1831
|
+
if use:
|
1832
|
+
vol.flags["zipmax"] = True
|
1833
|
+
|
1794
1834
|
for vol in vfs.all_vols.values():
|
1795
1835
|
lim = Lim(self.log_func)
|
1796
1836
|
use = False
|
@@ -2174,8 +2214,13 @@ class AuthSrv(object):
|
|
2174
2214
|
for vol in vfs.all_nodes.values():
|
2175
2215
|
for k in list(vol.flags.keys()):
|
2176
2216
|
if re.match("^-[^-]+$", k):
|
2177
|
-
vol.flags.pop(k[1:], None)
|
2178
2217
|
vol.flags.pop(k)
|
2218
|
+
zs = k[1:]
|
2219
|
+
if zs in vol.flags:
|
2220
|
+
vol.flags.pop(k[1:])
|
2221
|
+
else:
|
2222
|
+
t = "WARNING: the config for volume [/%s] tried to remove volflag [%s] by specifying [%s] but that volflag was not already set"
|
2223
|
+
self.log(t % (vol.vpath, zs, k), 3)
|
2179
2224
|
|
2180
2225
|
if vol.flags.get("dots"):
|
2181
2226
|
for name in vol.axs.uread:
|
@@ -2268,22 +2313,56 @@ class AuthSrv(object):
|
|
2268
2313
|
except Pebkac:
|
2269
2314
|
self.warn_anonwrite = True
|
2270
2315
|
|
2271
|
-
|
2316
|
+
self.idp_warn = []
|
2317
|
+
self.idp_err = []
|
2272
2318
|
for idp_vp in self.idp_vols:
|
2273
|
-
|
2274
|
-
|
2275
|
-
|
2276
|
-
|
2277
|
-
|
2278
|
-
|
2279
|
-
|
2280
|
-
|
2281
|
-
|
2282
|
-
|
2283
|
-
|
2284
|
-
|
2285
|
-
|
2286
|
-
|
2319
|
+
idp_vn, _ = vfs.get(idp_vp, "*", False, False)
|
2320
|
+
idp_vp0 = idp_vn.vpath0
|
2321
|
+
|
2322
|
+
sigils = set(re.findall(r"(\${[ug][}%])", idp_vp0))
|
2323
|
+
if len(sigils) > 1:
|
2324
|
+
t = '\nWARNING: IdP-volume "/%s" created by "/%s" has multiple IdP placeholders: %s'
|
2325
|
+
self.idp_warn.append(t % (idp_vp, idp_vp0, list(sigils)))
|
2326
|
+
continue
|
2327
|
+
|
2328
|
+
sigil = sigils.pop()
|
2329
|
+
par_vp = idp_vp
|
2330
|
+
while par_vp:
|
2331
|
+
par_vp = vsplit(par_vp)[0]
|
2332
|
+
par_vn, _ = vfs.get(par_vp, "*", False, False)
|
2333
|
+
if sigil in par_vn.vpath0:
|
2334
|
+
continue # parent was spawned for and by same user
|
2335
|
+
|
2336
|
+
oth_read = []
|
2337
|
+
oth_write = []
|
2338
|
+
for usr in par_vn.axs.uread:
|
2339
|
+
if usr not in idp_vn.axs.uread:
|
2340
|
+
oth_read.append(usr)
|
2341
|
+
for usr in par_vn.axs.uwrite:
|
2342
|
+
if usr not in idp_vn.axs.uwrite:
|
2343
|
+
oth_write.append(usr)
|
2344
|
+
|
2345
|
+
if "*" in oth_read:
|
2346
|
+
taxs = "WORLD-READABLE"
|
2347
|
+
elif "*" in oth_write:
|
2348
|
+
taxs = "WORLD-WRITABLE"
|
2349
|
+
elif oth_read:
|
2350
|
+
taxs = "READABLE BY %r" % (oth_read,)
|
2351
|
+
elif oth_write:
|
2352
|
+
taxs = "WRITABLE BY %r" % (oth_write,)
|
2353
|
+
else:
|
2354
|
+
break # no sigil; not idp; safe to stop
|
2355
|
+
|
2356
|
+
t = '\nWARNING: IdP-volume "/%s" created by "/%s" has parent/grandparent "/%s" and would be %s'
|
2357
|
+
self.idp_err.append(t % (idp_vp, idp_vp0, par_vn.vpath, taxs))
|
2358
|
+
|
2359
|
+
if self.idp_warn:
|
2360
|
+
t = "WARNING! Some IdP volumes include multiple IdP placeholders; this is too complex to automatically determine if safe or not. To ensure that no users gain unintended access, please use only a single placeholder for each IdP volume."
|
2361
|
+
self.log(t + "".join(self.idp_warn), 1)
|
2362
|
+
|
2363
|
+
if self.idp_err:
|
2364
|
+
t = "WARNING! The following IdP volumes are mounted below another volume where other users can read and/or write files. This is a SECURITY HAZARD!! When copyparty is restarted, it will not know about these IdP volumes yet. These volumes will then be accessible by an unexpected set of permissions UNTIL one of the users associated with their volume sends a request to the server. RECOMMENDATION: You should create a restricted volume where nobody can read/write files, and make sure that all IdP volumes are configured to appear somewhere below that volume."
|
2365
|
+
self.log(t + "".join(self.idp_err), 1)
|
2287
2366
|
|
2288
2367
|
self.vfs = vfs
|
2289
2368
|
self.acct = acct
|
@@ -2318,11 +2397,6 @@ class AuthSrv(object):
|
|
2318
2397
|
for x, y in vfs.all_vols.items()
|
2319
2398
|
if x != shr and not x.startswith(shrs)
|
2320
2399
|
}
|
2321
|
-
vfs.all_nodes = {
|
2322
|
-
x: y
|
2323
|
-
for x, y in vfs.all_nodes.items()
|
2324
|
-
if x != shr and not x.startswith(shrs)
|
2325
|
-
}
|
2326
2400
|
|
2327
2401
|
assert db and cur and cur2 and shv # type: ignore
|
2328
2402
|
for row in cur.execute("select * from sh"):
|
@@ -2352,6 +2426,7 @@ class AuthSrv(object):
|
|
2352
2426
|
else:
|
2353
2427
|
shn.ls = shn._ls
|
2354
2428
|
|
2429
|
+
shn.shr_owner = s_un
|
2355
2430
|
shn.shr_src = (s_vfs, s_rem)
|
2356
2431
|
shn.realpath = s_vfs.canonical(s_rem)
|
2357
2432
|
|
@@ -2369,7 +2444,7 @@ class AuthSrv(object):
|
|
2369
2444
|
continue # also fine
|
2370
2445
|
for zs in svn.nodes.keys():
|
2371
2446
|
# hide subvolume
|
2372
|
-
vn.nodes[zs] = VFS(self.log_func, "", "", AXS(), {})
|
2447
|
+
vn.nodes[zs] = VFS(self.log_func, "", "", "", AXS(), {})
|
2373
2448
|
|
2374
2449
|
cur2.close()
|
2375
2450
|
cur.close()
|
@@ -2377,7 +2452,9 @@ class AuthSrv(object):
|
|
2377
2452
|
|
2378
2453
|
self.js_ls = {}
|
2379
2454
|
self.js_htm = {}
|
2380
|
-
for vn in self.vfs.all_nodes.
|
2455
|
+
for vp, vn in self.vfs.all_nodes.items():
|
2456
|
+
if enshare and vp.startswith(shrs):
|
2457
|
+
continue # propagates later in this func
|
2381
2458
|
vf = vn.flags
|
2382
2459
|
vn.js_ls = {
|
2383
2460
|
"idx": "e2d" in vf,
|
@@ -2434,8 +2511,12 @@ class AuthSrv(object):
|
|
2434
2511
|
|
2435
2512
|
vols = list(vfs.all_nodes.values())
|
2436
2513
|
if enshare:
|
2437
|
-
|
2438
|
-
|
2514
|
+
for vol in shv.nodes.values():
|
2515
|
+
if vol.vpath not in vfs.all_nodes:
|
2516
|
+
self.log("BUG: /%s not in all_nodes" % (vol.vpath,), 1)
|
2517
|
+
vols.append(vol)
|
2518
|
+
if shr in vfs.all_nodes:
|
2519
|
+
self.log("BUG: %s found in all_nodes" % (shr,), 1)
|
2439
2520
|
|
2440
2521
|
for vol in vols:
|
2441
2522
|
dbv = vol.get_dbv("")[0]
|
@@ -2538,8 +2619,8 @@ class AuthSrv(object):
|
|
2538
2619
|
if not bos.path.exists(ap):
|
2539
2620
|
pwdb = {}
|
2540
2621
|
else:
|
2541
|
-
|
2542
|
-
|
2622
|
+
jtxt = read_utf8(self.log, ap, True)
|
2623
|
+
pwdb = json.loads(jtxt)
|
2543
2624
|
|
2544
2625
|
pwdb = [x for x in pwdb if x[0] != uname]
|
2545
2626
|
pwdb.append((uname, self.defpw[uname], hpw))
|
@@ -2562,8 +2643,8 @@ class AuthSrv(object):
|
|
2562
2643
|
if not self.args.chpw or not bos.path.exists(ap):
|
2563
2644
|
return
|
2564
2645
|
|
2565
|
-
|
2566
|
-
|
2646
|
+
jtxt = read_utf8(self.log, ap, True)
|
2647
|
+
pwdb = json.loads(jtxt)
|
2567
2648
|
|
2568
2649
|
useen = set()
|
2569
2650
|
urst = set()
|
@@ -3059,8 +3140,9 @@ def expand_config_file(
|
|
3059
3140
|
ipath += " -> " + fp
|
3060
3141
|
ret.append("#\033[36m opening cfg file{}\033[0m".format(ipath))
|
3061
3142
|
|
3062
|
-
|
3063
|
-
|
3143
|
+
cfg_lines = read_utf8(log, fp, True).split("\n")
|
3144
|
+
if True: # diff-golf
|
3145
|
+
for oln in [x.rstrip() for x in cfg_lines]:
|
3064
3146
|
ln = oln.split(" #")[0].strip()
|
3065
3147
|
if ln.startswith("% "):
|
3066
3148
|
pad = " " * len(oln.split("%")[0])
|