mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
add optional username login; closes #511
This commit is contained in:
parent
3c42a34f7b
commit
346515ccf1
10
README.md
10
README.md
|
@ -437,6 +437,7 @@ upgrade notes
|
||||||
|
|
||||||
* can I link someone to a password-protected volume/file by including the password in the URL?
|
* can I link someone to a password-protected volume/file by including the password in the URL?
|
||||||
* yes, by adding `?pw=hunter2` to the end; replace `?` with `&` if there are parameters in the URL already, meaning it contains a `?` near the end
|
* yes, by adding `?pw=hunter2` to the end; replace `?` with `&` if there are parameters in the URL already, meaning it contains a `?` near the end
|
||||||
|
* if you have enabled `--accounts` then do `?pw=username:password` instead
|
||||||
|
|
||||||
* how do I stop `.hist` folders from appearing everywhere on my HDD?
|
* how do I stop `.hist` folders from appearing everywhere on my HDD?
|
||||||
* by default, a `.hist` folder is created inside each volume for the filesystem index, thumbnails, audio transcodes, and markdown document history. Use the `--hist` global-option or the `hist` volflag to move it somewhere else; see [database location](#database-location)
|
* by default, a `.hist` folder is created inside each volume for the filesystem index, thumbnails, audio transcodes, and markdown document history. Use the `--hist` global-option or the `hist` volflag to move it somewhere else; see [database location](#database-location)
|
||||||
|
@ -1016,6 +1017,7 @@ a feed example: https://cd.ocv.me/a/d2/d22/?rss&fext=mp3
|
||||||
url parameters:
|
url parameters:
|
||||||
|
|
||||||
* `pw=hunter2` for password auth
|
* `pw=hunter2` for password auth
|
||||||
|
* if you enabled `--usernames` then do `pw=username:password` instead
|
||||||
* `recursive` to also include subfolders
|
* `recursive` to also include subfolders
|
||||||
* `title=foo` changes the feed title (default: folder name)
|
* `title=foo` changes the feed title (default: folder name)
|
||||||
* `fext=mp3,opus` only include mp3 and opus files (default: all)
|
* `fext=mp3,opus` only include mp3 and opus files (default: all)
|
||||||
|
@ -1301,6 +1303,7 @@ an FTP server can be started using `--ftp 3921`, and/or `--ftps` for explicit T
|
||||||
* if you enable both `ftp` and `ftps`, the port-range will be divided in half
|
* if you enable both `ftp` and `ftps`, the port-range will be divided in half
|
||||||
* some older software (filezilla on debian-stable) cannot passive-mode with TLS
|
* some older software (filezilla on debian-stable) cannot passive-mode with TLS
|
||||||
* login with any username + your password, or put your password in the username field
|
* login with any username + your password, or put your password in the username field
|
||||||
|
* unless you enabled `--usernames`
|
||||||
|
|
||||||
some recommended FTP / FTPS clients; `wark` = example password:
|
some recommended FTP / FTPS clients; `wark` = example password:
|
||||||
* https://winscp.net/eng/download.php
|
* https://winscp.net/eng/download.php
|
||||||
|
@ -1318,6 +1321,7 @@ click the [connect](http://127.0.0.1:3923/?hc) button in the control-panel to se
|
||||||
|
|
||||||
general usage:
|
general usage:
|
||||||
* login with any username + your password, or put your password in the username field (password field can be empty/whatever)
|
* login with any username + your password, or put your password in the username field (password field can be empty/whatever)
|
||||||
|
* unless you enabled `--usernames`
|
||||||
|
|
||||||
on macos, connect from finder:
|
on macos, connect from finder:
|
||||||
* [Go] -> [Connect to Server...] -> http://192.168.123.1:3923/
|
* [Go] -> [Connect to Server...] -> http://192.168.123.1:3923/
|
||||||
|
@ -1333,6 +1337,7 @@ using the GUI (winXP or later):
|
||||||
* rightclick [my computer] -> [map network drive] -> Folder: `http://192.168.123.1:3923/`
|
* rightclick [my computer] -> [map network drive] -> Folder: `http://192.168.123.1:3923/`
|
||||||
* on winXP only, click the `Sign up for online storage` hyperlink instead and put the URL there
|
* on winXP only, click the `Sign up for online storage` hyperlink instead and put the URL there
|
||||||
* providing your password as the username is recommended; the password field can be anything or empty
|
* providing your password as the username is recommended; the password field can be anything or empty
|
||||||
|
* unless you enabled `--usernames`
|
||||||
|
|
||||||
the webdav client that's built into windows has the following list of bugs; you can avoid all of these by connecting with rclone instead:
|
the webdav client that's built into windows has the following list of bugs; you can avoid all of these by connecting with rclone instead:
|
||||||
* win7+ doesn't actually send the password to the server when reauthenticating after a reboot unless you first try to login with an incorrect password and then switch to the correct password
|
* win7+ doesn't actually send the password to the server when reauthenticating after a reboot unless you first try to login with an incorrect password and then switch to the correct password
|
||||||
|
@ -1390,6 +1395,7 @@ some **BIG WARNINGS** specific to SMB/CIFS, in decreasing importance:
|
||||||
* the smb backend is not fully integrated with vfs, meaning there could be security issues (path traversal). Please use `--smb-port` (see below) and [prisonparty](./bin/prisonparty.sh) or [bubbleparty](./bin/bubbleparty.sh)
|
* the smb backend is not fully integrated with vfs, meaning there could be security issues (path traversal). Please use `--smb-port` (see below) and [prisonparty](./bin/prisonparty.sh) or [bubbleparty](./bin/bubbleparty.sh)
|
||||||
* account passwords work per-volume as expected, and so does account permissions (read/write/move/delete), but `--smbw` must be given to allow write-access from smb
|
* account passwords work per-volume as expected, and so does account permissions (read/write/move/delete), but `--smbw` must be given to allow write-access from smb
|
||||||
* [shadowing](#shadowing) probably works as expected but no guarantees
|
* [shadowing](#shadowing) probably works as expected but no guarantees
|
||||||
|
* not compatible with pw-hashing or `--usernames`
|
||||||
|
|
||||||
and some minor issues,
|
and some minor issues,
|
||||||
* clients only see the first ~400 files in big folders;
|
* clients only see the first ~400 files in big folders;
|
||||||
|
@ -2506,6 +2512,8 @@ you can provide passwords using header `PW: hunter2`, cookie `cppwd=hunter2`, ur
|
||||||
|
|
||||||
> for basic-authentication, all of the following are accepted: `password` / `whatever:password` / `password:whatever` (the username is ignored)
|
> for basic-authentication, all of the following are accepted: `password` / `whatever:password` / `password:whatever` (the username is ignored)
|
||||||
|
|
||||||
|
* unless you've enabled `--usernames`, then it's `PW: usr:pwd`, cookie `cppwd=usr:pwd`, url-param `?pw=usr:pwd`
|
||||||
|
|
||||||
NOTE: curl will not send the original filename if you use `-T` combined with url-params! Also, make sure to always leave a trailing slash in URLs unless you want to override the filename
|
NOTE: curl will not send the original filename if you use `-T` combined with url-params! Also, make sure to always leave a trailing slash in URLs unless you want to override the filename
|
||||||
|
|
||||||
|
|
||||||
|
@ -2721,6 +2729,8 @@ when generating hashes using `--ah-cli` for docker or systemd services, make sur
|
||||||
* inspecting the generated salt using `--show-ah-salt` in copyparty service configuration
|
* inspecting the generated salt using `--show-ah-salt` in copyparty service configuration
|
||||||
* setting the same `--ah-salt` in both environments
|
* setting the same `--ah-salt` in both environments
|
||||||
|
|
||||||
|
> ⚠️ if you have enabled `--usernames` then provide the password as `username:password` when hashing it, for example `ed:hunter2`
|
||||||
|
|
||||||
|
|
||||||
## https
|
## https
|
||||||
|
|
||||||
|
|
|
@ -918,6 +918,9 @@ def get_sects():
|
||||||
copyparty will also hash and print any passwords that are non-hashed
|
copyparty will also hash and print any passwords that are non-hashed
|
||||||
(password which do not start with '+') and then terminate afterwards
|
(password which do not start with '+') and then terminate afterwards
|
||||||
|
|
||||||
|
if you have enabled --accounts then the password
|
||||||
|
must be provided as username:password for hashing
|
||||||
|
|
||||||
\033[36m--ah-alg\033[0m specifies the hashing algorithm and a
|
\033[36m--ah-alg\033[0m specifies the hashing algorithm and a
|
||||||
list of optional comma-separated arguments:
|
list of optional comma-separated arguments:
|
||||||
|
|
||||||
|
@ -1002,6 +1005,7 @@ def add_general(ap, nc, srvname):
|
||||||
ap2.add_argument("-a", metavar="ACCT", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add account, \033[33mUSER\033[0m:\033[33mPASS\033[0m; example [\033[32med:wark\033[0m]")
|
ap2.add_argument("-a", metavar="ACCT", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add account, \033[33mUSER\033[0m:\033[33mPASS\033[0m; example [\033[32med:wark\033[0m]")
|
||||||
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")
|
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")
|
||||||
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]")
|
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]")
|
||||||
|
ap2.add_argument("--usernames", action="store_true", help="require username and password for login; default is just password")
|
||||||
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)")
|
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)")
|
||||||
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")
|
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")
|
||||||
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-]")
|
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-]")
|
||||||
|
@ -1393,7 +1397,7 @@ def add_logging(ap):
|
||||||
ap2.add_argument("--no-voldump", action="store_true", help="do not list volumes and permissions on startup")
|
ap2.add_argument("--no-voldump", action="store_true", help="do not list volumes and permissions on startup")
|
||||||
ap2.add_argument("--log-utc", action="store_true", help="do not use local timezone; assume the TZ env-var is UTC (tiny bit faster)")
|
ap2.add_argument("--log-utc", action="store_true", help="do not use local timezone; assume the TZ env-var is UTC (tiny bit faster)")
|
||||||
ap2.add_argument("--log-tdec", metavar="N", type=int, default=3, help="timestamp resolution / number of timestamp decimals")
|
ap2.add_argument("--log-tdec", metavar="N", type=int, default=3, help="timestamp resolution / number of timestamp decimals")
|
||||||
ap2.add_argument("--log-badpwd", metavar="N", type=int, default=1, help="log failed login attempt passwords: 0=terse, 1=plaintext, 2=hashed")
|
ap2.add_argument("--log-badpwd", metavar="N", type=int, default=2, help="log failed login attempt passwords: 0=terse, 1=plaintext, 2=hashed")
|
||||||
ap2.add_argument("--log-conn", action="store_true", help="debug: print tcp-server msgs")
|
ap2.add_argument("--log-conn", action="store_true", help="debug: print tcp-server msgs")
|
||||||
ap2.add_argument("--log-htp", action="store_true", help="debug: print http-server threadpool scaling")
|
ap2.add_argument("--log-htp", action="store_true", help="debug: print http-server threadpool scaling")
|
||||||
ap2.add_argument("--ihead", metavar="HEADER", type=u, action='append', help="print request \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
|
ap2.add_argument("--ihead", metavar="HEADER", type=u, action='append', help="print request \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
|
||||||
|
|
|
@ -2642,6 +2642,8 @@ class AuthSrv(object):
|
||||||
self.re_pwd = None
|
self.re_pwd = None
|
||||||
pwds = [re.escape(x) for x in self.iacct.keys()]
|
pwds = [re.escape(x) for x in self.iacct.keys()]
|
||||||
pwds.extend(list(self.sesa))
|
pwds.extend(list(self.sesa))
|
||||||
|
if self.args.usernames:
|
||||||
|
pwds.extend([x.split(":", 1)[1] for x in pwds if ":" in x])
|
||||||
if pwds:
|
if pwds:
|
||||||
if self.ah.on:
|
if self.ah.on:
|
||||||
zs = r"(\[H\] pw:.*|[?&]pw=)([^&]+)"
|
zs = r"(\[H\] pw:.*|[?&]pw=)([^&]+)"
|
||||||
|
@ -2942,6 +2944,9 @@ class AuthSrv(object):
|
||||||
t = "minimum password length: %d characters"
|
t = "minimum password length: %d characters"
|
||||||
return False, t % (self.args.chpw_len,)
|
return False, t % (self.args.chpw_len,)
|
||||||
|
|
||||||
|
if self.args.usernames:
|
||||||
|
pw = "%s:%s" % (uname, pw)
|
||||||
|
|
||||||
hpw = self.ah.hash(pw) if self.ah.on else pw
|
hpw = self.ah.hash(pw) if self.ah.on else pw
|
||||||
|
|
||||||
if hpw == self.acct[uname]:
|
if hpw == self.acct[uname]:
|
||||||
|
@ -3033,6 +3038,12 @@ class AuthSrv(object):
|
||||||
self.log("chpw: " + msg, 6)
|
self.log("chpw: " + msg, 6)
|
||||||
|
|
||||||
def setup_pwhash(self, acct: dict[str, str]) -> None:
|
def setup_pwhash(self, acct: dict[str, str]) -> None:
|
||||||
|
if self.args.usernames:
|
||||||
|
for uname, pw in list(acct.items())[:]:
|
||||||
|
if pw.startswith("+") and len(pw) == 33:
|
||||||
|
continue
|
||||||
|
acct[uname] = "%s:%s" % (uname, pw)
|
||||||
|
|
||||||
self.ah = PWHash(self.args)
|
self.ah = PWHash(self.args)
|
||||||
if not self.ah.on:
|
if not self.ah.on:
|
||||||
if self.args.ah_cli or self.args.ah_gen:
|
if self.args.ah_cli or self.args.ah_gen:
|
||||||
|
|
|
@ -83,7 +83,12 @@ class FtpAuth(DummyAuthorizer):
|
||||||
uname = "*"
|
uname = "*"
|
||||||
if username != "anonymous":
|
if username != "anonymous":
|
||||||
uname = ""
|
uname = ""
|
||||||
for zs in (password, username):
|
if args.usernames:
|
||||||
|
alts = ["%s:%s" % (username, password)]
|
||||||
|
else:
|
||||||
|
alts = password, username
|
||||||
|
|
||||||
|
for zs in alts:
|
||||||
zs = asrv.iacct.get(asrv.ah.hash(zs), "")
|
zs = asrv.iacct.get(asrv.ah.hash(zs), "")
|
||||||
if zs:
|
if zs:
|
||||||
uname = zs
|
uname = zs
|
||||||
|
|
|
@ -2937,12 +2937,16 @@ class HttpCli(object):
|
||||||
|
|
||||||
def handle_chpw(self) -> bool:
|
def handle_chpw(self) -> bool:
|
||||||
assert self.parser # !rm
|
assert self.parser # !rm
|
||||||
|
if self.args.usernames:
|
||||||
|
self.parser.require("uname", 64)
|
||||||
pwd = self.parser.require("pw", 64)
|
pwd = self.parser.require("pw", 64)
|
||||||
self.parser.drop()
|
self.parser.drop()
|
||||||
|
|
||||||
ok, msg = self.asrv.chpw(self.conn.hsrv.broker, self.uname, pwd)
|
ok, msg = self.asrv.chpw(self.conn.hsrv.broker, self.uname, pwd)
|
||||||
if ok:
|
if ok:
|
||||||
self.cbonk(self.conn.hsrv.gpwc, pwd, "pw", "too many password changes")
|
self.cbonk(self.conn.hsrv.gpwc, pwd, "pw", "too many password changes")
|
||||||
|
if self.args.usernames:
|
||||||
|
pwd = "%s:%s" % (self.uname, pwd)
|
||||||
ok, msg = self.get_pwd_cookie(pwd)
|
ok, msg = self.get_pwd_cookie(pwd)
|
||||||
if ok:
|
if ok:
|
||||||
msg = "new password OK"
|
msg = "new password OK"
|
||||||
|
@ -2955,6 +2959,13 @@ class HttpCli(object):
|
||||||
|
|
||||||
def handle_login(self) -> bool:
|
def handle_login(self) -> bool:
|
||||||
assert self.parser # !rm
|
assert self.parser # !rm
|
||||||
|
if self.args.usernames:
|
||||||
|
try:
|
||||||
|
un = self.parser.require("uname", 256)
|
||||||
|
except:
|
||||||
|
un = ""
|
||||||
|
else:
|
||||||
|
un = ""
|
||||||
pwd = self.parser.require("cppwd", 64)
|
pwd = self.parser.require("cppwd", 64)
|
||||||
try:
|
try:
|
||||||
uhash = self.parser.require("uhash", 256)
|
uhash = self.parser.require("uhash", 256)
|
||||||
|
@ -2965,6 +2976,9 @@ class HttpCli(object):
|
||||||
if not pwd:
|
if not pwd:
|
||||||
raise Pebkac(422, "password cannot be blank")
|
raise Pebkac(422, "password cannot be blank")
|
||||||
|
|
||||||
|
if un:
|
||||||
|
pwd = "%s:%s" % (un, pwd)
|
||||||
|
|
||||||
dst = self.args.SRS
|
dst = self.args.SRS
|
||||||
if self.vpath:
|
if self.vpath:
|
||||||
dst += quotep(self.vpaths)
|
dst += quotep(self.vpaths)
|
||||||
|
|
|
@ -147,6 +147,10 @@ class PWHash(object):
|
||||||
def cli(self) -> None:
|
def cli(self) -> None:
|
||||||
import getpass
|
import getpass
|
||||||
|
|
||||||
|
if self.args.usernames:
|
||||||
|
t = "since you have enabled --usernames, please provide username:password"
|
||||||
|
print(t)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
p1 = getpass.getpass("password> ")
|
p1 = getpass.getpass("password> ")
|
||||||
|
|
|
@ -2983,8 +2983,7 @@ def justcopy(
|
||||||
|
|
||||||
|
|
||||||
def eol_conv(
|
def eol_conv(
|
||||||
fin: Generator[bytes, None, None],
|
fin: Generator[bytes, None, None], conv: str
|
||||||
conv: str
|
|
||||||
) -> Generator[bytes, None, None]:
|
) -> Generator[bytes, None, None]:
|
||||||
crlf = conv.lower() == "crlf"
|
crlf = conv.lower() == "crlf"
|
||||||
for buf in fin:
|
for buf in fin:
|
||||||
|
|
|
@ -120,7 +120,12 @@
|
||||||
<div>
|
<div>
|
||||||
<form id="lf" method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
|
<form id="lf" method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}">
|
||||||
<input type="hidden" id="la" name="act" value="login" />
|
<input type="hidden" id="la" name="act" value="login" />
|
||||||
|
{% if this.args.usernames %}
|
||||||
|
<input type="text" id="lu" name="uname" placeholder=" username" size="12" />
|
||||||
|
<input type="password" id="lp" name="cppwd" placeholder=" password" size="12" />
|
||||||
|
{% else %}
|
||||||
<input type="password" id="lp" name="cppwd" placeholder=" password" />
|
<input type="password" id="lp" name="cppwd" placeholder=" password" />
|
||||||
|
{% endif %}
|
||||||
<input type="hidden" name="uhash" id="uhash" value="x" />
|
<input type="hidden" name="uhash" id="uhash" value="x" />
|
||||||
<input type="submit" id="ls" value="login" />
|
<input type="submit" id="ls" value="login" />
|
||||||
{% if chpw %}
|
{% if chpw %}
|
||||||
|
|
|
@ -416,7 +416,7 @@ try {
|
||||||
catch (ex) { }
|
catch (ex) { }
|
||||||
|
|
||||||
tt.init();
|
tt.init();
|
||||||
var o = QS('input[name="cppwd"]');
|
var o = QS('input[name="uname"]') || QS('input[name="cppwd"]');
|
||||||
if (!ebi('c') && o.offsetTop + o.offsetHeight < window.innerHeight)
|
if (!ebi('c') && o.offsetTop + o.offsetHeight < window.innerHeight)
|
||||||
o.focus();
|
o.focus();
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,7 @@ class Cfg(Namespace):
|
||||||
def __init__(self, a=None, v=None, c=None, **ka0):
|
def __init__(self, a=None, v=None, c=None, **ka0):
|
||||||
ka = {}
|
ka = {}
|
||||||
|
|
||||||
ex = "allow_flac allow_wav chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead localtime magic nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_fnugg no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz reflink rmagic rss smb srch_dbg srch_excl stats uqe vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs"
|
ex = "allow_flac allow_wav chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead localtime magic nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_fnugg no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz reflink rmagic rss smb srch_dbg srch_excl stats uqe usernames vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs"
|
||||||
ka.update(**{k: False for k in ex.split()})
|
ka.update(**{k: False for k in ex.split()})
|
||||||
|
|
||||||
ex = "dav_inf dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump re_dhash see_dots plain_ip"
|
ex = "dav_inf dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump re_dhash see_dots plain_ip"
|
||||||
|
|
Loading…
Reference in a new issue