diff --git a/copyparty/__main__.py b/copyparty/__main__.py index ea8a4292..b7932c5b 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -52,15 +52,15 @@ def main(): epilog=dedent( """ -a takes username:password, - -v takes path:permset:permset:... where "permset" is + -v takes src:dst:permset:permset:... where "permset" is accesslevel followed by username (no separator) example:\033[35m - -a ed:hunter2 -v .:r:aed -v ../inc:w:aed \033[36m - share current directory with + -a ed:hunter2 -v .::r:aed -v ../inc:dump:w:aed \033[36m + mount current directory at "/" with * r (read-only) for everyone * a (read+write) for ed - share ../inc with + mount ../inc at "/dump" with * w (write-only) for everyone * a (read+write) for ed \033[0m @@ -72,14 +72,16 @@ def main(): """ ), ) - ap.add_argument("-c", metavar="PATH", type=str, help="config file") + ap.add_argument( + "-c", metavar="PATH", type=str, action="append", help="add config file" + ) ap.add_argument("-i", metavar="IP", type=str, default="0.0.0.0", help="ip to bind") ap.add_argument("-p", metavar="PORT", type=int, default=1234, help="port to bind") ap.add_argument("-nc", metavar="NUM", type=int, default=16, help="max num clients") ap.add_argument("-j", metavar="CORES", type=int, help="max num cpu cores") - ap.add_argument("-a", metavar="ACCT", type=str, help="add account") - ap.add_argument("-v", metavar="VOL", type=str, help="add volume") - ap.add_argument("-nw", action="store_true", help="DEBUG: disable writing") + ap.add_argument("-a", metavar="ACCT", type=str, action="append", help="add account") + ap.add_argument("-v", metavar="VOL", type=str, action="append", help="add volume") + ap.add_argument("-nw", action="store_true", help="benchmark: disable writing") al = ap.parse_args() tcpsrv = TcpSrv(al) diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py new file mode 100644 index 00000000..c010f388 --- /dev/null +++ b/copyparty/authsrv.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# coding: utf-8 +from __future__ import print_function + +import pprint +import threading + +from .__init__ import * + + +class AuthSrv(object): + """verifies users against given paths""" + + def __init__(self, args, log_func): + self.log_func = log_func + self.args = args + + self.mutex = threading.Lock() + self.reload() + + def log(self, msg): + self.log_func("auth", msg) + + def invert(self, orig): + if PY2: + return {v: k for k, v in orig.iteritems()} + else: + return {v: k for k, v in orig.items()} + + def reload(self): + user = {} # username:password + uread = {} # username:readable-mp + uwrite = {} # username:writable-mp + mount = {} # dst:src (mountpoint:realpath) + + if self.args.a: + # list of username:password + for u, p in [x.split(":", 1) for x in self.args.a]: + user[u] = p + + if self.args.v: + # list of src:dst:permset:permset:... + # permset is [rwa]username + for src, dst, perms in [x.split(":", 2) for x in self.args.v]: + src = os.path.abspath(src) + dst = ("/" + dst.strip("/") + "/").replace("//", "/") + mount[dst] = src + perms = perms.split(":") + for (lvl, uname) in [[x[0], x[1:]] for x in perms]: + if uname == "": + uname = "*" + if lvl in "ra": + uread[uname] = dst + if lvl in "wa": + uwrite[uname] = dst + + if self.args.c: + for logfile in self.args.c: + with open(logfile, "rb") as f: + for ln in [x.decode("utf-8").rstrip() for x in f]: + # self.log(ln) + pass + + with self.mutex: + self.user = user + self.uread = uread + self.uwrite = uwrite + self.mount = mount + self.iuser = self.invert(user) + self.iuread = self.invert(uread) + self.iuwrite = self.invert(uwrite) + self.imount = self.invert(mount) + + pprint.pprint( + { + "user": self.user, + "uread": self.uread, + "uwrite": self.uwrite, + "mount": self.mount, + } + ) diff --git a/copyparty/httpsrv.py b/copyparty/httpsrv.py index 4d470e2c..7196d26c 100644 --- a/copyparty/httpsrv.py +++ b/copyparty/httpsrv.py @@ -5,6 +5,7 @@ from __future__ import print_function import threading from .httpcli import * +from .authsrv import * class HttpSrv(object): @@ -18,11 +19,12 @@ class HttpSrv(object): self.args = args self.disconnect_func = None + self.mutex = threading.Lock() + self.clients = {} self.workload = 0 self.workload_thr_alive = False - - self.mutex = threading.Lock() + self.auth = AuthSrv(args, log_func) def accept(self, sck, addr): """takes an incoming tcp connection and creates a thread to handle it""" diff --git a/copyparty/mpsrv.py b/copyparty/mpsrv.py index 827f7c16..97e235bd 100644 --- a/copyparty/mpsrv.py +++ b/copyparty/mpsrv.py @@ -108,10 +108,10 @@ class MpSrv(object): self.args = args self.disconnect_func = None - self.procs = [] - self.mutex = threading.Lock() + self.procs = [] + cores = args.j if cores is None: cores = mp.cpu_count() diff --git a/docs/example.conf b/docs/example.conf new file mode 100644 index 00000000..26577810 --- /dev/null +++ b/docs/example.conf @@ -0,0 +1,38 @@ +# any line with a : creates a user, +# username:password +# so you can create users anywhere really +# but keeping them here is prob a good idea +ed:123 +k:k + +# leave a blank line before each volume + +# this is a volume, +# it shares the contents of /home/... +# and appears at "/dj" in the web-ui +# "r" grants read-access for anyone +# "a ed" grants read-write to ed +/home/ed/Music/dj +/dj +r +a ed + +# display /home/ed/ocv.me as the webroot +# and allow user "k" to see/read it +/home/ed/ocv.me +/ +r k + +# this shares the current directory as "/pwd" +# but does nothing since there's no permissions +. +/pwd + +# and a folder where anyone can upload +# but nobody can see the contents +/home/ed/inc +/incoming +w + +# you can use relative paths too btw +# but they're a pain for testing purpose so I didn't diff --git a/docs/notes.sh b/docs/notes.sh index e0c3421e..d0ec6298 100644 --- a/docs/notes.sh +++ b/docs/notes.sh @@ -1,14 +1,31 @@ +#!/bin/bash +echo not a script +exit 1 + + ## ## prep debug env (vscode embedded terminal) renice 20 -p $$ +## +## cleanup after a busted shutdown + +ps ax | awk '/python[23]?[ ]-m copyparty/ {print $1}' | tee /dev/stderr | xargs kill + + +## +## create a test payload + +head -c $((2*1024*1024*1024)) /dev/zero | openssl enc -aes-256-ctr -pass pass:hunter2 -nosalt > garbage.file + + ## ## testing multiple parallel uploads ## usage: para | tee log -para() { for s in 1 2 3 4 5 6 7 8 12 16 24 32 48 64; do echo $s; for r in {1..5}; do for ((n=0;n&1 & done; wait; echo; done; done; } +para() { for s in 1 2 3 4 5 6 7 8 12 16 24 32 48 64; do echo $s; for r in {1..4}; do for ((n=0;n&1 & done; wait; echo; done; done; } ##