mlisting

Mailing list service

git clone git://git.lin.moe/go/mlisting.git

 1package service
 2
 3import (
 4	"fmt"
 5
 6	_ "github.com/emersion/go-message/charset"
 7	"github.com/emersion/go-message/textproto"
 8	"github.com/emersion/go-sasl"
 9	smtp "github.com/emersion/go-smtp"
10)
11
12const (
13	CONNTYPE_PLAIN    = "plain"
14	CONNTYPE_STARTTLS = "starttls"
15	CONNTYPE_TLS      = "tls"
16)
17
18type MTA interface {
19	Send(header textproto.Header, body []byte, sender string, rcpts []string) error
20}
21type MockMTA func(header textproto.Header, body []byte, sender string, rcpts []string) error
22
23func (mta *MockMTA) Send(header textproto.Header, body []byte, sender string, rcpts []string) error {
24	return (func(header textproto.Header, body []byte, sender string, rcpts []string) error)(*mta)(header, body, sender, rcpts)
25}
26
27type SMTPClient struct {
28	Address    string
29	AuthClient sasl.Client
30	ConnType   string
31}
32
33func (mta *SMTPClient) Send(header textproto.Header, body []byte, sender string, rcpts []string) error {
34	var (
35		c   *smtp.Client
36		err error
37	)
38	switch mta.ConnType {
39	case CONNTYPE_PLAIN:
40		c, err = smtp.Dial(mta.Address)
41	case CONNTYPE_STARTTLS:
42		c, err = smtp.DialStartTLS(mta.Address, nil)
43	case CONNTYPE_TLS:
44		c, err = smtp.DialTLS(mta.Address, nil)
45	default:
46		return fmt.Errorf("unsupport smtp connection type")
47	}
48	if err != nil {
49		return err
50	}
51	defer c.Close()
52
53	if mta.AuthClient != nil {
54		if ok, _ := c.Extension("AUTH"); !ok {
55			return fmt.Errorf("smtp server %s doesn't support AUTH", mta.Address)
56		}
57		if err := c.Auth(mta.AuthClient); err != nil {
58			return err
59		}
60	}
61
62	if err := c.Mail(sender, nil); err != nil {
63		return err
64	}
65
66	for _, rcpt := range rcpts {
67		if err := c.Rcpt(rcpt, nil); err != nil {
68			return err
69		}
70	}
71
72	wc, err := c.Data()
73	if err != nil {
74		return fmt.Errorf("mail DATA cmd error: %v", err)
75	}
76
77	if err := textproto.WriteHeader(wc, header); err != nil {
78		wc.Close()
79		return fmt.Errorf("send mail header error: %v", err)
80	}
81	if _, err := wc.Write(body); err != nil {
82		wc.Close()
83		return fmt.Errorf("send mail body error: %v", err)
84	}
85
86	if err := wc.Close(); err != nil {
87		return fmt.Errorf("mail data writer close error: %v", err)
88	}
89	return c.Quit()
90}