1
0
mirror of https://blitiri.com.ar/repos/chasquid synced 2025-12-17 14:37:02 +00:00

chasquid-util: Remove dependency on docopt-go

The docopt-go library is quite convenient, but it has been abandoned for
a while :(

Since we only use it for chasquid-util, this patch removes it and
replaces it with a custom small parser, that is a reasonable fit for the
required use cases.

The patch also adds a couple of tests to increase coverage.

NOTE: docopt-go accepted some undocumented behaviour, in particular the
use of "-a b" instead of "-a=b". The new parser does not, so some
user scripts may require updating.

I think this should be rare enough not to be worth the complexity of
adjusting the parser to allow it.
This commit is contained in:
Alberto Bertogli
2021-01-16 14:19:50 +00:00
parent def6e1cee2
commit 5c09138db8
6 changed files with 102 additions and 25 deletions

View File

@@ -6,15 +6,16 @@
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/url" "net/url"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings"
"syscall" "syscall"
"bytes"
"blitiri.com.ar/go/chasquid/internal/aliases" "blitiri.com.ar/go/chasquid/internal/aliases"
"blitiri.com.ar/go/chasquid/internal/config" "blitiri.com.ar/go/chasquid/internal/config"
"blitiri.com.ar/go/chasquid/internal/envelope" "blitiri.com.ar/go/chasquid/internal/envelope"
@@ -22,11 +23,12 @@ import (
"blitiri.com.ar/go/chasquid/internal/userdb" "blitiri.com.ar/go/chasquid/internal/userdb"
"google.golang.org/protobuf/encoding/prototext" "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" "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 = ` const usage = `
Usage: Usage:
chasquid-util [options] user-add <user@domain> [--password=<password>] chasquid-util [options] user-add <user@domain> [--password=<password>]
@@ -39,11 +41,16 @@ Usage:
chasquid-util [options] aliases-add <source> <target> chasquid-util [options] aliases-add <source> <target>
Options: Options:
-C --configdir=<path> Configuration directory -C=<path>, --configdir=<path> Configuration directory
` `
// Command-line arguments. // 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. // Globals, loaded from top-level options.
var ( var (
@@ -51,10 +58,18 @@ var (
) )
func main() { func main() {
args, _ = docopt.ParseDoc(usage) args = parseArgs(usage)
if _, ok := args["--help"]; ok {
fmt.Print(usage)
return
}
// Load globals. // 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 configDir = d
} }
@@ -69,10 +84,12 @@ func main() {
"aliases-add": aliasesAdd, "aliases-add": aliasesAdd,
} }
for cmd, f := range commands { cmd := args["$1"]
if args[cmd].(bool) { if f, ok := commands[cmd]; ok {
f() 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 { func userDBForDomain(domain string) string {
if domain == "" { if domain == "" {
domain = args["<domain>"].(string) domain = args["$2"]
} }
return configDir + "/domains/" + domain + "/users" return configDir + "/domains/" + domain + "/users"
} }
func userDBFromArgs(create bool) (string, string, *userdb.DB) { func userDBFromArgs(create bool) (string, string, *userdb.DB) {
username := args["<user@domain>"].(string) username := args["$2"]
user, domain := envelope.Split(username) user, domain := envelope.Split(username)
if domain == "" { if domain == "" {
Fatalf("Domain missing, username should be of the form 'user@domain'") Fatalf("Domain missing, username should be of the form 'user@domain'")
@@ -159,7 +176,7 @@ func authenticate() {
} }
func getPassword() string { func getPassword() string {
password, ok := args["--password"].(string) password, ok := args["--password"]
if ok { if ok {
return password return password
} }
@@ -236,7 +253,7 @@ func aliasesResolve() {
} }
} }
rcpts, err := r.Resolve(args["<address>"].(string)) rcpts, err := r.Resolve(args["$2"])
if err != nil { if err != nil {
Fatalf("Error resolving: %v", err) Fatalf("Error resolving: %v", err)
} }
@@ -258,7 +275,7 @@ func printConfig() {
// chasquid-util domaininfo-remove <domain> // chasquid-util domaininfo-remove <domain>
func domaininfoRemove() { func domaininfoRemove() {
domain := args["<domain>"].(string) domain := args["$2"]
conf, err := config.Load(configDir+"/chasquid.conf", "") conf, err := config.Load(configDir+"/chasquid.conf", "")
if err != nil { if err != nil {
@@ -277,14 +294,18 @@ func domaininfoRemove() {
// chasquid-util aliases-add <source> <target> // chasquid-util aliases-add <source> <target>
func aliasesAdd() { func aliasesAdd() {
source := args["<source>"].(string) source := args["$2"]
target := args["<target>"].(string) target := args["$3"]
user, domain := envelope.Split(source) user, domain := envelope.Split(source)
if domain == "" { if domain == "" {
Fatalf("Domain required in source address") Fatalf("Domain required in source address")
} }
if target == "" {
Fatalf("Target must be present")
}
// Ensure the domain exists. // Ensure the domain exists.
if _, err := os.Stat(filepath.Join(configDir, "domains", domain)); os.IsNotExist(err) { if _, err := os.Stat(filepath.Join(configDir, "domains", domain)); os.IsNotExist(err) {
Fatalf("Domain doesn't exist") Fatalf("Domain doesn't exist")
@@ -324,3 +345,33 @@ func aliasesAdd() {
aliasesFile.Close() aliasesFile.Close()
fmt.Println("Added alias") 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
}

View File

@@ -8,7 +8,7 @@ init
go build || exit 1 go build || exit 1
function r() { function r() {
./chasquid-util -C .config "$@" ./chasquid-util -C=.config "$@"
} }
function check_userdb() { function check_userdb() {
@@ -44,6 +44,22 @@ if r authenticate user@domain --password=abcd > /dev/null; then
exit 1 exit 1
fi 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 if ! r user-remove user@domain > /dev/null; then
echo user-remove failed echo user-remove failed
exit 1 exit 1
@@ -99,4 +115,9 @@ if r aliases-add alias3@notexist target > /dev/null; then
exit 1 exit 1
fi fi
if r aliases-add alias4@domain > /dev/null; then
echo aliases-add without target worked
exit 1
fi
success success

View File

@@ -5,6 +5,14 @@ This file contains notes for each release, summarizing changes and explicitly
noting backward-incompatible changes or known security issues. 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) ## 1.6 (2020-11-22)
- Pass the EHLO domain to the post-data hook. - Pass the EHLO domain to the post-data hook.

1
go.mod
View File

@@ -6,7 +6,6 @@ require (
blitiri.com.ar/go/log v1.1.0 blitiri.com.ar/go/log v1.1.0
blitiri.com.ar/go/spf v1.1.1 blitiri.com.ar/go/spf v1.1.1
blitiri.com.ar/go/systemd v1.1.0 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/golang/protobuf v1.4.2
github.com/google/go-cmp v0.4.0 github.com/google/go-cmp v0.4.0
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37

2
go.sum
View File

@@ -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/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 h1:AMr7Ce/5CkvLZvGxsn/ZOagzFf3zU13rcgWdlbWMQ+Y=
blitiri.com.ar/go/systemd v1.1.0/go.mod h1:0D9Ttrh+TX+WuKQ/dJpdhFND7NYy505v6jhsWrihmPY= 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/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.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=

View File

@@ -65,9 +65,9 @@ function add_user() {
DOMAIN=$(echo $1 | cut -d @ -f 2) DOMAIN=$(echo $1 | cut -d @ -f 2)
mkdir -p "${CONFDIR}/domains/$DOMAIN/" mkdir -p "${CONFDIR}/domains/$DOMAIN/"
go run ${TBASE}/../../cmd/chasquid-util/chasquid-util.go \ go run ${TBASE}/../../cmd/chasquid-util/chasquid-util.go \
-C "${CONFDIR}" \ -C="${CONFDIR}" \
user-add "$1" \ user-add "$1" \
--password "$2" \ --password="$2" \
>> .add_user_logs >> .add_user_logs
} }