mirror of
https://github.com/1f349/lotus.git
synced 2024-11-12 15:01:39 +00:00
Some improvements to postfix config tests
This commit is contained in:
parent
7698bc9d50
commit
d6048ccc6e
7
.idea/discord.xml
Normal file
7
.idea/discord.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
@ -1,9 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="ASK" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
<component name="MarkdownSettingsMigration">
|
||||
<option name="stateVersion" value="1" />
|
||||
</component>
|
||||
|
@ -3,6 +3,7 @@ package comma_list_scanner
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
@ -18,8 +19,15 @@ func NewCommaListScanner(r io.Reader) *CommaListScanner {
|
||||
if atEOF && len(data) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
if i := bytes.IndexByte(data, ','); i >= 0 {
|
||||
return i + 1, bytes.TrimSpace(data[0:i]), nil
|
||||
println("data", fmt.Sprintf("%s", data))
|
||||
println("index", bytes.IndexAny(data, " ,"))
|
||||
if i := bytes.IndexAny(data, " ,"); i >= 0 {
|
||||
// consume all spaces after the comma
|
||||
j := i + 1
|
||||
for j < len(data) && data[j] == ' ' {
|
||||
j++
|
||||
}
|
||||
return j, bytes.TrimSpace(data[0:i]), nil
|
||||
}
|
||||
// If we're at EOF, we have a final non-terminated line. Return it.
|
||||
if atEOF {
|
||||
@ -34,6 +42,7 @@ func NewCommaListScanner(r io.Reader) *CommaListScanner {
|
||||
func (c *CommaListScanner) Scan() bool {
|
||||
if c.r.Scan() {
|
||||
c.text = c.r.Text()
|
||||
return true
|
||||
}
|
||||
c.err = c.r.Err()
|
||||
return false
|
||||
@ -42,3 +51,7 @@ func (c *CommaListScanner) Scan() bool {
|
||||
func (c *CommaListScanner) Text() string {
|
||||
return c.text
|
||||
}
|
||||
|
||||
func (c *CommaListScanner) Err() error {
|
||||
return c.err
|
||||
}
|
||||
|
@ -6,22 +6,40 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testCommaList = []struct {
|
||||
text string
|
||||
out []string
|
||||
}{
|
||||
{"hello, wow this is cool, amazing", []string{"hello", "wow this is cool", "amazing"}},
|
||||
{"hello, wow this is cool, amazing", []string{"hello", "wow this is cool", "amazing"}},
|
||||
var testCommaList = []string{
|
||||
"hello, wow-this-is-cool, amazing",
|
||||
"hello, wow-this-is-cool",
|
||||
"hello, wow-this-is-cool, ",
|
||||
"hello, wow-this-is-cool,",
|
||||
",hello, wow-this-is-cool",
|
||||
",hello, wow-this-is-cool,",
|
||||
"hello, wow-this-is-cool,,,",
|
||||
}
|
||||
|
||||
func TestNewCommaListScanner(t *testing.T) {
|
||||
for _, i := range testCommaList {
|
||||
t.Run(i.text, func(t *testing.T) {
|
||||
s := NewCommaListScanner(strings.NewReader(i.text))
|
||||
n := 0
|
||||
t.Run(i, func(t *testing.T) {
|
||||
// use comma list scanner
|
||||
s := NewCommaListScanner(strings.NewReader(i))
|
||||
n := strings.Count(i, ",")
|
||||
a := make([]string, 0, n+1)
|
||||
for s.Scan() {
|
||||
assert.Equal(t, i.out[n], s.Text())
|
||||
a = append(a, s.Text())
|
||||
}
|
||||
assert.NoError(t, s.Err())
|
||||
|
||||
// test against splitting and trimming strings
|
||||
b := strings.Split(i, ",")
|
||||
for i := 0; i < len(b); i++ {
|
||||
c := strings.TrimSpace(b[i])
|
||||
if c == "" {
|
||||
b = append(b[0:i], b[i+1:]...)
|
||||
i--
|
||||
} else {
|
||||
b[i] = c
|
||||
}
|
||||
}
|
||||
assert.Equal(t, b, a)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ scanAgain:
|
||||
}
|
||||
|
||||
func (c *ConfigParser) Pair() (string, string) {
|
||||
return c.pair[0], c.pair[1]
|
||||
return strings.TrimSpace(c.pair[0]), strings.TrimSpace(c.pair[1])
|
||||
}
|
||||
|
||||
func (c *ConfigParser) Err() error {
|
||||
|
@ -9,7 +9,20 @@ type Config struct {
|
||||
VirtualMailboxMaps mapProvider.MapProvider
|
||||
AliasMaps mapProvider.MapProvider
|
||||
LocalRecipientMaps mapProvider.MapProvider
|
||||
SmtpdSenderLoginMaps string // TODO(melon): union map?
|
||||
SmtpdSenderLoginMaps mapProvider.MapProvider
|
||||
}
|
||||
|
||||
var parseProviderData = map[string]string{
|
||||
"virtual_mailbox_domains": "comma",
|
||||
"virtual_alias_maps": "comma",
|
||||
"virtual_mailbox_maps": "comma",
|
||||
"alias_maps": "comma",
|
||||
"local_recipient_maps": "comma",
|
||||
"smtpd_sender_login_maps": "union",
|
||||
}
|
||||
|
||||
func (c *Config) ParseProvider(k string) string {
|
||||
return parseProviderData[k]
|
||||
}
|
||||
|
||||
func (c *Config) SetKey(k string, m mapProvider.MapProvider) {
|
||||
@ -25,14 +38,6 @@ func (c *Config) SetKey(k string, m mapProvider.MapProvider) {
|
||||
case "local_recipient_maps":
|
||||
c.LocalRecipientMaps = m
|
||||
case "smtpd_sender_login_maps":
|
||||
c.SmtpdSenderLoginMaps = "<ERROR>"
|
||||
c.SmtpdSenderLoginMaps = m
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) NeedsMapProvider(k string) bool {
|
||||
switch k {
|
||||
case "virtual_mailbox_domains", "virtual_alias_maps", "virtual_mailbox_maps", "alias_maps", "local_recipient_maps", "smtpd_sender_login_maps":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
@ -1,18 +1,20 @@
|
||||
package postfix_config
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
commaListScanner "github.com/1f349/lotus/postfix-config/comma-list-scanner"
|
||||
configParser "github.com/1f349/lotus/postfix-config/config-parser"
|
||||
mapProvider "github.com/1f349/lotus/postfix-config/map-provider"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Decoder struct {
|
||||
r *configParser.ConfigParser
|
||||
v *Config
|
||||
t map[string]string
|
||||
r *configParser.ConfigParser
|
||||
temp map[string]string
|
||||
basePath string
|
||||
}
|
||||
|
||||
func NewDecoder(r io.Reader) *Decoder {
|
||||
@ -20,30 +22,87 @@ func NewDecoder(r io.Reader) *Decoder {
|
||||
}
|
||||
|
||||
func (d *Decoder) Load() error {
|
||||
d.v = &Config{}
|
||||
for d.r.Scan() {
|
||||
k, v := d.r.Pair()
|
||||
if d.v.NeedsMapProvider(k) {
|
||||
d.temp[k] = v
|
||||
}
|
||||
if err := d.r.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch d.value.ParseProvider(k) {
|
||||
case "comma":
|
||||
m := mapProvider.SequenceMapProvider{}
|
||||
|
||||
s := bufio.NewScanner(strings.NewReader(v))
|
||||
s.Split(bufio.ScanWords)
|
||||
s := commaListScanner.NewCommaListScanner(strings.NewReader(v))
|
||||
for s.Scan() {
|
||||
a := s.Text()
|
||||
println("a", a)
|
||||
if strings.HasPrefix(a, "$") {
|
||||
// is variable
|
||||
m = append(m, &mapProvider.Variable{Name: a[1:]})
|
||||
continue
|
||||
}
|
||||
n := strings.IndexByte(a, ':')
|
||||
if n == -1 {
|
||||
return fmt.Errorf("missing prefix")
|
||||
|
||||
v2, err := d.createValue(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m = append(m, v2)
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
d.v.SetKey(k, m)
|
||||
d.value.SetKey(k, m)
|
||||
case "union":
|
||||
if !strings.HasPrefix(v, "unionmap:{") || !strings.HasSuffix(v, "}") {
|
||||
return errors.New("key requires a union map")
|
||||
}
|
||||
v = v[len("unionmap:{") : len(v)-1]
|
||||
|
||||
m := mapProvider.SequenceMapProvider{}
|
||||
s := commaListScanner.NewCommaListScanner(strings.NewReader(v))
|
||||
for s.Scan() {
|
||||
a := s.Text()
|
||||
v2, err := d.createValue(a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m = append(m, v2)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("key '%s' has no defined parse provider", k)
|
||||
}
|
||||
}
|
||||
return d.r.Err()
|
||||
}
|
||||
|
||||
func (d *Decoder) createValue(a string) (mapProvider.MapProvider, error) {
|
||||
n := strings.IndexByte(a, ':')
|
||||
if n == -1 {
|
||||
return nil, fmt.Errorf("missing prefix")
|
||||
}
|
||||
|
||||
namespace := a[:n]
|
||||
value := a[n+1:]
|
||||
switch namespace {
|
||||
case "mysql":
|
||||
if !filepath.IsAbs(value) {
|
||||
value = filepath.Join(d.basePath, value)
|
||||
}
|
||||
provider, err := mapProvider.NewMySqlMapProvider(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return provider, nil
|
||||
case "hash":
|
||||
if !filepath.IsAbs(value) {
|
||||
value = filepath.Join(d.basePath, value)
|
||||
}
|
||||
provider, err := mapProvider.NewHashMapProvider(value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return provider, nil
|
||||
}
|
||||
return nil, errors.New("invalid provider namespace")
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import (
|
||||
_ "embed"
|
||||
configParser "github.com/1f349/lotus/postfix-config/config-parser"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -12,7 +14,15 @@ import (
|
||||
var exampleConfig []byte
|
||||
|
||||
func TestDecoder_Load(t *testing.T) {
|
||||
// get working directory
|
||||
wd, err := os.Getwd()
|
||||
assert.NoError(t, err)
|
||||
|
||||
// read example config
|
||||
b := bytes.NewReader(exampleConfig)
|
||||
d := &Decoder{r: configParser.NewConfigParser(b)}
|
||||
d := &Decoder{
|
||||
r: configParser.NewConfigParser(b),
|
||||
basePath: filepath.Join(wd, "test-data"),
|
||||
}
|
||||
assert.NoError(t, d.Load())
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
# this only contains the relevant config properties
|
||||
|
||||
recipient_delimiter = +
|
||||
#recipient_delimiter = +
|
||||
|
||||
virtual_mailbox_domains = mysql:/etc/postfix/sql/mysql_virtual_domains_maps.cf
|
||||
virtual_alias_maps = mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_wildcard_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_user_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_userdomain_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_domain_catchall_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_user_catchall_maps.cf
|
||||
virtual_mailbox_maps = mysql:/etc/postfix/sql/mysql_virtual_mailbox_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_domain_mailbox_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_user_mailbox_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_userdomain_mailbox_maps.cf
|
||||
alias_maps = hash:/etc/aliases $virtual_alias_maps
|
||||
virtual_mailbox_domains = mysql:mysql_virtual_domains_maps.cf
|
||||
virtual_alias_maps = mysql:mysql_virtual_alias_maps.cf, mysql:mysql_virtual_alias_wildcard_maps.cf, mysql:mysql_virtual_alias_domain_maps.cf, mysql:mysql_virtual_alias_user_maps.cf, mysql:mysql_virtual_alias_userdomain_maps.cf, mysql:mysql_virtual_alias_domain_catchall_maps.cf, mysql:mysql_virtual_alias_user_catchall_maps.cf
|
||||
virtual_mailbox_maps = mysql:mysql_virtual_mailbox_maps.cf, mysql:mysql_virtual_alias_domain_mailbox_maps.cf, mysql:mysql_virtual_alias_user_mailbox_maps.cf, mysql:mysql_virtual_alias_userdomain_mailbox_maps.cf
|
||||
alias_maps = hash:aliases.txt $virtual_alias_maps
|
||||
local_recipient_maps = $virtual_mailbox_maps $alias_maps
|
||||
smtpd_sender_login_maps = unionmap:{ hash:/etc/aliases, mysql:/etc/postfix/sql/mysql_sender_alias_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_domain_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_user_maps.cf, mysql:/etc/postfix/sql/mysql_virtual_alias_userdomain_maps.cf }
|
||||
smtpd_sender_login_maps = unionmap:{ hash:aliases.txt, mysql:mysql_sender_alias_maps.cf, mysql:mysql_virtual_alias_maps.cf, mysql:mysql_virtual_alias_domain_maps.cf, mysql:mysql_virtual_alias_user_maps.cf, mysql:mysql_virtual_alias_userdomain_maps.cf }
|
||||
|
1
postfix-config/test-data/aliases.txt
Normal file
1
postfix-config/test-data/aliases.txt
Normal file
@ -0,0 +1 @@
|
||||
a: a b
|
0
postfix-config/test-data/mysql_sender_alias_maps.cf
Normal file
0
postfix-config/test-data/mysql_sender_alias_maps.cf
Normal file
Loading…
Reference in New Issue
Block a user