2014-11-22 19:15:50 +00:00
|
|
|
package server
|
2014-11-22 16:11:04 +00:00
|
|
|
|
|
|
|
// http://www.rfc-editor.org/rfc/rfc5321.txt
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/ian-kent/Go-MailHog/mailhog/config"
|
|
|
|
"github.com/ian-kent/Go-MailHog/mailhog/data"
|
2014-11-22 19:15:50 +00:00
|
|
|
"github.com/ian-kent/Go-MailHog/mailhog/smtp/protocol"
|
2014-11-22 16:11:04 +00:00
|
|
|
"github.com/ian-kent/Go-MailHog/mailhog/storage"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Session represents a SMTP session using net.TCPConn
|
|
|
|
type Session struct {
|
|
|
|
conn *net.TCPConn
|
2014-11-22 19:15:50 +00:00
|
|
|
proto *protocol.Protocol
|
2014-11-22 16:11:04 +00:00
|
|
|
conf *config.Config
|
|
|
|
isTLS bool
|
|
|
|
line string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Accept starts a new SMTP session using net.TCPConn
|
|
|
|
func Accept(conn *net.TCPConn, conf *config.Config) {
|
2014-11-22 19:15:50 +00:00
|
|
|
proto := protocol.NewProtocol()
|
2014-11-22 16:11:04 +00:00
|
|
|
session := &Session{conn, proto, conf, false, ""}
|
|
|
|
proto.LogHandler = session.logf
|
2014-11-22 19:26:11 +00:00
|
|
|
proto.MessageReceivedHandler = session.acceptMessage
|
2014-11-22 19:20:47 +00:00
|
|
|
proto.ValidateSenderHandler = session.validateSender
|
|
|
|
proto.ValidateRecipientHandler = session.validateRecipient
|
2014-11-22 19:26:11 +00:00
|
|
|
proto.ValidateAuthenticationHandler = session.validateAuthentication
|
2014-11-22 16:11:04 +00:00
|
|
|
|
|
|
|
session.logf("Starting session")
|
2014-11-22 18:47:31 +00:00
|
|
|
session.Write(proto.Start(conf.Hostname))
|
2014-11-22 16:11:04 +00:00
|
|
|
for session.Read() == true {
|
|
|
|
}
|
|
|
|
session.logf("Session ended")
|
|
|
|
}
|
|
|
|
|
2014-11-22 19:26:11 +00:00
|
|
|
func (c *Session) validateAuthentication(mechanism string, args ...string) bool {
|
|
|
|
return true
|
|
|
|
}
|
2014-11-22 19:20:47 +00:00
|
|
|
func (c *Session) validateRecipient(to string) bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Session) validateSender(from string) bool {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2014-11-22 19:26:11 +00:00
|
|
|
func (c *Session) acceptMessage(msg *data.Message) (id string, err error) {
|
2014-11-22 16:11:04 +00:00
|
|
|
switch c.conf.Storage.(type) {
|
|
|
|
case *storage.MongoDB:
|
|
|
|
c.logf("Storing message using MongoDB")
|
|
|
|
id, err = c.conf.Storage.(*storage.MongoDB).Store(msg)
|
2014-11-22 18:45:14 +00:00
|
|
|
case *storage.InMemory:
|
2014-11-22 16:11:04 +00:00
|
|
|
c.logf("Storing message using Memory")
|
2014-11-22 18:45:14 +00:00
|
|
|
id, err = c.conf.Storage.(*storage.InMemory).Store(msg)
|
2014-11-22 16:11:04 +00:00
|
|
|
default:
|
|
|
|
err = errors.New("Unknown storage stype")
|
|
|
|
}
|
2014-11-22 18:45:14 +00:00
|
|
|
c.conf.MessageChan <- msg
|
2014-11-22 16:11:04 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Session) logf(message string, args ...interface{}) {
|
|
|
|
message = strings.Join([]string{"[SMTP %s]", message}, " ")
|
|
|
|
args = append([]interface{}{c.conn.RemoteAddr()}, args...)
|
|
|
|
log.Printf(message, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read reads from the underlying net.TCPConn
|
|
|
|
func (c *Session) Read() bool {
|
|
|
|
buf := make([]byte, 1024)
|
|
|
|
n, err := c.conn.Read(buf)
|
|
|
|
|
|
|
|
if n == 0 {
|
|
|
|
c.logf("Connection closed by remote host\n")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
c.logf("Error reading from socket: %s\n", err)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
text := string(buf[0:n])
|
|
|
|
logText := strings.Replace(text, "\n", "\\n", -1)
|
|
|
|
logText = strings.Replace(logText, "\r", "\\r", -1)
|
|
|
|
c.logf("Received %d bytes: '%s'\n", n, logText)
|
|
|
|
|
|
|
|
c.line += text
|
|
|
|
|
|
|
|
line, reply := c.proto.Parse(c.line)
|
|
|
|
c.line = line
|
|
|
|
|
|
|
|
if reply != nil {
|
|
|
|
c.Write(reply)
|
2014-11-22 19:15:50 +00:00
|
|
|
if reply.Status == 221 {
|
2014-11-22 16:11:04 +00:00
|
|
|
c.conn.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write writes a reply to the underlying net.TCPConn
|
2014-11-22 19:15:50 +00:00
|
|
|
func (c *Session) Write(reply *protocol.Reply) {
|
|
|
|
lines := reply.Lines()
|
|
|
|
for _, l := range lines {
|
2014-11-22 16:11:04 +00:00
|
|
|
logText := strings.Replace(l, "\n", "\\n", -1)
|
|
|
|
logText = strings.Replace(logText, "\r", "\\r", -1)
|
|
|
|
c.logf("Sent %d bytes: '%s'", len(l), logText)
|
|
|
|
c.conn.Write([]byte(l))
|
|
|
|
}
|
|
|
|
}
|