1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2025-12-17 17:47:03 +00:00

More MIME parsing progress!

After parsing MIME into a tree of nodes, we can now issue
BreadthFirstSearch commands against it to locate the text and HTML
portions of the email.
This commit is contained in:
James Hillyerd
2012-10-15 22:56:45 -07:00
parent a2070dd60a
commit bc7526abef

View File

@@ -2,33 +2,56 @@ package inbucket
import ( import (
"bytes" "bytes"
"container/list"
"fmt" "fmt"
"io" "io"
"mime" "mime"
"mime/multipart" "mime/multipart"
"net/mail" "net/mail"
"os"
) )
const MIME_BUF_BYTES = 1024 type MIMENodeMatcher func(node *MIMENode) bool
type MIMEMessage struct {
text string
html string
}
type MIMENode struct { type MIMENode struct {
Parent *MIMENode Parent *MIMENode
FirstChild *MIMENode FirstChild *MIMENode
NextSibling *MIMENode NextSibling *MIMENode
Type string Type string
Content *bytes.Buffer Content []byte
}
type MIMEMessage struct {
Text string
Html string
Root *MIMENode
} }
func NewMIMENode(parent *MIMENode, contentType string) *MIMENode { func NewMIMENode(parent *MIMENode, contentType string) *MIMENode {
return &MIMENode{Parent: parent, Type: contentType} return &MIMENode{Parent: parent, Type: contentType}
} }
func (n *MIMENode) BreadthFirstSearch(matcher MIMENodeMatcher) *MIMENode {
q := list.New()
q.PushBack(n)
// Push children onto queue and attempt to match in that order
for q.Len() > 0 {
e := q.Front()
n := e.Value.(*MIMENode)
if matcher(n) {
return n
}
q.Remove(e)
c := n.FirstChild
for c != nil {
q.PushBack(c)
c = c.NextSibling
}
}
return nil
}
func (n *MIMENode) String() string { func (n *MIMENode) String() string {
children := "" children := ""
siblings := "" siblings := ""
@@ -66,9 +89,32 @@ func ParseMIMEMessage(mailMsg *mail.Message) (*MIMEMessage, error) {
err = parseNodes(root, mailMsg.Body, boundary) err = parseNodes(root, mailMsg.Body, boundary)
fmt.Println(root.String()) fmt.Println(root.String())
// Locate text body
match := root.BreadthFirstSearch(func(node *MIMENode) bool {
return node.Type == "text/plain"
})
if match != nil {
mimeMsg.Text = string(match.Content)
}
// Locate HTML body
match = root.BreadthFirstSearch(func(node *MIMENode) bool {
return node.Type == "text/html"
})
if match != nil {
mimeMsg.Html = string(match.Content)
}
fmt.Println(mimeMsg.String())
return mimeMsg, err return mimeMsg, err
} }
func (m *MIMEMessage) String() string {
return fmt.Sprintf("----TEXT----\n%v\n----HTML----\n%v\n----END----\n", m.Text, m.Html)
}
func parseNodes(parent *MIMENode, reader io.Reader, boundary string) error { func parseNodes(parent *MIMENode, reader io.Reader, boundary string) error {
var prevSibling *MIMENode var prevSibling *MIMENode
@@ -106,14 +152,12 @@ func parseNodes(parent *MIMENode, reader io.Reader, boundary string) error {
} }
} else { } else {
// Content is data, allocate a buffer // Content is data, allocate a buffer
node.Content = new(bytes.Buffer) buf := new(bytes.Buffer)
_, err = io.Copy(node.Content, part) _, err = buf.ReadFrom(part)
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("\n----\n") node.Content = buf.Bytes()
io.Copy(os.Stdout, node.Content)
fmt.Printf("\n----\n")
} }
} }