MailHog/mailhog/data/message.go

141 lines
2.9 KiB
Go
Raw Normal View History

2014-04-19 22:37:11 +00:00
package data;
import (
"log"
"strings"
"time"
"regexp"
2014-04-19 22:37:11 +00:00
"labix.org/v2/mgo/bson"
)
2014-04-20 15:05:50 +00:00
type Messages []Message
2014-04-19 22:37:11 +00:00
type Message struct {
Id string
From *Path
To []*Path
Content *Content
Created time.Time
2014-04-21 21:32:34 +00:00
MIME *MIMEBody
2014-04-19 22:37:11 +00:00
}
type Path struct {
Relays []string
Mailbox string
Domain string
Params string
}
type Content struct {
Headers map[string][]string
Body string
Size int
}
type SMTPMessage struct {
From string
To []string
Data string
Helo string
}
type MIMEBody struct {
2014-04-21 21:32:34 +00:00
Parts []*Content
}
2014-04-23 23:22:50 +00:00
func ParseSMTPMessage(m *SMTPMessage, hostname string) *Message {
2014-04-19 22:37:11 +00:00
arr := make([]*Path, 0)
for _, path := range m.To {
arr = append(arr, PathFromString(path))
}
msg := &Message{
Id: bson.NewObjectId().Hex(),
From: PathFromString(m.From),
To: arr,
Content: ContentFromString(m.Data),
Created: time.Now(),
}
2014-04-21 21:32:34 +00:00
if msg.Content.IsMIME() {
log.Printf("Parsing MIME body")
msg.MIME = msg.Content.ParseMIMEBody()
}
2014-04-23 23:22:50 +00:00
msg.Content.Headers["Message-ID"] = []string{msg.Id + "@" + hostname}
msg.Content.Headers["Received"] = []string{"from " + m.Helo + " by " + hostname + " (Go-MailHog)\r\n id " + msg.Id + "@" + hostname + "; " + time.Now().Format(time.RFC1123Z)}
2014-04-19 22:37:11 +00:00
msg.Content.Headers["Return-Path"] = []string{"<" + m.From + ">"}
return msg
}
func (content *Content) IsMIME() bool {
2014-04-21 11:24:21 +00:00
return strings.HasPrefix(content.Headers["Content-Type"][0], "multipart/")
}
func (content *Content) ParseMIMEBody() *MIMEBody {
re := regexp.MustCompile("boundary=\"([^\"]+)\"")
2014-04-21 21:32:34 +00:00
match := re.FindStringSubmatch(content.Headers["Content-Type"][0])
log.Printf("Got boundary: %s", match[1])
2014-04-21 21:32:34 +00:00
p := strings.Split(content.Body, "--" + match[1])
parts := make([]*Content, 0)
for m := range p {
if len(p[m]) > 0 {
parts = append(parts, ContentFromString(strings.Trim(p[m], "\r\n")))
}
}
return &MIMEBody{
Parts: parts,
}
}
2014-04-19 22:37:11 +00:00
func PathFromString(path string) *Path {
relays := make([]string, 0)
email := path
if(strings.Contains(path, ":")) {
x := strings.SplitN(path, ":", 2)
r, e := x[0], x[1]
email = e
relays = strings.Split(r, ",")
}
mailbox, domain := "", ""
if(strings.Contains(email, "@")) {
x := strings.SplitN(email, "@", 2)
mailbox, domain = x[0], x[1]
} else {
mailbox = email
}
return &Path{
Relays: relays,
Mailbox: mailbox,
Domain: domain,
Params: "", // FIXME?
}
}
func ContentFromString(data string) *Content {
2014-04-21 21:32:34 +00:00
log.Printf("Parsing Content from string: '%s'", data)
x := strings.SplitN(data, "\r\n\r\n", 2)
2014-04-19 22:37:11 +00:00
headers, body := x[0], x[1]
h := make(map[string][]string, 0)
hdrs := strings.Split(headers, "\r\n")
for _, hdr := range hdrs {
if(strings.Contains(hdr, ": ")) {
y := strings.SplitN(hdr, ": ", 2)
key, value := y[0], y[1]
2014-04-20 14:35:59 +00:00
// TODO multiple header fields
2014-04-19 22:37:11 +00:00
h[key] = []string{value}
} else {
log.Printf("Found invalid header: '%s'", hdr)
}
}
return &Content{
Size: len(data),
Headers: h,
Body: body,
}
}