Blame


1 5e70998a 2022-04-12 o // package aliases provides encoding and decoding of Unix alias(5) files
2 5e70998a 2022-04-12 o // read by systems like Postfix and OpenBSD's smtpd(8).
3 5e70998a 2022-04-12 o package aliases
4 5e70998a 2022-04-12 o
5 5e70998a 2022-04-12 o import (
6 5e70998a 2022-04-12 o "bufio"
7 5e70998a 2022-04-12 o "errors"
8 5e70998a 2022-04-12 o "fmt"
9 5e70998a 2022-04-12 o "io"
10 5e70998a 2022-04-12 o "os"
11 5e70998a 2022-04-12 o "strings"
12 5e70998a 2022-04-12 o )
13 5e70998a 2022-04-12 o
14 5e70998a 2022-04-12 o type Aliases map[string]string
15 5e70998a 2022-04-12 o
16 5e70998a 2022-04-12 o type Encoder struct {
17 5e70998a 2022-04-12 o w *bufio.Writer
18 5e70998a 2022-04-12 o }
19 5e70998a 2022-04-12 o
20 5e70998a 2022-04-12 o func NewEncoder(w io.Writer) *Encoder {
21 5e70998a 2022-04-12 o return &Encoder{bufio.NewWriter(w)}
22 5e70998a 2022-04-12 o }
23 5e70998a 2022-04-12 o
24 5e70998a 2022-04-12 o func (e *Encoder) Encode(m Aliases) error {
25 5e70998a 2022-04-12 o defer e.w.Flush()
26 5e70998a 2022-04-12 o for rcpt, dest := range m {
27 5e70998a 2022-04-12 o _, err := e.w.WriteString(fmt.Sprintf("%s: %s\n", rcpt, dest))
28 5e70998a 2022-04-12 o if err != nil {
29 5e70998a 2022-04-12 o return fmt.Errorf("write alias %s: %w", rcpt, err)
30 5e70998a 2022-04-12 o }
31 5e70998a 2022-04-12 o }
32 5e70998a 2022-04-12 o return nil
33 5e70998a 2022-04-12 o }
34 5e70998a 2022-04-12 o
35 5e70998a 2022-04-12 o func Parse(r io.Reader) (Aliases, error) {
36 5e70998a 2022-04-12 o m := make(Aliases)
37 5e70998a 2022-04-12 o sc := bufio.NewScanner(r)
38 5e70998a 2022-04-12 o for sc.Scan() {
39 5e70998a 2022-04-12 o line := strings.TrimSpace(sc.Text())
40 5e70998a 2022-04-12 o if strings.HasPrefix(line, "#") {
41 5e70998a 2022-04-12 o continue // skip comments
42 5e70998a 2022-04-12 o }
43 5e70998a 2022-04-12 o if line == "" {
44 5e70998a 2022-04-12 o continue // skip blank lines
45 5e70998a 2022-04-12 o }
46 5e70998a 2022-04-12 o
47 5e70998a 2022-04-12 o rcpt, dest, err := parseLine(line)
48 5e70998a 2022-04-12 o if err != nil {
49 5e70998a 2022-04-12 o return nil, fmt.Errorf("parse: %w", err)
50 5e70998a 2022-04-12 o }
51 5e70998a 2022-04-12 o m[rcpt] = dest
52 5e70998a 2022-04-12 o }
53 5e70998a 2022-04-12 o if sc.Err() != nil {
54 5e70998a 2022-04-12 o return nil, fmt.Errorf("parse: %w", sc.Err())
55 5e70998a 2022-04-12 o }
56 5e70998a 2022-04-12 o return m, nil
57 5e70998a 2022-04-12 o }
58 5e70998a 2022-04-12 o
59 5e70998a 2022-04-12 o // Load reads and returns named aliases file.
60 5e70998a 2022-04-12 o func Load(name string) (Aliases, error) {
61 5e70998a 2022-04-12 o f, err := os.Open(name)
62 5e70998a 2022-04-12 o if err != nil {
63 5e70998a 2022-04-12 o return nil, fmt.Errorf("load: %w", err)
64 5e70998a 2022-04-12 o }
65 5e70998a 2022-04-12 o defer f.Close()
66 5e70998a 2022-04-12 o return Parse(f)
67 5e70998a 2022-04-12 o }
68 5e70998a 2022-04-12 o
69 5e70998a 2022-04-12 o // Put writes aliases to the named file.
70 5e70998a 2022-04-12 o // The file is truncated if it already exists, otherwise it is created.
71 5e70998a 2022-04-12 o func Put(m Aliases, name string) error {
72 5e70998a 2022-04-12 o f, err := os.Create(name)
73 5e70998a 2022-04-12 o if err != nil {
74 5e70998a 2022-04-12 o return fmt.Errorf("put: %w", err)
75 5e70998a 2022-04-12 o }
76 5e70998a 2022-04-12 o defer f.Close()
77 5e70998a 2022-04-12 o if err := NewEncoder(f).Encode(m); err != nil {
78 5e70998a 2022-04-12 o return fmt.Errorf("put: %w", err)
79 5e70998a 2022-04-12 o }
80 5e70998a 2022-04-12 o return nil
81 5e70998a 2022-04-12 o }
82 5e70998a 2022-04-12 o
83 5e70998a 2022-04-12 o // parseLine parses a line of the form "foo: bar@example.com", returning two fields:
84 5e70998a 2022-04-12 o // the recipient (foo) and destination (bar@example.com).
85 5e70998a 2022-04-12 o func parseLine(s string) (recipient, destination string, err error) {
86 5e70998a 2022-04-12 o a := strings.Fields(s)
87 5e70998a 2022-04-12 o if len(a) > 2 {
88 5e70998a 2022-04-12 o return "", "", fmt.Errorf("parse line: too many fields")
89 5e70998a 2022-04-12 o } else if len(a) < 2 {
90 5e70998a 2022-04-12 o return "", "", fmt.Errorf("parse line: too few fields")
91 5e70998a 2022-04-12 o }
92 5e70998a 2022-04-12 o
93 5e70998a 2022-04-12 o recipient, destination = strings.TrimSuffix(a[0], ":"), a[1]
94 5e70998a 2022-04-12 o if !strings.HasSuffix(a[0], ":") {
95 5e70998a 2022-04-12 o return "", "", fmt.Errorf("parse line: invalid recipient: expected %q, got %q", ":", recipient[len(recipient)])
96 5e70998a 2022-04-12 o }
97 4678774c 2022-04-21 o if strings.Contains(recipient, ":") {
98 4678774c 2022-04-21 o return "", "", errors.New("parse line: recipient contains colon character")
99 4678774c 2022-04-21 o }
100 5e70998a 2022-04-12 o if recipient == "" {
101 5e70998a 2022-04-12 o return "", "", errors.New("parse line: empty recipient")
102 5e70998a 2022-04-12 o }
103 5e70998a 2022-04-12 o if destination == "" {
104 5e70998a 2022-04-12 o return "", "", errors.New("parse line: empty destination")
105 5e70998a 2022-04-12 o }
106 5e70998a 2022-04-12 o return recipient, destination, nil
107 5e70998a 2022-04-12 o }