Define an smtp.Envelope that can be delivered to an smtp.Server.
authorRobert Sesek <rsesek@bluestatic.org>
Mon, 12 Dec 2016 06:30:16 +0000 (01:30 -0500)
committerRobert Sesek <rsesek@bluestatic.org>
Mon, 12 Dec 2016 06:32:19 +0000 (01:32 -0500)
Also introduce a smtp.ReplyLine rather than smpt.commnection.write*() methods.

smtp.go
smtp/conn.go
smtp/server.go

diff --git a/smtp.go b/smtp.go
index 92ac2e50546b3e030062916d12a066bd6ff462a3..156ace163331740782da6a6458839737cd4e6d8a 100644 (file)
--- a/smtp.go
+++ b/smtp.go
@@ -1,6 +1,7 @@
 package main
 
 import (
+       "crypto/tls"
        "fmt"
        "net"
 
@@ -43,10 +44,15 @@ func (server *smtpServer) Name() string {
        return server.config.Hostname
 }
 
-func (server *smtpServer) OnEHLO() error {
+func (server *smtpServer) TLSConfig() *tls.Config {
        return nil
 }
 
-func (server *smtpServer) OnMessageDelivered() error {
+func (server *smtpServer) OnEHLO() *smtp.ReplyLine {
+       return nil
+}
+
+func (server *smtpServer) OnMessageDelivered(en smtp.Envelope) *smtp.ReplyLine {
+       fmt.Printf("MSG: %#v\n%s\n", en, string(en.Data))
        return nil
 }
index f07afe11d8b1e1742b3e1069d0abdfc7c734421e..30d48f8ec67d91b577560091f6675caf7a9fb0a9 100644 (file)
@@ -10,7 +10,7 @@ import (
 type state int
 
 const (
-       stateNew state = iota // Before EHOL.
+       stateNew state = iota // Before EHLO.
        stateInitial
        stateMail
        stateRecipient
@@ -18,6 +18,8 @@ const (
 )
 
 type connection struct {
+       server Server
+
        tp         *textproto.Conn
        remoteAddr net.Addr
 
@@ -31,6 +33,7 @@ type connection struct {
 
 func AcceptConnection(netConn net.Conn, server Server) error {
        conn := connection{
+               server:     server,
                tp:         textproto.NewConn(netConn),
                remoteAddr: netConn.RemoteAddr(),
                state:      stateNew,
@@ -49,7 +52,7 @@ func AcceptConnection(netConn net.Conn, server Server) error {
 
                var cmd string
                if _, err = fmt.Sscanf(conn.line, "%s", &cmd); err != nil {
-                       conn.writeBadSyntax()
+                       conn.reply(ReplyBadSyntax)
                        continue
                }
 
@@ -74,7 +77,7 @@ func AcceptConnection(netConn net.Conn, server Server) error {
                case "EXPN":
                        conn.writeReply(550, "access denied")
                case "NOOP":
-                       conn.writeOK()
+                       conn.reply(ReplyOK)
                case "HELP":
                        conn.writeReply(250, "https://tools.ietf.org/html/rfc5321")
                default:
@@ -85,6 +88,10 @@ func AcceptConnection(netConn net.Conn, server Server) error {
        return err
 }
 
+func (conn *connection) reply(reply ReplyLine) {
+       conn.writeReply(reply.Code, reply.Message)
+}
+
 func (conn *connection) writeReply(code int, msg string) {
        if len(msg) > 0 {
                conn.tp.PrintfLine("%d %s", code, msg)
@@ -93,25 +100,13 @@ func (conn *connection) writeReply(code int, msg string) {
        }
 }
 
-func (conn *connection) writeOK() {
-       conn.writeReply(250, "OK")
-}
-
-func (conn *connection) writeBadSyntax() {
-       conn.writeReply(501, "syntax error")
-}
-
-func (conn *connection) writeBadSequence() {
-       conn.writeReply(503, "bad sequence of commands")
-}
-
 func (conn *connection) doEHLO() {
        conn.resetBuffers()
 
        var cmd string
        _, err := fmt.Sscanf(conn.line, "%s %s", &cmd, &conn.ehlo)
        if err != nil {
-               conn.writeBadSyntax()
+               conn.reply(ReplyBadSyntax)
                return
        }
 
@@ -122,54 +117,54 @@ func (conn *connection) doEHLO() {
 
 func (conn *connection) doMAIL() {
        if conn.state != stateInitial {
-               conn.writeBadSequence()
+               conn.reply(ReplyBadSequence)
                return
        }
 
        var mailFrom string
        _, err := fmt.Sscanf(conn.line, "MAIL FROM:%s", &mailFrom)
        if err != nil {
-               conn.writeBadSyntax()
+               conn.reply(ReplyBadSyntax)
                return
        }
 
        conn.mailFrom, err = mail.ParseAddress(mailFrom)
        if err != nil {
-               conn.writeBadSyntax()
+               conn.reply(ReplyBadSyntax)
                return
        }
 
        conn.state = stateMail
-       conn.writeOK()
+       conn.reply(ReplyOK)
 }
 
 func (conn *connection) doRCPT() {
        if conn.state != stateMail && conn.state != stateRecipient {
-               conn.writeBadSequence()
+               conn.reply(ReplyBadSequence)
                return
        }
 
        var rcptTo string
        _, err := fmt.Sscanf(conn.line, "RCPT TO:%s", &rcptTo)
        if err != nil {
-               conn.writeBadSyntax()
+               conn.reply(ReplyBadSyntax)
                return
        }
 
        address, err := mail.ParseAddress(rcptTo)
        if err != nil {
-               conn.writeBadSyntax()
+               conn.reply(ReplyBadSyntax)
        }
 
        conn.rcptTo = append(conn.rcptTo, *address)
 
        conn.state = stateRecipient
-       conn.writeOK()
+       conn.reply(ReplyOK)
 }
 
 func (conn *connection) doDATA() {
        if conn.state != stateRecipient {
-               conn.writeBadSequence()
+               conn.reply(ReplyBadSequence)
                return
        }
 
@@ -182,10 +177,21 @@ func (conn *connection) doDATA() {
                return
        }
 
-       fmt.Println(string(data))
+       env := Envelope{
+               RemoteAddr: conn.remoteAddr,
+               EHLO:       conn.ehlo,
+               MailFrom:   *conn.mailFrom,
+               RcptTo:     conn.rcptTo,
+               Data:       data,
+       }
+
+       if reply := conn.server.OnMessageDelivered(env); reply != nil {
+               conn.reply(*reply)
+               return
+       }
 
        conn.state = stateInitial
-       conn.writeOK()
+       conn.reply(ReplyOK)
 }
 
 func (conn *connection) doVRFY() {
@@ -194,7 +200,7 @@ func (conn *connection) doVRFY() {
 func (conn *connection) doRSET() {
        conn.state = stateInitial
        conn.resetBuffers()
-       conn.writeOK()
+       conn.reply(ReplyOK)
 }
 
 func (conn *connection) resetBuffers() {
index fe935efa29c4de20d5e191a4726dc1b2ce9035a9..271e172008e1f7831142bec80b7161ab567a32a4 100644 (file)
@@ -1,7 +1,33 @@
 package smtp
 
+import (
+       "crypto/tls"
+       "net"
+       "net/mail"
+)
+
+type ReplyLine struct {
+       Code    int
+       Message string
+}
+
+var (
+       ReplyOK          = ReplyLine{250, "OK"}
+       ReplyBadSyntax   = ReplyLine{501, "syntax error"}
+       ReplyBadSequence = ReplyLine{503, "bad sequence of commands"}
+)
+
+type Envelope struct {
+       RemoteAddr net.Addr
+       EHLO       string
+       MailFrom   mail.Address
+       RcptTo     []mail.Address
+       Data       []byte
+}
+
 type Server interface {
        Name() string
-       OnEHLO() error
-       OnMessageDelivered() error
+       TLSConfig() *tls.Config
+       OnEHLO() *ReplyLine
+       OnMessageDelivered(Envelope) *ReplyLine
 }