git » chasquid » commit fa1db7d

config: Support "" values for `drop_characters` and `suffix_separators`

author Alberto Bertogli
2022-01-14 01:13:31 UTC
committer Alberto Bertogli
2022-01-21 12:07:34 UTC
parent 4f595ce3d233722e94c8e71ff365726e3f18af51

config: Support "" values for `drop_characters` and `suffix_separators`

If the `drop_characters` or `suffix_separators` options are set to "",
currently instead of the empty string, their default value is used instead.

This is a bug, and it also happens on other config options, but because
the others have to be set in order for chasquid to function, it's not a
problem in practice.

Thanks Björn Busse (bbusse@github) for finding and reporting this
problem, on irc and in https://github.com/albertito/chasquid/issues/25.

This patch fixes the problem by marking these fields explicitly
optional, which enables presence testing, as described in the protobuf
documentation:
https://github.com/protocolbuffers/protobuf/blob/master/docs/field_presence.md.

chasquid.go +1 -1
cmd/chasquid-util/chasquid-util.go +4 -4
internal/config/config.go +15 -7
internal/config/config.pb.go +34 -30
internal/config/config.proto +2 -2
internal/config/config_test.go +8 -2

diff --git a/chasquid.go b/chasquid.go
index ccafeb7..4745f64 100644
--- a/chasquid.go
+++ b/chasquid.go
@@ -102,7 +102,7 @@ func main() {
 	s.HookPath = "hooks/"
 	s.HAProxyEnabled = conf.HaproxyIncoming
 
-	s.SetAliasesConfig(conf.SuffixSeparators, conf.DropCharacters)
+	s.SetAliasesConfig(*conf.SuffixSeparators, *conf.DropCharacters)
 
 	if conf.DovecotAuth {
 		loadDovecot(s, conf.DovecotUserdbPath, conf.DovecotClientPath)
diff --git a/cmd/chasquid-util/chasquid-util.go b/cmd/chasquid-util/chasquid-util.go
index feb5f3d..e932ba8 100644
--- a/cmd/chasquid-util/chasquid-util.go
+++ b/cmd/chasquid-util/chasquid-util.go
@@ -225,8 +225,8 @@ func aliasesResolve() {
 	_ = os.Chdir(configDir)
 
 	r := aliases.NewResolver()
-	r.SuffixSep = conf.SuffixSeparators
-	r.DropChars = conf.DropCharacters
+	r.SuffixSep = *conf.SuffixSeparators
+	r.DropChars = *conf.DropCharacters
 
 	domainDirs, err := ioutil.ReadDir("domains/")
 	if err != nil {
@@ -316,8 +316,8 @@ func aliasesAdd() {
 
 	// Setup alias resolver.
 	r := aliases.NewResolver()
-	r.SuffixSep = conf.SuffixSeparators
-	r.DropChars = conf.DropCharacters
+	r.SuffixSep = *conf.SuffixSeparators
+	r.DropChars = *conf.DropCharacters
 
 	r.AddDomain(domain)
 	aliasesFilePath := filepath.Join("domains", domain, "aliases")
diff --git a/internal/config/config.go b/internal/config/config.go
index 6616856..efde353 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -2,7 +2,7 @@
 package config
 
 // Generate the config protobuf.
-//go:generate protoc --go_out=. --go_opt=paths=source_relative config.proto
+//go:generate protoc --go_out=. --go_opt=paths=source_relative --experimental_allow_proto3_optional config.proto
 
 import (
 	"fmt"
@@ -27,8 +27,8 @@ var defaultConfig = &Config{
 
 	DataDir: "/var/lib/chasquid",
 
-	SuffixSeparators: "+",
-	DropCharacters:   ".",
+	SuffixSeparators: proto.String("+"),
+	DropCharacters:   proto.String("."),
 
 	MailLogPath: "<syslog>",
 }
@@ -104,10 +104,10 @@ func override(c, o *Config) {
 		c.DataDir = o.DataDir
 	}
 
-	if o.SuffixSeparators != "" {
+	if o.SuffixSeparators != nil {
 		c.SuffixSeparators = o.SuffixSeparators
 	}
-	if o.DropCharacters != "" {
+	if o.DropCharacters != nil {
 		c.DropCharacters = o.DropCharacters
 	}
 	if o.MailLogPath != "" {
@@ -140,8 +140,16 @@ func LogConfig(c *Config) {
 	log.Infof("  Monitoring address: %s", c.MonitoringAddress)
 	log.Infof("  MDA: %s %v", c.MailDeliveryAgentBin, c.MailDeliveryAgentArgs)
 	log.Infof("  Data directory: %s", c.DataDir)
-	log.Infof("  Suffix separators: %s", c.SuffixSeparators)
-	log.Infof("  Drop characters: %s", c.DropCharacters)
+	if c.SuffixSeparators == nil {
+		log.Infof("  Suffix separators: nil")
+	} else {
+		log.Infof("  Suffix separators: %s", *c.SuffixSeparators)
+	}
+	if c.DropCharacters == nil {
+		log.Infof("  Drop characters: nil")
+	} else {
+		log.Infof("  Drop characters: %s", *c.DropCharacters)
+	}
 	log.Infof("  Mail log: %s", c.MailLogPath)
 	log.Infof("  Dovecot auth: %v (%q, %q)",
 		c.DovecotAuth, c.DovecotUserdbPath, c.DovecotClientPath)
diff --git a/internal/config/config.pb.go b/internal/config/config.pb.go
index 1be7333..7d5c50a 100644
--- a/internal/config/config.pb.go
+++ b/internal/config/config.pb.go
@@ -1,6 +1,6 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.26.0
+// 	protoc-gen-go v1.27.1
 // 	protoc        v3.12.4
 // source: config.proto
 
@@ -80,12 +80,12 @@ type Config struct {
 	// Including "+" is strongly encouraged, as it is assumed for email
 	// forwarding.
 	// Default: "+".
-	SuffixSeparators string `protobuf:"bytes,10,opt,name=suffix_separators,json=suffixSeparators,proto3" json:"suffix_separators,omitempty"`
+	SuffixSeparators *string `protobuf:"bytes,10,opt,name=suffix_separators,json=suffixSeparators,proto3,oneof" json:"suffix_separators,omitempty"`
 	// Characters to drop from the user part on local emails.
 	// For example, if you set this to "._", email to local user
 	// "u.se_r" will be delivered to "user".
 	// Default: ".".
-	DropCharacters string `protobuf:"bytes,11,opt,name=drop_characters,json=dropCharacters,proto3" json:"drop_characters,omitempty"`
+	DropCharacters *string `protobuf:"bytes,11,opt,name=drop_characters,json=dropCharacters,proto3,oneof" json:"drop_characters,omitempty"`
 	// Path where to write the mail log to.
 	// If "<syslog>", log using the syslog (at MAIL|INFO priority).
 	// If "<stdout>", log to stdout; if "<stderr>", log to stderr.
@@ -205,15 +205,15 @@ func (x *Config) GetDataDir() string {
 }
 
 func (x *Config) GetSuffixSeparators() string {
-	if x != nil {
-		return x.SuffixSeparators
+	if x != nil && x.SuffixSeparators != nil {
+		return *x.SuffixSeparators
 	}
 	return ""
 }
 
 func (x *Config) GetDropCharacters() string {
-	if x != nil {
-		return x.DropCharacters
+	if x != nil && x.DropCharacters != nil {
+		return *x.DropCharacters
 	}
 	return ""
 }
@@ -256,7 +256,7 @@ func (x *Config) GetHaproxyIncoming() bool {
 var File_config_proto protoreflect.FileDescriptor
 
 var file_config_proto_rawDesc = []byte{
-	0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc0,
+	0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf4,
 	0x05, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73,
 	0x74, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73,
 	0x74, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x10, 0x6d, 0x61, 0x78, 0x5f, 0x64, 0x61, 0x74,
@@ -282,29 +282,32 @@ var file_config_proto_rawDesc = []byte{
 	0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x15, 0x6d, 0x61, 0x69, 0x6c, 0x44, 0x65, 0x6c,
 	0x69, 0x76, 0x65, 0x72, 0x79, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x41, 0x72, 0x67, 0x73, 0x12, 0x19,
 	0x0a, 0x08, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x07, 0x64, 0x61, 0x74, 0x61, 0x44, 0x69, 0x72, 0x12, 0x2b, 0x0a, 0x11, 0x73, 0x75, 0x66,
+	0x52, 0x07, 0x64, 0x61, 0x74, 0x61, 0x44, 0x69, 0x72, 0x12, 0x30, 0x0a, 0x11, 0x73, 0x75, 0x66,
 	0x66, 0x69, 0x78, 0x5f, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x0a,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x53, 0x65, 0x70, 0x61,
-	0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x63,
-	0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x0e, 0x64, 0x72, 0x6f, 0x70, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x12,
-	0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x69, 0x6c, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68,
-	0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x61, 0x69, 0x6c, 0x4c, 0x6f, 0x67, 0x50,
-	0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x6f, 0x76, 0x65, 0x63, 0x6f, 0x74, 0x5f, 0x61,
-	0x75, 0x74, 0x68, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x64, 0x6f, 0x76, 0x65, 0x63,
-	0x6f, 0x74, 0x41, 0x75, 0x74, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x6f, 0x76, 0x65, 0x63, 0x6f,
-	0x74, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x64, 0x62, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0e, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x11, 0x64, 0x6f, 0x76, 0x65, 0x63, 0x6f, 0x74, 0x55, 0x73, 0x65, 0x72,
-	0x64, 0x62, 0x50, 0x61, 0x74, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x64, 0x6f, 0x76, 0x65, 0x63, 0x6f,
-	0x74, 0x5f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0f, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x11, 0x64, 0x6f, 0x76, 0x65, 0x63, 0x6f, 0x74, 0x43, 0x6c, 0x69, 0x65,
-	0x6e, 0x74, 0x50, 0x61, 0x74, 0x68, 0x12, 0x29, 0x0a, 0x10, 0x68, 0x61, 0x70, 0x72, 0x6f, 0x78,
-	0x79, 0x5f, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08,
-	0x52, 0x0f, 0x68, 0x61, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e,
-	0x67, 0x42, 0x2c, 0x5a, 0x2a, 0x62, 0x6c, 0x69, 0x74, 0x69, 0x72, 0x69, 0x2e, 0x63, 0x6f, 0x6d,
-	0x2e, 0x61, 0x72, 0x2f, 0x67, 0x6f, 0x2f, 0x63, 0x68, 0x61, 0x73, 0x71, 0x75, 0x69, 0x64, 0x2f,
-	0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x62,
-	0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x10, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x53, 0x65,
+	0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72, 0x73, 0x88, 0x01, 0x01, 0x12, 0x2c, 0x0a, 0x0f, 0x64,
+	0x72, 0x6f, 0x70, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x18, 0x0b,
+	0x20, 0x01, 0x28, 0x09, 0x48, 0x01, 0x52, 0x0e, 0x64, 0x72, 0x6f, 0x70, 0x43, 0x68, 0x61, 0x72,
+	0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x88, 0x01, 0x01, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x69,
+	0x6c, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0b, 0x6d, 0x61, 0x69, 0x6c, 0x4c, 0x6f, 0x67, 0x50, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a,
+	0x0c, 0x64, 0x6f, 0x76, 0x65, 0x63, 0x6f, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x18, 0x0d, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x0b, 0x64, 0x6f, 0x76, 0x65, 0x63, 0x6f, 0x74, 0x41, 0x75, 0x74, 0x68,
+	0x12, 0x2e, 0x0a, 0x13, 0x64, 0x6f, 0x76, 0x65, 0x63, 0x6f, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x72,
+	0x64, 0x62, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x64,
+	0x6f, 0x76, 0x65, 0x63, 0x6f, 0x74, 0x55, 0x73, 0x65, 0x72, 0x64, 0x62, 0x50, 0x61, 0x74, 0x68,
+	0x12, 0x2e, 0x0a, 0x13, 0x64, 0x6f, 0x76, 0x65, 0x63, 0x6f, 0x74, 0x5f, 0x63, 0x6c, 0x69, 0x65,
+	0x6e, 0x74, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x64,
+	0x6f, 0x76, 0x65, 0x63, 0x6f, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x74, 0x68,
+	0x12, 0x29, 0x0a, 0x10, 0x68, 0x61, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x69, 0x6e, 0x63, 0x6f,
+	0x6d, 0x69, 0x6e, 0x67, 0x18, 0x10, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x68, 0x61, 0x70, 0x72,
+	0x6f, 0x78, 0x79, 0x49, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x42, 0x14, 0x0a, 0x12, 0x5f,
+	0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x5f, 0x73, 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x6f, 0x72,
+	0x73, 0x42, 0x12, 0x0a, 0x10, 0x5f, 0x64, 0x72, 0x6f, 0x70, 0x5f, 0x63, 0x68, 0x61, 0x72, 0x61,
+	0x63, 0x74, 0x65, 0x72, 0x73, 0x42, 0x2c, 0x5a, 0x2a, 0x62, 0x6c, 0x69, 0x74, 0x69, 0x72, 0x69,
+	0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x72, 0x2f, 0x67, 0x6f, 0x2f, 0x63, 0x68, 0x61, 0x73, 0x71,
+	0x75, 0x69, 0x64, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -350,6 +353,7 @@ func file_config_proto_init() {
 			}
 		}
 	}
+	file_config_proto_msgTypes[0].OneofWrappers = []interface{}{}
 	type x struct{}
 	out := protoimpl.TypeBuilder{
 		File: protoimpl.DescBuilder{
diff --git a/internal/config/config.proto b/internal/config/config.proto
index 54ecf95..b7a55b9 100644
--- a/internal/config/config.proto
+++ b/internal/config/config.proto
@@ -67,13 +67,13 @@ message Config {
 	// Including "+" is strongly encouraged, as it is assumed for email
 	// forwarding.
 	// Default: "+".
-	string suffix_separators = 10;
+	optional string suffix_separators = 10;
 
 	// Characters to drop from the user part on local emails.
 	// For example, if you set this to "._", email to local user
 	// "u.se_r" will be delivered to "user".
 	// Default: ".".
-	string drop_characters = 11;
+	optional string drop_characters = 11;
 
 	// Path where to write the mail log to.
 	// If "<syslog>", log using the syslog (at MAIL|INFO priority).
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index d539187..ad36cf2 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -24,6 +24,10 @@ func mustCreateConfig(t *testing.T, contents string) (string, string) {
 	return tmpDir, tmpDir + "/chasquid.conf"
 }
 
+func TestEmptyStruct(t *testing.T) {
+	testLogConfig(&Config{})
+}
+
 func TestEmptyConfig(t *testing.T) {
 	tmpDir, path := mustCreateConfig(t, "")
 	defer testlib.RemoveIfOk(t, tmpDir)
@@ -54,6 +58,7 @@ func TestFullConfig(t *testing.T) {
 		submission_address: ":10002"
 		monitoring_address: ":1111"
 		max_data_size_mb: 26
+		suffix_separators: ""
 	`
 
 	tmpDir, path := mustCreateConfig(t, confStr)
@@ -63,6 +68,7 @@ func TestFullConfig(t *testing.T) {
 		hostname: "proust"
 		submission_address: ":999"
 		dovecot_auth: true
+		drop_characters: ""
 	`
 
 	expected := &Config{
@@ -79,8 +85,8 @@ func TestFullConfig(t *testing.T) {
 
 		DataDir: "/var/lib/chasquid",
 
-		SuffixSeparators: "+",
-		DropCharacters:   ".",
+		SuffixSeparators: proto.String(""),
+		DropCharacters:   proto.String(""),
 
 		MailLogPath: "<syslog>",