なんとなく日々徒然と

強引にGoのメールでverpに対応する

Golangで VERP対応したくて色々と調べましたが
強引にやる方法しか思いつかなかったけどせっかく調べたので記録する。
ソースはこちらを参考にさせていただきました(感謝)
Golang Cafe #16 まとめ その2 smtpパッケージ

verpって単一の宛先のみでカンマ区切りだったりccだったりbccだったりは対応できないっぽいです。
送るなら1メール1ユーザにしないとダメ?

postfix設定

main.cf /etc/postfix/main.cf

1
2
3
recipient_delimiter = +
default_verp_delimiters = -=
smtpd_authorized_verp_clients = $mynetworks

以下sendMailはsmtp.SendMailと引数を一緒にしています。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
func sendMail(addr string, auth smtp.Auth, from string, to []string, msg []byte) error {
	var client *smtp.Client
	var err error

	if client, err = smtp.Dial(addr); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}

	defer client.Close()

	if err = client.Hello("localhost"); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}

	if auth != nil {
		if err = client.Auth(auth); err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}
	}

	for _, addr := range to {
		if err = client.Reset(); err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}

		if err = mail(client, from); err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}

		if err = client.Rcpt(addr); err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}

		var w io.WriteCloser

		if w, err = client.Data(); err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}

		if _, err = w.Write([]byte(msg)); err != nil {
			fmt.Fprintf(os.Stderr, "Error: %v\n", err)
			os.Exit(1)
		}

		w.Close()
	}

	if err = client.Quit(); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}

	return nil
}

func mail(c *smtp.Client, from string) error {
	cmdStr := "MAIL FROM:<%s> XVERP"

	// 8BITMIMEは考慮しない

	_, _, err := cmd(c, 250, cmdStr, from)
	return err
}

func cmd(c *smtp.Client, expectCode int, format string, args ...interface{}) (int, string, error) {
	id, err := c.Text.Cmd(format, args...)
	if err != nil {
		return 0, "", err
	}
	c.Text.StartResponse(id)
	defer c.Text.EndResponse(id)
	code, msg, err := c.Text.ReadResponse(expectCode)
	return code, msg, err
}