diff --git a/bindata.go b/bindata.go index b1021e1..b1e3966 100644 --- a/bindata.go +++ b/bindata.go @@ -1603,8 +1603,8 @@ var _bintree = &_bintree_t{nil, map[string]*_bintree_t{ "strutil.js": &_bintree_t{assets_js_strutil_js, map[string]*_bintree_t{}}, }}, "templates": &_bintree_t{nil, map[string]*_bintree_t{ - "index.html": &_bintree_t{assets_templates_index_html, map[string]*_bintree_t{}}, "layout.html": &_bintree_t{assets_templates_layout_html, map[string]*_bintree_t{}}, + "index.html": &_bintree_t{assets_templates_index_html, map[string]*_bintree_t{}}, }}, }}, }} diff --git a/mailhog/smtp/protocol/protocol.go b/mailhog/smtp/protocol/protocol.go index 7152713..cf14fbe 100644 --- a/mailhog/smtp/protocol/protocol.go +++ b/mailhog/smtp/protocol/protocol.go @@ -24,7 +24,7 @@ type Protocol struct { hostname string // LogHandler is called for each log message. If nil, log messages will - // be output using fmt.Printf instead. + // be output using log.Printf instead. LogHandler func(message string, args ...interface{}) // MessageReceivedHandler is called for each message accepted by the // SMTP protocol. It must return a MessageID or error. If nil, messages @@ -39,7 +39,7 @@ type Protocol struct { // ValidateAuthenticationhandler should return true if the authentication // parameters are valid, otherwise false. If nil, all authentication // attempts will be accepted. - ValidateAuthenticationHandler func(mechanism string, args ...string) bool + ValidateAuthenticationHandler func(mechanism string, args ...string) (errorReply *Reply, ok bool) } // NewProtocol returns a new SMTP state machine in INVALID state @@ -167,14 +167,37 @@ func (proto *Protocol) Command(command *Command) (reply *Reply) { proto.logf("Got unknown command for ESTABLISH state: '%s'", command.verb) return ReplyUnrecognisedCommand() } - case AUTH == proto.state: - proto.logf("Got authentication response: '%s', switching to MAIL state", command.args) + case AUTHPLAIN == proto.state: + proto.logf("Got PLAIN authentication response: '%s', switching to MAIL state", command.args) proto.state = MAIL + if proto.ValidateAuthenticationHandler != nil { + if reply, ok := proto.ValidateAuthenticationHandler("CRAM-MD5", command.args); !ok { + return reply + } + } return ReplyAuthOk() case AUTHLOGIN == proto.state: - proto.logf("Got LOGIN authentication response: '%s', switching to AUTH state", command.args) - proto.state = AUTH + proto.logf("Got LOGIN authentication response: '%s', switching to AUTHLOGIN2 state", command.args) + proto.state = AUTHLOGIN2 return ReplyAuthResponse("UGFzc3dvcmQ6") + case AUTHLOGIN2 == proto.state: + proto.logf("Got LOGIN authentication response: '%s', switching to MAIL state", command.args) + proto.state = MAIL + if proto.ValidateAuthenticationHandler != nil { + if reply, ok := proto.ValidateAuthenticationHandler("CRAM-MD5", command.args); !ok { + return reply + } + } + return ReplyAuthOk() + case AUTHCRAMMD5 == proto.state: + proto.logf("Got CRAM-MD5 authentication response: '%s', switching to MAIL state", command.args) + proto.state = MAIL + if proto.ValidateAuthenticationHandler != nil { + if reply, ok := proto.ValidateAuthenticationHandler("CRAM-MD5", command.args); !ok { + return reply + } + } + return ReplyAuthOk() case MAIL == proto.state: switch command.verb { case "AUTH": @@ -182,6 +205,11 @@ func (proto *Protocol) Command(command *Command) (reply *Reply) { switch { case strings.HasPrefix(command.args, "PLAIN "): proto.logf("Got PLAIN authentication: %s", strings.TrimPrefix(command.args, "PLAIN ")) + if proto.ValidateAuthenticationHandler != nil { + if reply, ok := proto.ValidateAuthenticationHandler("PLAIN", strings.TrimPrefix(command.args, "PLAIN ")); !ok { + return reply + } + } return ReplyAuthOk() case "LOGIN" == command.args: proto.logf("Got LOGIN authentication, switching to AUTH state") @@ -189,17 +217,17 @@ func (proto *Protocol) Command(command *Command) (reply *Reply) { return ReplyAuthResponse("VXNlcm5hbWU6") case "PLAIN" == command.args: proto.logf("Got PLAIN authentication (no args), switching to AUTH2 state") - proto.state = AUTH + proto.state = AUTHPLAIN return ReplyAuthResponse("") case "CRAM-MD5" == command.args: proto.logf("Got CRAM-MD5 authentication, switching to AUTH state") - proto.state = AUTH + proto.state = AUTHCRAMMD5 return ReplyAuthResponse("PDQxOTI5NDIzNDEuMTI4Mjg0NzJAc291cmNlZm91ci5hbmRyZXcuY211LmVkdT4=") case strings.HasPrefix(command.args, "EXTERNAL "): proto.logf("Got EXTERNAL authentication: %s", strings.TrimPrefix(command.args, "EXTERNAL ")) if proto.ValidateAuthenticationHandler != nil { - if !proto.ValidateAuthenticationHandler("EXTERNAL", command.args) { - // TODO error reply + if reply, ok := proto.ValidateAuthenticationHandler("EXTERNAL", strings.TrimPrefix(command.args, "EXTERNAL ")); !ok { + return reply } } return ReplyAuthOk() diff --git a/mailhog/smtp/protocol/state.go b/mailhog/smtp/protocol/state.go index 8c7dc0e..91bdb77 100644 --- a/mailhog/smtp/protocol/state.go +++ b/mailhog/smtp/protocol/state.go @@ -7,8 +7,10 @@ type State int const ( INVALID = State(-1) ESTABLISH = State(iota) - AUTH + AUTHPLAIN AUTHLOGIN + AUTHLOGIN2 + AUTHCRAMMD5 MAIL RCPT DATA @@ -17,12 +19,14 @@ const ( // StateMap provides string representations of SMTP conversation states var StateMap = map[State]string{ - INVALID: "INVALID", - ESTABLISH: "ESTABLISH", - AUTH: "AUTH", - AUTHLOGIN: "AUTHLOGIN", - MAIL: "MAIL", - RCPT: "RCPT", - DATA: "DATA", - DONE: "DONE", + INVALID: "INVALID", + ESTABLISH: "ESTABLISH", + AUTHPLAIN: "AUTHPLAIN", + AUTHLOGIN: "AUTHLOGIN", + AUTHLOGIN2: "AUTHLOGIN2", + AUTHCRAMMD5: "AUTHCRAMMD5", + MAIL: "MAIL", + RCPT: "RCPT", + DATA: "DATA", + DONE: "DONE", } diff --git a/mailhog/smtp/server/session.go b/mailhog/smtp/server/session.go index 6418102..daecfeb 100644 --- a/mailhog/smtp/server/session.go +++ b/mailhog/smtp/server/session.go @@ -38,8 +38,8 @@ func Accept(conn *net.TCPConn, conf *config.Config) { session.logf("Session ended") } -func (c *Session) validateAuthentication(mechanism string, args ...string) bool { - return true +func (c *Session) validateAuthentication(mechanism string, args ...string) (errorReply *protocol.Reply, ok bool) { + return nil, true } func (c *Session) validateRecipient(to string) bool {