From 84937d1ce0b68ed70b5afadfa4b5cb3476fd5d7f Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 7 Feb 2023 19:54:08 +0000 Subject: [PATCH] add v2 config syntax (#20) --- README.md | 4 +- copyparty/__main__.py | 36 +-- copyparty/authsrv.py | 372 ++++++++++++++++++++++-------- docs/copyparty.d/foo/another.conf | 7 +- docs/copyparty.d/foo/sibling.conf | 2 +- docs/copyparty.d/some.conf | 35 +-- docs/example.conf | 114 +++++---- scripts/sfx.ls | 1 + 8 files changed, 378 insertions(+), 193 deletions(-) diff --git a/README.md b/README.md index c5a4be2c..2919fb3a 100644 --- a/README.md +++ b/README.md @@ -306,6 +306,7 @@ upgrade notes per-folder, per-user permissions - if your setup is getting complex, consider making a [config file](./docs/example.conf) instead of using arguments * much easier to manage, and you can modify the config at runtime with `systemctl reload copyparty` or more conveniently using the `[reload cfg]` button in the control-panel (if logged in as admin) + * changes to the `[global]` config section requires a restart to take effect a quick summary can be seen using `--help-accounts` @@ -690,6 +691,7 @@ using arguments or config files, or a mix of both: * config files (`-c some.conf`) can set additional commandline arguments; see [./docs/example.conf](docs/example.conf) and [./docs/example2.conf](docs/example2.conf) * `kill -s USR1` (same as `systemctl reload copyparty`) to reload accounts and volumes from config files without restarting * or click the `[reload cfg]` button in the control-panel when logged in as admin + * changes to the `[global]` config section requires a restart to take effect ## zeroconf @@ -800,7 +802,7 @@ some **BIG WARNINGS** specific to SMB/CIFS, in decreasing importance: and some minor issues, * clients only see the first ~400 files in big folders; [impacket#1433](https://github.com/SecureAuthCorp/impacket/issues/1433) -* hot-reload of server config (`/?reload=cfg`) only works for volumes, not account passwords +* hot-reload of server config (`/?reload=cfg`) does not include the `[global]` section (commandline args) * listens on the first IPv4 `-i` interface only (default = :: = 0.0.0.0 = all) * login doesn't work on winxp, but anonymous access is ok -- remove all accounts from copyparty config for that to work * win10 onwards does not allow connecting anonymously / without accounts diff --git a/copyparty/__main__.py b/copyparty/__main__.py index dff21339..3337111e 100755 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -25,9 +25,9 @@ from textwrap import dedent from .__init__ import ANYWIN, CORES, PY2, VT100, WINDOWS, E, EnvParams, unicode from .__version__ import CODENAME, S_BUILD_DT, S_VERSION -from .authsrv import expand_config_file, re_vol +from .authsrv import expand_config_file, re_vol, upgrade_cfg_fmt, split_cfg_ln from .svchub import SvcHub -from .cfg import flagcats +from .cfg import flagcats, onedash from .util import ( IMPLICATIONS, JINJA_VER, @@ -356,28 +356,28 @@ def configure_ssl_ciphers(al: argparse.Namespace) -> None: def args_from_cfg(cfg_path: str) -> list[str]: lines: list[str] = [] expand_config_file(lines, cfg_path, "") + lines = upgrade_cfg_fmt(None, argparse.Namespace(vc=False), lines, "") ret: list[str] = [] - skip = False + skip = True for ln in lines: - if not ln: + sn = ln.split(" #")[0].strip() + if sn.startswith("["): + skip = True + if sn.startswith("[global]"): skip = False continue - - if ln.startswith("#"): + if skip or not sn.split("#")[0].strip(): continue - - if not ln.startswith("-"): - skip = True - continue - - if skip: - continue - - try: - ret.extend(ln.split(" ", 1)) - except: - ret.append(ln) + for k, v in split_cfg_ln(sn).items(): + k = k.lstrip("-") + if not k: + continue + prefix = "-" if k in onedash else "--" + if v is True: + ret.append(prefix + k) + else: + ret.append(prefix + k + "=" + v) return ret diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index 84cf6e67..7e6b0ecd 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -37,7 +37,7 @@ if True: # pylint: disable=using-constant-test from typing import Any, Generator, Optional, Union - from .util import RootLogger + from .util import RootLogger, NamedLogger if TYPE_CHECKING: pass @@ -710,7 +710,7 @@ class AuthSrv(object): daxs[dst] = AXS() mflags[dst] = {} - def _e(self, desc: str) -> None: + def _e(self, desc: Optional[str] = None) -> None: if not self.args.vc or not self.line_ctr: return @@ -718,10 +718,11 @@ class AuthSrv(object): self.log("") return + desc = desc or "" desc = desc.replace("[", "[\033[0m").replace("]", "\033[90m]") self.log(" >>> {}{}".format(self.indent, desc), "90") - def _l(self, ln: str, c: int) -> None: + def _l(self, ln: str, c: int, desc: str) -> None: if not self.args.vc or not self.line_ctr: return @@ -729,11 +730,11 @@ class AuthSrv(object): c += 30 t = "\033[97m{:4} \033[{}m{}{}" - self.log(t.format(self.line_ctr, c, self.indent, ln)) + if desc: + t += " \033[0;90m# {}\033[0m" + desc = desc.replace("[", "[\033[0m").replace("]", "\033[90m]") - def _el(self, ln: str, c: int, desc: str) -> None: - self._e(desc) - self._l(ln, c) + self.log(t.format(self.line_ctr, c, self.indent, ln, desc)) def _parse_config_file( self, @@ -744,9 +745,6 @@ class AuthSrv(object): mflags: dict[str, dict[str, Any]], mount: dict[str, str], ) -> None: - vol_src = None - vol_dst = None - new_blk = True self.desc = [] self.line_ctr = 0 @@ -755,83 +753,131 @@ class AuthSrv(object): lns = ["{:4}: {}".format(n, s) for n, s in enumerate(cfg_lines, 1)] self.log("expanded config file (unprocessed):\n" + "\n".join(lns)) + cfg_lines = upgrade_cfg_fmt(self.log, self.args, cfg_lines, fp) + + cat = "" + catg = "[global]" + cata = "[accounts]" + catx = "accs:" + catf = "flags:" + ap: Optional[str] = None + vp: Optional[str] = None for ln in cfg_lines: self.line_ctr += 1 - if not ln and vol_src is not None: - self.indent = "" - self._e("└─end of settings for volume at URL [/{}]".format(vol_dst)) - if vol_dst is None: - t = "no URL provided for filesystem path [{}]" - raise Exception(t.format(vol_src)) - vol_src = None - vol_dst = None - - if not ln: - new_blk = True + ln = ln.split(" #")[0].strip() + if not ln.split("#")[0].strip(): continue - if ln.startswith("#"): - continue + subsection = ln in (catx, catf) + if ln.startswith("[") or subsection: + self._e() + if ap is None and vp is not None: + t = "the first line after [/{}] must be a filesystem path to share on that volume" + raise Exception(t.format(vp)) - if vol_src is None: - if ln.startswith("u "): - u, p = ln[2:].split(":", 1) - self._e("") - self._el(ln, 5, "account [{}], password [{}]:".format(u, p)) - acct[u] = p - elif ln.startswith("-"): - self._e("") - try: - ck, cv = ln.split(" ", 1) - t = "argument [{}] with value [{}]" - self._el(ln, 6, t.format(ck, cv)) - except: - self._el(ln, 6, "argument [{}]".format(ln)) - elif new_blk: - self._e("") - self._e("┌─share filesystem path [{}]:".format(ln)) - self.indent = "│ " - self._l(ln, 3) - vol_src = ln + cat = ln + if not subsection: + ap = vp = None + self.indent = "" else: - raise Exception("unexpected line: {}".format(ln)) + self.indent = " " - new_blk = False + if ln == catg: + t = "begin commandline-arguments (anything from --help; dashes are optional)" + self._l(ln, 6, t) + elif ln == cata: + self._l(ln, 5, "begin user-accounts section") + elif ln.startswith("[/"): + vp = ln[1:-1].strip("/") + self._l(ln, 2, "define volume at URL [/{}]".format(vp)) + elif subsection: + if ln == catx: + self._l(ln, 5, "volume access config:") + else: + t = "volume-specific config (anything from --help-flags)" + self._l(ln, 6, t) + else: + raise Exception("invalid section header") + + self.indent = " " if subsection else " " continue - if vol_src and vol_dst is None: - vol_dst = ln - if not vol_dst.startswith("/"): - raise Exception('invalid mountpoint "{}"'.format(vol_dst)) - - if vol_src.startswith("~"): - vol_src = os.path.expanduser(vol_src) - - # cfg files override arguments and previous files - vol_src = absreal(vol_src) - vol_dst = vol_dst.strip("/") - self._e("[{}]".format(vol_src)) - self._e("") - self._el(ln, 2, "at URL [/{}]:".format(vol_dst)) - self._map_volume(vol_src, vol_dst, mount, daxs, mflags) + if cat == catg: + self._l(ln, 6, "") + zt = split_cfg_ln(ln) + for zs, za in zt.items(): + zs = zs.lstrip("-") + if za is True: + self._e("└─argument [{}]".format(zs)) + else: + self._e("└─argument [{}] with value [{}]".format(zs, za)) continue - try: - lvl, uname = ln.split(" ", 1) - except: - lvl = ln - uname = "*" + if cat == cata: + try: + u, p = [zs.strip() for zs in ln.split(":", 1)] + self._l(ln, 5, "account [{}], password [{}]".format(u, p)) + acct[u] = p + except: + t = 'lines inside the [accounts] section must be "username: password"' + raise Exception(t) + continue - if lvl == "a": - t = "WARNING (config-file): permission flag 'a' is deprecated; please use 'rw' instead" - self.log(t, 1) + if vp is not None and ap is None: + ap = ln + if ap.startswith("~"): + ap = os.path.expanduser(ap) - assert vol_dst is not None - self._e("") - self._read_vol_str(lvl, uname, daxs[vol_dst], mflags[vol_dst]) - self._l(ln, 2) + ap = absreal(ap) + self._l(ln, 2, "bound to filesystem-path [{}]".format(ap)) + self._map_volume(ap, vp, mount, daxs, mflags) + continue - self._e("") + if cat == catx: + err = "" + try: + self._l(ln, 5, "volume access config:") + sk, sv = ln.split(":") + if re.sub("[rwmdgG]", "", sk) or not sk: + err = "invalid accs permissions list; " + raise Exception(err) + if " " in re.sub(", *", "", sv).strip(): + err = "list of users is not comma-separated; " + raise Exception(err) + self._read_vol_str(sk, sv.replace(" ", ""), daxs[vp], mflags[vp]) + continue + except: + err += "accs entries must be 'rwmdgG: user1, user2, ...'" + raise Exception(err) + + if cat == catf: + err = "" + try: + self._l(ln, 6, "volume-specific config:") + zd = split_cfg_ln(ln) + fstr = "" + for sk, sv in zd.items(): + bad = re.sub(r"[a-z0-9_]", "", sk) + if bad: + err = "bad characters [{}] in volflag name [{}]; " + err = err.format(bad, sk) + raise Exception(err) + if sv is True: + fstr += "," + sk + else: + fstr += ",{}={}".format(sk, sv) + self._read_vol_str("c", fstr[1:], daxs[vp], mflags[vp]) + fstr = "" + if fstr: + self._read_vol_str("c", fstr[1:], daxs[vp], mflags[vp]) + continue + except: + err += "flags entries (volflags) must be one of the following:\n 'flag1, flag2, ...'\n 'key: value'\n 'flag1, flag2, key: value'" + raise Exception(err) + + raise Exception("unprocessable line in config") + + self._e() self.line_ctr = 0 def _read_vol_str( @@ -871,7 +917,7 @@ class AuthSrv(object): ("G", axs.upget), ]: # b bb bbb if ch in lvl: - t = "add permission [{}] for user [{}] -- {}" + t = "└─add permission [{}] for user [{}] -- {}" desc = permdescs.get(ch, "?") self._e(t.format(ch, un, desc)) al.add(un) @@ -886,9 +932,9 @@ class AuthSrv(object): desc = flagdescs.get(name, "?").replace("\n", " ") if name not in ["mtp", "xbu", "xau", "xbr", "xar", "xbd", "xad", "xm"]: if value is True: - t = "add volflag [{}] = {} ({})" + t = "└─add volflag [{}] = {} ({})" else: - t = "add volflag [{}] = [{}] ({})" + t = "└─add volflag [{}] = [{}] ({})" self._e(t.format(name, value, desc)) flags[name] = value return @@ -1580,12 +1626,12 @@ class AuthSrv(object): args.pop(pop, None) if args: - ret.append("# add commandline args") + ret.append("[global]") for k, v in args.items(): if k in askip: continue if k in csv: - v = ",".join([str(za) for za in v]) + v = ", ".join([str(za) for za in v]) try: v2 = getattr(self.dargs, k) if v == v2: @@ -1593,27 +1639,27 @@ class AuthSrv(object): except: continue - dk = ("-" if k in onedash else "--") + k.replace("_", "-") + dk = " " + k.replace("_", "-") if k in lst: for ve in v: - ret.append("{} {}".format(dk, ve)) + ret.append("{}: {}".format(dk, ve)) else: if v is True: ret.append(dk) elif v not in (False, None, ""): - ret.append("{} {}".format(dk, v)) + ret.append("{}: {}".format(dk, v)) ret.append("") if self.acct: - ret.append("# add accounts") + ret.append("[accounts]") for u, p in self.acct.items(): - ret.append("u {}:{}".format(u, p)) + ret.append(" {}: {}".format(u, p)) ret.append("") for vol in self.vfs.all_vols.values(): - ret.append("# add volume [/{}]".format(vol.vpath)) - ret.append(vol.realpath) - ret.append("/" + vol.vpath) + ret.append("[/{}]".format(vol.vpath)) + ret.append(" " + vol.realpath) + ret.append(" accs:") perms = { "r": "uread", "w": "uwrite", @@ -1638,14 +1684,12 @@ class AuthSrv(object): continue if uname in getattr(vol.axs, pkey): pstr += pchar - if uname == "*": - uname = "" try: vperms[pstr].append(uname) except: vperms[pstr] = [uname] for pstr, uname in vperms.items(): - ret.append("{} {}".format(pstr, " ".join(uname)).rstrip(" ")) + ret.append(" {}: {}".format(pstr, ", ".join(uname))) trues = [] vals = [] for k, v in sorted(vol.flags.items()): @@ -1658,24 +1702,49 @@ class AuthSrv(object): if k in lst: for ve in v: - vals.append("c {}={}".format(k, ve)) + vals.append("{}: {}".format(k, ve)) elif v is True: trues.append(k) elif v is not False: - vals.append("c {}={}".format(k, v)) + vals.append("{}: {}".format(k, v)) pops = [] for k1, k2 in IMPLICATIONS: if k1 in trues: pops.append(k2) trues = [x for x in trues if x not in pops] if trues: - ret.append("c " + ",".join(trues)) - ret.extend(vals) + vals.append(", ".join(trues)) + if vals: + ret.append(" flags:") + for zs in vals: + ret.append(" " + zs) ret.append("") self.log("generated config:\n\n" + "\n".join(ret)) +def split_cfg_ln(ln: str) -> dict[str, Any]: + # "a, b, c: 3" => {a:true, b:true, c:3} + ret = {} + while True: + ln = ln.strip() + if not ln: + break + ofs_sep = ln.find(",") + 1 + ofs_var = ln.find(":") + 1 + if not ofs_sep and not ofs_var: + ret[ln] = True + break + if ofs_sep and (ofs_sep < ofs_var or not ofs_var): + k, ln = ln.split(",", 1) + ret[k.strip()] = True + else: + k, ln = ln.split(":", 1) + ret[k.strip()] = ln.strip() + break + return ret + + def expand_config_file(ret: list[str], fp: str, ipath: str) -> None: """expand all % file includes""" fp = absreal(fp) @@ -1694,13 +1763,126 @@ def expand_config_file(ret: list[str], fp: str, ipath: str) -> None: return with open(fp, "rb") as f: - for ln in [x.decode("utf-8").strip() for x in f]: + for oln in [x.decode("utf-8").rstrip() for x in f]: + ln = oln.split(" #")[0].strip() if ln.startswith("% "): + pad = " " * len(oln.split("%")[0]) fp2 = ln[1:].strip() fp2 = os.path.join(os.path.dirname(fp), fp2) + ofs = len(ret) expand_config_file(ret, fp2, ipath) + for n in range(ofs, len(ret)): + ret[n] = pad + ret[n] continue - ret.append(ln) + ret.append(oln) ret.append("#\033[36m closed{}\033[0m".format(ipath)) + + +def upgrade_cfg_fmt( + log: Optional["NamedLogger"], args: argparse.Namespace, orig: list[str], cfg_fp: str +) -> list[str]: + """convert from v1 to v2 format""" + zst = [x.split("#")[0].strip() for x in orig] + zst = [x for x in zst if x] + if ( + "[global]" in zst + or "[accounts]" in zst + or "accs:" in zst + or "flags:" in zst + or [x for x in zst if x.startswith("[/")] + or len(zst) == len([x for x in zst if x.startswith("%")]) + ): + return orig + + zst = [x for x in orig if "#\033[36m opening cfg file" not in x] + incl = len(zst) != len(orig) - 1 + + t = "upgrading config file [{}] from v1 to v2" + if not args.vc: + t += ". Run with argument '--vc' to see the converted config if you want to upgrade" + if incl: + t += ". Please don't include v1 configs from v2 files or vice versa! Upgrade all of them at the same time." + if log: + log(t.format(cfg_fp), 3) + + ret = [] + vp = "" + ap = "" + cat = "" + catg = "[global]" + cata = "[accounts]" + catx = " accs:" + catf = " flags:" + for ln in orig: + sn = ln.strip() + if not sn: + cat = vp = ap = "" + if not sn.split("#")[0]: + ret.append(ln) + elif sn.startswith("-") and cat in ("", catg): + if cat != catg: + cat = catg + ret.append(cat) + sn = sn.lstrip("-") + zst = sn.split(" ", 1) + if len(zst) > 1: + sn = "{}: {}".format(zst[0], zst[1].strip()) + ret.append(" " + sn) + elif sn.startswith("u ") and cat in ("", catg, cata): + if cat != cata: + cat = cata + ret.append(cat) + s1, s2 = sn[1:].split(":", 1) + ret.append(" {}: {}".format(s1.strip(), s2.strip())) + elif not ap: + ap = sn + elif not vp: + vp = "/" + sn.strip("/") + cat = "[{}]".format(vp) + ret.append(cat) + ret.append(" " + ap) + elif sn.startswith("c "): + if cat != catf: + cat = catf + ret.append(cat) + sn = sn[1:].strip() + if "=" in sn: + zst = sn.split("=", 1) + sn = zst[0].replace(",", ", ") + sn += ": " + zst[1] + else: + sn = sn.replace(",", ", ") + ret.append(" " + sn) + elif sn[:1] in "rwmdgG": + if cat != catx: + cat = catx + ret.append(cat) + zst = sn.split(" ") + zst = [x for x in zst if x] + if len(zst) == 1: + zst.append("*") + ret.append(" {}: {}".format(zst[0], ", ".join(zst[1:]))) + else: + t = "did not understand line {} in the config" + t1 = t + n = 0 + for ln in orig: + n += 1 + t += "\n{:4} {}".format(n, ln) + if log: + log(t, 1) + else: + print("\033[31m" + t) + raise Exception(t1) + + if args.vc and log: + t = "new config syntax (copy/paste this to upgrade your config):\n" + t += "\n# ======================[ begin upgraded config ]======================\n\n" + for ln in ret: + t += ln + "\n" + t += "\n# ======================[ end of upgraded config ]======================\n" + log(t) + + return ret diff --git a/docs/copyparty.d/foo/another.conf b/docs/copyparty.d/foo/another.conf index a72689ba..205077c4 100644 --- a/docs/copyparty.d/foo/another.conf +++ b/docs/copyparty.d/foo/another.conf @@ -1,5 +1,6 @@ # this file gets included twice from ../some.conf, # setting user permissions for a volume -rw usr1 -r usr2 -% sibling.conf +accs: + rw: usr1 + r: usr2 + % sibling.conf diff --git a/docs/copyparty.d/foo/sibling.conf b/docs/copyparty.d/foo/sibling.conf index 732bb78e..d9ea2999 100644 --- a/docs/copyparty.d/foo/sibling.conf +++ b/docs/copyparty.d/foo/sibling.conf @@ -1,3 +1,3 @@ # and this config file gets included from ./another.conf, # adding a final permission for each of the two volumes in ../some.conf -m usr1 usr2 +m: usr1, usr2 diff --git a/docs/copyparty.d/some.conf b/docs/copyparty.d/some.conf index c49cb951..b43e1bfe 100644 --- a/docs/copyparty.d/some.conf +++ b/docs/copyparty.d/some.conf @@ -1,26 +1,29 @@ +# not actually YAML but lets pretend: +# -*- mode: yaml -*- +# vim: ft=yaml: + # lets make two volumes with the same accounts/permissions for both; # first declare the accounts just once: -u usr1:passw0rd -u usr2:letmein +[accounts] + usr1: passw0rd + usr2: letmein -# and listen on 127.0.0.1 only, port 2434 --i 127.0.0.1 --p 2434 - -# enable file indexing+scanning and multimedia indexing+scanning --e2ds --e2ts +[global] + i: 127.0.0.1 # listen on 127.0.0.1 only, + p: 2434 # port 2434 + e2ds # enable file indexing+scanning + e2ts # and multimedia indexing+scanning + # (inline comments are OK if there is 2 spaces before the #) # share /usr/share/games from the server filesystem -/usr/share/games -/vidya -# include config file with volume permissions -% foo/another.conf +[/vidya] + /usr/share/games + % foo/another.conf # include config file with volume permissions # and share your ~/Music folder too -~/Music -/bangers -% foo/another.conf +[/bangers] + ~/Music + % foo/another.conf # which should result in each of the volumes getting the following permissions: # usr1 read/write/move diff --git a/docs/example.conf b/docs/example.conf index 4afc56ee..05e2d919 100644 --- a/docs/example.conf +++ b/docs/example.conf @@ -1,72 +1,68 @@ +# not actually YAML but lets pretend: +# -*- mode: yaml -*- +# vim: ft=yaml: + # append some arguments to the commandline; -# the first space in a line counts as a separator, -# any additional spaces are part of the value --e2dsa --e2ts --i 127.0.0.1 +# accepts anything listed in --help (leading dashes are optional) +# and inline comments are OK if there is 2 spaces before the '#' +[global] + p: 8086, 3939 # listen on ports 8086 and 3939 + e2dsa # enable file indexing and filesystem scanning + e2ts # and enable multimedia indexing + z, qr # and zeroconf and qrcode (you can comma-separate arguments) # create users: -# u username:password -u ed:123 -u k:k +[accounts] + ed: 123 # username: password + k: k -# leave a blank line between volumes -# (and also between users and volumes) +# create volumes: +[/] # create a volume at "/" (the webroot), which will + . # share the contents of "." (the current directory) + accs: + r: * # everyone gets read-access, but + rw: ed # the user "ed" gets read-write -# create a volume: -# share "." (the current directory) -# as "/" (the webroot) for the following users: -# "r" grants read-access for anyone -# "rw ed" grants read-write to ed -. -/ -r -rw ed - -# custom permissions for the "priv" folder: -# user "k" can only see/read the contents -# user "ed" gets read-write access -./priv -/priv -r k -rw ed - -# this does the same thing, -# and will cause an error on startup since /priv is already taken: -./priv -/priv -r ed k -w ed +# let's specify different permissions for the "priv" subfolder +# by creating another volume at that location: +[/priv] + ./priv + accs: + r: k # the user "k" can see the contents, + rw: ed # while "ed" gets read-write # share /home/ed/Music/ as /music and let anyone read it # (this will replace any folder called "music" in the webroot) -/home/ed/Music -/music -r +[/music] + /home/ed/Music + accs: + r: * + +# and a folder where anyone can upload, but nobody can see the contents +[/dump] + /home/ed/inc + accs: + w: * + flags: + e2d # the e2d volflag enables the uploads database + nodupe # the nodupe volflag rejects duplicate uploads + # (see --help-flags for all available volflags to use) # and a folder where anyone can upload -# but nobody can see the contents -# and set the e2d flag to enable the uploads database -# and set the nodupe flag to reject duplicate uploads -/home/ed/inc -/dump -w -c e2d -c nodupe - -# and a folder where anyone can upload -# and anyone can access their own uploads but nothing else -# (permissions "wG" = write + upget); -# volflag "e2d" enables the uploads database, -# "d2t" disables multimedia parsers (in case the uploads are malicious), -# "dthumb" disables thumbnails (same reason), -# "fk" enables filekeys (necessary for upget permission) (4 chars long) -# -- note that its fine to combine all the volflags on -# one line because only the last volflag has an argument -/home/ed/inc/sharex -/sharex -wG -c e2d,d2t,fk=4 +# and anyone can access their own uploads, but nothing else +[/sharex] + /home/ed/inc/sharex + accs: + wG: * # wG = write-upget = see your own uploads only + rwmd: ed, k # read-write-modify-delete for users "ed" and "k" + flags: + e2d, d2t, fk: 4 + # volflag "e2d" enables the uploads database, + # "d2t" disables multimedia parsers (in case the uploads are malicious), + # "dthumb" disables thumbnails (same reason), + # "fk" enables filekeys (necessary for upget permission) (4 chars long) + # -- note that its fine to combine all the volflags on + # one line because only the last volflag has an argument # this entire config file can be replaced with these arguments: # -u ed:123 -u k:k -v .::r:a,ed -v priv:priv:r,k:rw,ed -v /home/ed/Music:music:r -v /home/ed/inc:dump:w:c,e2d,nodupe -v /home/ed/inc/sharex:sharex:wG:c,e2d,d2t,fk=4 diff --git a/scripts/sfx.ls b/scripts/sfx.ls index 34cff932..e50b8c91 100644 --- a/scripts/sfx.ls +++ b/scripts/sfx.ls @@ -11,6 +11,7 @@ copyparty/broker_mp.py, copyparty/broker_mpw.py, copyparty/broker_thr.py, copyparty/broker_util.py, +copyparty/cfg.py, copyparty/dxml.py, copyparty/fsutil.py, copyparty/ftpd.py,