diff --git a/smtp/connection.go b/smtp/connection.go index 05b280a..7fbee1d 100644 --- a/smtp/connection.go +++ b/smtp/connection.go @@ -30,7 +30,7 @@ type Connection struct { connection net.Conn ReversePathBuffer *string - ForwardPathBuffer *string + ForwardPathBuffer []string MailBuffer *string } @@ -38,7 +38,7 @@ func NewConnection(connection net.Conn) Connection { return Connection{ connection, nil, - nil, + []string{}, nil, } } @@ -83,6 +83,16 @@ func (self Connection) Write(message string) error { return err } +func (self Connection) AddRecipient(recipient string) { + self.ForwardPathBuffer = append(self.ForwardPathBuffer, recipient) +} + +func (self Connection) Reset() { + self.ReversePathBuffer = nil + self.ForwardPathBuffer = []string{} + self.MailBuffer = nil +} + func (self Connection) Close() { self.connection.Close() } diff --git a/smtp/handlers.go b/smtp/handlers.go index 990910d..8ba5560 100644 --- a/smtp/handlers.go +++ b/smtp/handlers.go @@ -25,7 +25,7 @@ import ( type Command struct { name string - Exec func(Connection, string)([]Command, error) + Exec func(Connection, string)(bool, error) } func (self Command) Name() string { @@ -37,37 +37,40 @@ func (self Command) Check(message string) bool { } func (self Connection) Chain() error { - commands, err := Greet(self) + err := Greet(self) if err != nil { return err } - for { - message, err := self.Read("\r\n") + COMMANDS := []Command{ + Command{ "HELO", Helo }, + Command{ "EHLO", Ehlo }, + Command{ "MAIL FROM:", MailFrom }, + Command{ "RCPT TO:", RcptTo }, + Command{ "DATA", Data }, + Command{ "QUIT", Quit }, + Command{ "NOOP", Noop }, + Command{ "RSET", Rset }, + } + quit := false + for !quit { + message, err := self.Read("\n") if err != nil { return err } command_found := false - for _, command := range commands { + for _, command := range COMMANDS { if command.Check(message) { - commands, err = command.Exec(self, message[len(command.Name()):]) + command_found = true + quit, err = command.Exec(self, message[len(command.Name()):]) if err != nil { return err } - command_found = true break } } if !command_found { - expected := make([]string, len(commands)) - for index, command := range commands { - expected[index] = command.Name() - } - return fmt.Errorf("Expected one of %v, but got: %v", expected, message) - } - - if len(commands) == 0 { - break + self.Write("500 Unrecognized Command\n") } } return nil @@ -75,25 +78,20 @@ func (self Connection) Chain() error { /* --- GREETING RESPONSE --- */ -func Greet(connection Connection) ([]Command, error) { +func Greet(connection Connection) error { err := connection.Write( "220 localhost ESMTP diodemail -- Service ready" + "\n", ) if err != nil { - return []Command{}, err + return err } - return []Command{ - HELO, - EHLO, - }, nil + return nil } /* --- HELO/EHLO RESPONSE --- */ -var HELO = Command{ "HELO", Hello } -var EHLO = Command{ "EHLO", Hello } -func Hello(connection Connection, message string) ([]Command, error) { +func Helo(connection Connection, message string) (bool, error) { err := connection.Write( fmt.Sprintf( "250 %v is shy" + "\r\n", @@ -101,84 +99,116 @@ func Hello(connection Connection, message string) ([]Command, error) { ), ) if err != nil { - return []Command{}, err + return false, err } - return []Command{ - MAIL_FROM, - }, nil + return false, nil +} + +func Ehlo(connection Connection, message string) (bool, error) { + err := connection.Write( + fmt.Sprintf( + "250 %v is shy" + "\r\n", + connection.connection.LocalAddr(), + ), + ) + if err != nil { + return false, err + } + + return false, nil } /* --- MAIL FROM RESPONSE --- */ -var MAIL_FROM = Command{ "MAIL FROM:", MailFrom } -func MailFrom(connection Connection, message string) ([]Command, error) { +func MailFrom(connection Connection, message string) (bool, error) { match := ReversePath.FindStringSubmatch(message) if match == nil { - return []Command{}, fmt.Errorf("Invalid forward-path: %v", message) + connection.Write( + "501 Could not parse reverse-path", + ) } else if len(match) > 1 { connection.ReversePathBuffer = &match[1] - } - err := connection.Write( - "250 OK\n", - ) - if err != nil { - return []Command{}, err + err := connection.Write( + "250 OK\n", + ) + if err != nil { + return false, err + } } - return []Command{ - RCPT_TO, - }, nil + return false, nil } -var RCPT_TO = Command{ "RCPT TO:", RcptTo } -func RcptTo(connection Connection, message string) ([]Command, error) { +func RcptTo(connection Connection, message string) (bool, error) { match := ForwardPath.FindStringSubmatch(message) if match == nil { - return []Command{}, fmt.Errorf("Invalid forward-path: %v", message) + connection.Write( + "501 Could not parse forward-path", + ) } else { - connection.ForwardPathBuffer = &match[1] - } - err := connection.Write( - "250 OK\n", - ) - if err != nil { - return []Command{}, err + connection.AddRecipient(match[1]) + err := connection.Write( + "250 OK\n", + ) + if err != nil { + return false, err + } } - return []Command{ - DATA, - }, nil + return false, nil } -var DATA = Command { "DATA", Data } -func Data(connection Connection, message string) ([]Command, error) { +func Data(connection Connection, message string) (bool, error) { err := connection.Write( "354 Start Input\n", ) if err != nil { - return []Command{}, err + return false, err } data, err := connection.Read("\r\n.\r\n") connection.MailBuffer = &data err = connection.Write( "250 OK\n", ) + connection.Reset() if err != nil { - return nil, err + return false, err } - return []Command{ - QUIT, - }, nil + + return false, nil } -var QUIT = Command { "QUIT", Quit } -func Quit(connection Connection, message string) ([]Command, error) { +func Quit(connection Connection, message string) (bool, error) { err := connection.Write( - "221 OK\n", + "221 Goodbye :)\n", ) if err != nil { - return nil, err + return true, err } - return []Command{}, nil + + return true, nil +} + +func Noop(connection Connection, message string) (bool, error) { + err := connection.Write( + "250 OK\n", + ) + if err != nil { + return false, err + } + + return false, nil +} + +func Rset(connection Connection, message string) (bool, error) { + err := connection.Write( + "250 Reset\n", + ) + connection.Reset() + if err != nil { + return false, err + } + + return false, nil }