mirror of
https://github.com/1f349/lotus.git
synced 2024-12-22 08:04:06 +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"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="DiscordProjectSettings">
|
|
||||||
<option name="show" value="ASK" />
|
|
||||||
<option name="description" value="" />
|
|
||||||
</component>
|
|
||||||
<component name="MarkdownSettingsMigration">
|
<component name="MarkdownSettingsMigration">
|
||||||
<option name="stateVersion" value="1" />
|
<option name="stateVersion" value="1" />
|
||||||
</component>
|
</component>
|
||||||
|
@ -3,6 +3,7 @@ package comma_list_scanner
|
|||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,8 +19,15 @@ func NewCommaListScanner(r io.Reader) *CommaListScanner {
|
|||||||
if atEOF && len(data) == 0 {
|
if atEOF && len(data) == 0 {
|
||||||
return 0, nil, nil
|
return 0, nil, nil
|
||||||
}
|
}
|
||||||
if i := bytes.IndexByte(data, ','); i >= 0 {
|
println("data", fmt.Sprintf("%s", data))
|
||||||
return i + 1, bytes.TrimSpace(data[0:i]), nil
|
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 we're at EOF, we have a final non-terminated line. Return it.
|
||||||
if atEOF {
|
if atEOF {
|
||||||
@ -34,6 +42,7 @@ func NewCommaListScanner(r io.Reader) *CommaListScanner {
|
|||||||
func (c *CommaListScanner) Scan() bool {
|
func (c *CommaListScanner) Scan() bool {
|
||||||
if c.r.Scan() {
|
if c.r.Scan() {
|
||||||
c.text = c.r.Text()
|
c.text = c.r.Text()
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
c.err = c.r.Err()
|
c.err = c.r.Err()
|
||||||
return false
|
return false
|
||||||
@ -42,3 +51,7 @@ func (c *CommaListScanner) Scan() bool {
|
|||||||
func (c *CommaListScanner) Text() string {
|
func (c *CommaListScanner) Text() string {
|
||||||
return c.text
|
return c.text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *CommaListScanner) Err() error {
|
||||||
|
return c.err
|
||||||
|
}
|
||||||
|
@ -6,22 +6,40 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testCommaList = []struct {
|
var testCommaList = []string{
|
||||||
text string
|
"hello, wow-this-is-cool, amazing",
|
||||||
out []string
|
"hello, wow-this-is-cool",
|
||||||
}{
|
"hello, wow-this-is-cool, ",
|
||||||
{"hello, wow this is cool, amazing", []string{"hello", "wow this is cool", "amazing"}},
|
"hello, wow-this-is-cool,",
|
||||||
{"hello, wow this is cool, amazing", []string{"hello", "wow this is cool", "amazing"}},
|
",hello, wow-this-is-cool",
|
||||||
|
",hello, wow-this-is-cool,",
|
||||||
|
"hello, wow-this-is-cool,,,",
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewCommaListScanner(t *testing.T) {
|
func TestNewCommaListScanner(t *testing.T) {
|
||||||
for _, i := range testCommaList {
|
for _, i := range testCommaList {
|
||||||
t.Run(i.text, func(t *testing.T) {
|
t.Run(i, func(t *testing.T) {
|
||||||
s := NewCommaListScanner(strings.NewReader(i.text))
|
// use comma list scanner
|
||||||
n := 0
|
s := NewCommaListScanner(strings.NewReader(i))
|
||||||
|
n := strings.Count(i, ",")
|
||||||
|
a := make([]string, 0, n+1)
|
||||||
for s.Scan() {
|
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) {
|
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 {
|
func (c *ConfigParser) Err() error {
|
||||||
|
@ -9,7 +9,20 @@ type Config struct {
|
|||||||
VirtualMailboxMaps mapProvider.MapProvider
|
VirtualMailboxMaps mapProvider.MapProvider
|
||||||
AliasMaps mapProvider.MapProvider
|
AliasMaps mapProvider.MapProvider
|
||||||
LocalRecipientMaps 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) {
|
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":
|
case "local_recipient_maps":
|
||||||
c.LocalRecipientMaps = m
|
c.LocalRecipientMaps = m
|
||||||
case "smtpd_sender_login_maps":
|
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
|
package postfix_config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
commaListScanner "github.com/1f349/lotus/postfix-config/comma-list-scanner"
|
||||||
configParser "github.com/1f349/lotus/postfix-config/config-parser"
|
configParser "github.com/1f349/lotus/postfix-config/config-parser"
|
||||||
mapProvider "github.com/1f349/lotus/postfix-config/map-provider"
|
mapProvider "github.com/1f349/lotus/postfix-config/map-provider"
|
||||||
"io"
|
"io"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Decoder struct {
|
type Decoder struct {
|
||||||
r *configParser.ConfigParser
|
r *configParser.ConfigParser
|
||||||
v *Config
|
temp map[string]string
|
||||||
t map[string]string
|
basePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDecoder(r io.Reader) *Decoder {
|
func NewDecoder(r io.Reader) *Decoder {
|
||||||
@ -20,30 +22,87 @@ func NewDecoder(r io.Reader) *Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Decoder) Load() error {
|
func (d *Decoder) Load() error {
|
||||||
d.v = &Config{}
|
|
||||||
for d.r.Scan() {
|
for d.r.Scan() {
|
||||||
k, v := d.r.Pair()
|
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{}
|
m := mapProvider.SequenceMapProvider{}
|
||||||
|
|
||||||
s := bufio.NewScanner(strings.NewReader(v))
|
s := commaListScanner.NewCommaListScanner(strings.NewReader(v))
|
||||||
s.Split(bufio.ScanWords)
|
|
||||||
for s.Scan() {
|
for s.Scan() {
|
||||||
a := s.Text()
|
a := s.Text()
|
||||||
println("a", a)
|
println("a", a)
|
||||||
if strings.HasPrefix(a, "$") {
|
if strings.HasPrefix(a, "$") {
|
||||||
// is variable
|
m = append(m, &mapProvider.Variable{Name: a[1:]})
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
n := strings.IndexByte(a, ':')
|
|
||||||
if n == -1 {
|
v2, err := d.createValue(a)
|
||||||
return fmt.Errorf("missing prefix")
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
m = append(m, v2)
|
||||||
}
|
}
|
||||||
if err := s.Err(); err != nil {
|
if err := s.Err(); err != nil {
|
||||||
return err
|
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()
|
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"
|
_ "embed"
|
||||||
configParser "github.com/1f349/lotus/postfix-config/config-parser"
|
configParser "github.com/1f349/lotus/postfix-config/config-parser"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -12,7 +14,15 @@ import (
|
|||||||
var exampleConfig []byte
|
var exampleConfig []byte
|
||||||
|
|
||||||
func TestDecoder_Load(t *testing.T) {
|
func TestDecoder_Load(t *testing.T) {
|
||||||
|
// get working directory
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// read example config
|
||||||
b := bytes.NewReader(exampleConfig)
|
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())
|
assert.NoError(t, d.Load())
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
# this only contains the relevant config properties
|
# this only contains the relevant config properties
|
||||||
|
|
||||||
recipient_delimiter = +
|
#recipient_delimiter = +
|
||||||
|
|
||||||
virtual_mailbox_domains = mysql:/etc/postfix/sql/mysql_virtual_domains_maps.cf
|
virtual_mailbox_domains = mysql: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_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:/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
|
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:/etc/aliases $virtual_alias_maps
|
alias_maps = hash:aliases.txt $virtual_alias_maps
|
||||||
local_recipient_maps = $virtual_mailbox_maps $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