This commit is contained in:
Gnarwhal 2024-09-29 18:24:06 +00:00
parent 542ef9e528
commit a66b846fb3
Signed by: Gnarwhal
GPG key ID: 0989A73D8C421174
2 changed files with 108 additions and 68 deletions

View file

@ -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()
}

View file

@ -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
}