author | Alberto Bertogli
<albertito@blitiri.com.ar> 2021-01-16 14:19:50 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2021-01-16 23:21:35 UTC |
parent | def6e1cee2b78f4bd8e2ac8591532e5c59ccd58d |
cmd/chasquid-util/chasquid-util.go | +70 | -19 |
cmd/chasquid-util/test.sh | +22 | -1 |
docs/relnotes.md | +8 | -0 |
go.mod | +0 | -1 |
go.sum | +0 | -2 |
test/util/lib.sh | +2 | -2 |
diff --git a/cmd/chasquid-util/chasquid-util.go b/cmd/chasquid-util/chasquid-util.go index 5b4aef7..57a12db 100644 --- a/cmd/chasquid-util/chasquid-util.go +++ b/cmd/chasquid-util/chasquid-util.go @@ -6,15 +6,16 @@ package main import ( + "bytes" "fmt" "io/ioutil" "net/url" "os" "path/filepath" + "strconv" + "strings" "syscall" - "bytes" - "blitiri.com.ar/go/chasquid/internal/aliases" "blitiri.com.ar/go/chasquid/internal/config" "blitiri.com.ar/go/chasquid/internal/envelope" @@ -22,11 +23,12 @@ import ( "blitiri.com.ar/go/chasquid/internal/userdb" "google.golang.org/protobuf/encoding/prototext" - "github.com/docopt/docopt-go" + // TODO: Move to golang.org/x/term once we don't support Go 1.11 anymore, + // since this one is deprecated (but still fully functional, so no rush). "golang.org/x/crypto/ssh/terminal" ) -// Usage, which doubles as parameter definitions thanks to docopt. +// Usage to show users on --help or invocation errors. const usage = ` Usage: chasquid-util [options] user-add <user@domain> [--password=<password>] @@ -39,11 +41,16 @@ Usage: chasquid-util [options] aliases-add <source> <target> Options: - -C --configdir=<path> Configuration directory + -C=<path>, --configdir=<path> Configuration directory ` // Command-line arguments. -var args map[string]interface{} +// Arguments starting with "-" will be parsed as key-value pairs, and +// positional arguments will appear as "$POS" -> value. +// +// For example, "--abc=def x y -p=q -r" will result in: +// {"--abc": "def", "$1": "x", "$2": "y", "-p": "q", "-r": ""} +var args map[string]string // Globals, loaded from top-level options. var ( @@ -51,10 +58,18 @@ var ( ) func main() { - args, _ = docopt.ParseDoc(usage) + args = parseArgs(usage) + + if _, ok := args["--help"]; ok { + fmt.Print(usage) + return + } // Load globals. - if d, ok := args["--configdir"].(string); ok { + if d, ok := args["--configdir"]; ok { + configDir = d + } + if d, ok := args["-C"]; ok { configDir = d } @@ -69,10 +84,12 @@ func main() { "aliases-add": aliasesAdd, } - for cmd, f := range commands { - if args[cmd].(bool) { - f() - } + cmd := args["$1"] + if f, ok := commands[cmd]; ok { + f() + } else { + fmt.Printf("Unknown argument %q\n", cmd) + Fatalf(usage) } } @@ -84,13 +101,13 @@ func Fatalf(s string, arg ...interface{}) { func userDBForDomain(domain string) string { if domain == "" { - domain = args["<domain>"].(string) + domain = args["$2"] } return configDir + "/domains/" + domain + "/users" } func userDBFromArgs(create bool) (string, string, *userdb.DB) { - username := args["<user@domain>"].(string) + username := args["$2"] user, domain := envelope.Split(username) if domain == "" { Fatalf("Domain missing, username should be of the form 'user@domain'") @@ -159,7 +176,7 @@ func authenticate() { } func getPassword() string { - password, ok := args["--password"].(string) + password, ok := args["--password"] if ok { return password } @@ -236,7 +253,7 @@ func aliasesResolve() { } } - rcpts, err := r.Resolve(args["<address>"].(string)) + rcpts, err := r.Resolve(args["$2"]) if err != nil { Fatalf("Error resolving: %v", err) } @@ -258,7 +275,7 @@ func printConfig() { // chasquid-util domaininfo-remove <domain> func domaininfoRemove() { - domain := args["<domain>"].(string) + domain := args["$2"] conf, err := config.Load(configDir+"/chasquid.conf", "") if err != nil { @@ -277,14 +294,18 @@ func domaininfoRemove() { // chasquid-util aliases-add <source> <target> func aliasesAdd() { - source := args["<source>"].(string) - target := args["<target>"].(string) + source := args["$2"] + target := args["$3"] user, domain := envelope.Split(source) if domain == "" { Fatalf("Domain required in source address") } + if target == "" { + Fatalf("Target must be present") + } + // Ensure the domain exists. if _, err := os.Stat(filepath.Join(configDir, "domains", domain)); os.IsNotExist(err) { Fatalf("Domain doesn't exist") @@ -324,3 +345,33 @@ func aliasesAdd() { aliasesFile.Close() fmt.Println("Added alias") } + +// parseArgs parses the command line arguments, and returns a map. +// +// Arguments starting with "-" will be parsed as key-value pairs, and +// positional arguments will appear as "$POS" -> value. +// +// For example, "--abc=def x y -p=q -r" will result in: +// {"--abc": "def", "$1": "x", "$2": "y", "-p": "q", "-r": ""} +func parseArgs(usage string) map[string]string { + args := map[string]string{} + + pos := 1 + for _, a := range os.Args[1:] { + // Note: Consider handling end of args marker "--" explicitly in + // the future if needed. + if strings.HasPrefix(a, "-") { + sp := strings.SplitN(a, "=", 2) + if len(sp) < 2 { + args[a] = "" + } else { + args[sp[0]] = sp[1] + } + } else { + args["$"+strconv.Itoa(pos)] = a + pos++ + } + } + + return args +} diff --git a/cmd/chasquid-util/test.sh b/cmd/chasquid-util/test.sh index c549b51..8b884d7 100755 --- a/cmd/chasquid-util/test.sh +++ b/cmd/chasquid-util/test.sh @@ -8,7 +8,7 @@ init go build || exit 1 function r() { - ./chasquid-util -C .config "$@" + ./chasquid-util -C=.config "$@" } function check_userdb() { @@ -44,6 +44,22 @@ if r authenticate user@domain --password=abcd > /dev/null; then exit 1 fi +# Interactive authentication. +# Need to wrap the execution under "script" since the interaction requires an +# actual TTY, and that's a fairly portable way to do that. +if hash script 2>/dev/null; then + if ! (echo passwd; echo passwd ) \ + | script \ + -qfec "./chasquid-util -C=.config authenticate user@domain" \ + ".script-out" \ + | grep -q "Authentication succeeded"; + then + echo interactive authenticate failed + exit 1 + fi +fi + + if ! r user-remove user@domain > /dev/null; then echo user-remove failed exit 1 @@ -99,4 +115,9 @@ if r aliases-add alias3@notexist target > /dev/null; then exit 1 fi +if r aliases-add alias4@domain > /dev/null; then + echo aliases-add without target worked + exit 1 +fi + success diff --git a/docs/relnotes.md b/docs/relnotes.md index 61de06c..c7c96d5 100644 --- a/docs/relnotes.md +++ b/docs/relnotes.md @@ -5,6 +5,14 @@ This file contains notes for each release, summarizing changes and explicitly noting backward-incompatible changes or known security issues. +## 1.7 (TODO) + +- chasquid-util no longer depends on the unmaintained docopt-go. + If you relied on undocumented parsing behaviour before, your invocations may + need adjustment. In particular, `--a b` is no longer supported, and `--a=b` + must be used instead. + + ## 1.6 (2020-11-22) - Pass the EHLO domain to the post-data hook. diff --git a/go.mod b/go.mod index f08fe89..98f2141 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ require ( blitiri.com.ar/go/log v1.1.0 blitiri.com.ar/go/spf v1.1.1 blitiri.com.ar/go/systemd v1.1.0 - github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 github.com/golang/protobuf v1.4.2 github.com/google/go-cmp v0.4.0 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 diff --git a/go.sum b/go.sum index d03b337..49d1a3a 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,6 @@ blitiri.com.ar/go/spf v1.1.1 h1:H5MKnEe5feN4NjtPDK/vFkkS0fI+ecTIsOfLNCR+6yI= blitiri.com.ar/go/spf v1.1.1/go.mod h1:HLmgHxdrsqbBgi5omEopdAKm18PypvUKJGkF/j7BO0w= blitiri.com.ar/go/systemd v1.1.0 h1:AMr7Ce/5CkvLZvGxsn/ZOagzFf3zU13rcgWdlbWMQ+Y= blitiri.com.ar/go/systemd v1.1.0/go.mod h1:0D9Ttrh+TX+WuKQ/dJpdhFND7NYy505v6jhsWrihmPY= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= diff --git a/test/util/lib.sh b/test/util/lib.sh index b0bf3fe..b9d343c 100644 --- a/test/util/lib.sh +++ b/test/util/lib.sh @@ -65,9 +65,9 @@ function add_user() { DOMAIN=$(echo $1 | cut -d @ -f 2) mkdir -p "${CONFDIR}/domains/$DOMAIN/" go run ${TBASE}/../../cmd/chasquid-util/chasquid-util.go \ - -C "${CONFDIR}" \ + -C="${CONFDIR}" \ user-add "$1" \ - --password "$2" \ + --password="$2" \ >> .add_user_logs }