13 stateNew state = iota // Before EHLO.
20 type connection struct {
30 mailFrom *mail.Address
34 func AcceptConnection(netConn net.Conn, server Server) error {
37 tp: textproto.NewConn(netConn),
38 remoteAddr: netConn.RemoteAddr(),
44 conn.writeReply(250, fmt.Sprintf("%s ESMTP [%s] (mailpopbox)", server.Name(), netConn.LocalAddr()))
47 conn.line, err = conn.tp.ReadLine()
49 conn.writeReply(500, "line too long")
54 if _, err = fmt.Sscanf(conn.line, "%s", &cmd); err != nil {
55 conn.reply(ReplyBadSyntax)
78 conn.writeReply(550, "access denied")
82 conn.writeReply(250, "https://tools.ietf.org/html/rfc5321")
84 conn.writeReply(500, "unrecognized command")
91 func (conn *connection) reply(reply ReplyLine) {
92 conn.writeReply(reply.Code, reply.Message)
95 func (conn *connection) writeReply(code int, msg string) {
97 conn.tp.PrintfLine("%d %s", code, msg)
99 conn.tp.PrintfLine("%d", code)
103 func (conn *connection) doEHLO() {
107 _, err := fmt.Sscanf(conn.line, "%s %s", &cmd, &conn.ehlo)
109 conn.reply(ReplyBadSyntax)
114 conn.writeReply(250, fmt.Sprintf("Hello %s [%s]", conn.ehlo, conn.remoteAddr))
116 conn.tp.PrintfLine("250-Hello %s [%s]", conn.ehlo, conn.remoteAddr)
117 if conn.server.TLSConfig() != nil {
118 conn.tp.PrintfLine("250-STARTTLS")
120 conn.tp.PrintfLine("250 SIZE %d", 40960000)
123 conn.state = stateInitial
126 func (conn *connection) doMAIL() {
127 if conn.state != stateInitial {
128 conn.reply(ReplyBadSequence)
133 _, err := fmt.Sscanf(conn.line, "MAIL FROM:%s", &mailFrom)
135 conn.reply(ReplyBadSyntax)
139 conn.mailFrom, err = mail.ParseAddress(mailFrom)
141 conn.reply(ReplyBadSyntax)
145 conn.state = stateMail
149 func (conn *connection) doRCPT() {
150 if conn.state != stateMail && conn.state != stateRecipient {
151 conn.reply(ReplyBadSequence)
156 _, err := fmt.Sscanf(conn.line, "RCPT TO:%s", &rcptTo)
158 conn.reply(ReplyBadSyntax)
162 address, err := mail.ParseAddress(rcptTo)
164 conn.reply(ReplyBadSyntax)
167 conn.rcptTo = append(conn.rcptTo, *address)
169 conn.state = stateRecipient
173 func (conn *connection) doDATA() {
174 if conn.state != stateRecipient {
175 conn.reply(ReplyBadSequence)
179 conn.writeReply(354, "Start mail input; end with <CRLF>.<CRLF>")
181 data, err := conn.tp.ReadDotBytes()
184 conn.writeReply(552, "transaction failed")
189 RemoteAddr: conn.remoteAddr,
191 MailFrom: *conn.mailFrom,
196 if reply := conn.server.OnMessageDelivered(env); reply != nil {
201 conn.state = stateInitial
205 func (conn *connection) doVRFY() {
208 func (conn *connection) doRSET() {
209 conn.state = stateInitial
214 func (conn *connection) resetBuffers() {
216 conn.rcptTo = make([]mail.Address, 0)