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(220, 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)
61 conn.writeReply(221, "Goodbye")
79 conn.writeReply(550, "access denied")
83 conn.writeReply(250, "https://tools.ietf.org/html/rfc5321")
85 conn.writeReply(500, "unrecognized command")
92 func (conn *connection) reply(reply ReplyLine) {
93 conn.writeReply(reply.Code, reply.Message)
96 func (conn *connection) writeReply(code int, msg string) {
98 conn.tp.PrintfLine("%d %s", code, msg)
100 conn.tp.PrintfLine("%d", code)
104 func (conn *connection) doEHLO() {
108 _, err := fmt.Sscanf(conn.line, "%s %s", &cmd, &conn.ehlo)
110 conn.reply(ReplyBadSyntax)
115 conn.writeReply(250, fmt.Sprintf("Hello %s [%s]", conn.ehlo, conn.remoteAddr))
117 conn.tp.PrintfLine("250-Hello %s [%s]", conn.ehlo, conn.remoteAddr)
118 if conn.server.TLSConfig() != nil {
119 conn.tp.PrintfLine("250-STARTTLS")
121 conn.tp.PrintfLine("250 SIZE %d", 40960000)
124 conn.state = stateInitial
127 func (conn *connection) doMAIL() {
128 if conn.state != stateInitial {
129 conn.reply(ReplyBadSequence)
134 _, err := fmt.Sscanf(conn.line, "MAIL FROM:%s", &mailFrom)
136 conn.reply(ReplyBadSyntax)
140 conn.mailFrom, err = mail.ParseAddress(mailFrom)
142 conn.reply(ReplyBadSyntax)
146 conn.state = stateMail
150 func (conn *connection) doRCPT() {
151 if conn.state != stateMail && conn.state != stateRecipient {
152 conn.reply(ReplyBadSequence)
157 _, err := fmt.Sscanf(conn.line, "RCPT TO:%s", &rcptTo)
159 conn.reply(ReplyBadSyntax)
163 address, err := mail.ParseAddress(rcptTo)
165 conn.reply(ReplyBadSyntax)
168 conn.rcptTo = append(conn.rcptTo, *address)
170 conn.state = stateRecipient
174 func (conn *connection) doDATA() {
175 if conn.state != stateRecipient {
176 conn.reply(ReplyBadSequence)
180 conn.writeReply(354, "Start mail input; end with <CRLF>.<CRLF>")
182 data, err := conn.tp.ReadDotBytes()
185 conn.writeReply(552, "transaction failed")
190 RemoteAddr: conn.remoteAddr,
192 MailFrom: *conn.mailFrom,
197 if reply := conn.server.OnMessageDelivered(env); reply != nil {
202 conn.state = stateInitial
206 func (conn *connection) doVRFY() {
209 func (conn *connection) doRSET() {
210 conn.state = stateInitial
215 func (conn *connection) resetBuffers() {
217 conn.rcptTo = make([]mail.Address, 0)