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().String()))
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)
113 conn.writeReply(250, fmt.Sprintf("Hello %s, I am glad to meet you", conn.ehlo))
115 conn.state = stateInitial
118 func (conn *connection) doMAIL() {
119 if conn.state != stateInitial {
120 conn.reply(ReplyBadSequence)
125 _, err := fmt.Sscanf(conn.line, "MAIL FROM:%s", &mailFrom)
127 conn.reply(ReplyBadSyntax)
131 conn.mailFrom, err = mail.ParseAddress(mailFrom)
133 conn.reply(ReplyBadSyntax)
137 conn.state = stateMail
141 func (conn *connection) doRCPT() {
142 if conn.state != stateMail && conn.state != stateRecipient {
143 conn.reply(ReplyBadSequence)
148 _, err := fmt.Sscanf(conn.line, "RCPT TO:%s", &rcptTo)
150 conn.reply(ReplyBadSyntax)
154 address, err := mail.ParseAddress(rcptTo)
156 conn.reply(ReplyBadSyntax)
159 conn.rcptTo = append(conn.rcptTo, *address)
161 conn.state = stateRecipient
165 func (conn *connection) doDATA() {
166 if conn.state != stateRecipient {
167 conn.reply(ReplyBadSequence)
171 conn.writeReply(354, "Start mail input; end with <CRLF>.<CRLF>")
173 data, err := conn.tp.ReadDotBytes()
176 conn.writeReply(552, "transaction failed")
181 RemoteAddr: conn.remoteAddr,
183 MailFrom: *conn.mailFrom,
188 if reply := conn.server.OnMessageDelivered(env); reply != nil {
193 conn.state = stateInitial
197 func (conn *connection) doVRFY() {
200 func (conn *connection) doRSET() {
201 conn.state = stateInitial
206 func (conn *connection) resetBuffers() {
208 conn.rcptTo = make([]mail.Address, 0)