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

Parse MIME parts into a node tree

This commit is contained in:
James Hillyerd
2012-10-15 21:04:58 -07:00
parent 46b123606b
commit a2070dd60a

View File

@@ -7,6 +7,7 @@ import (
"mime"
"mime/multipart"
"net/mail"
"os"
)
const MIME_BUF_BYTES = 1024
@@ -16,6 +17,30 @@ type MIMEMessage struct {
html string
}
type MIMENode struct {
Parent *MIMENode
FirstChild *MIMENode
NextSibling *MIMENode
Type string
Content *bytes.Buffer
}
func NewMIMENode(parent *MIMENode, contentType string) *MIMENode {
return &MIMENode{Parent: parent, Type: contentType}
}
func (n *MIMENode) String() string {
children := ""
siblings := ""
if n.FirstChild != nil {
children = n.FirstChild.String()
}
if n.NextSibling != nil {
siblings = n.NextSibling.String()
}
return fmt.Sprintf("[%v %v] %v", n.Type, children, siblings)
}
func ParseMIMEMessage(mailMsg *mail.Message) (*MIMEMessage, error) {
mimeMsg := new(MIMEMessage)
@@ -36,11 +61,19 @@ func ParseMIMEMessage(mailMsg *mail.Message) (*MIMEMessage, error) {
return nil, fmt.Errorf("Unable to locate boundary param in Content-Type header")
}
// Buffer used by Part.Read to hand us chunks of data
readBytes := make([]byte, MIME_BUF_BYTES)
// Root Node of our tree
root := NewMIMENode(nil, mediatype)
err = parseNodes(root, mailMsg.Body, boundary)
fmt.Println(root.String())
return mimeMsg, err
}
func parseNodes(parent *MIMENode, reader io.Reader, boundary string) error {
var prevSibling *MIMENode
// Loop over MIME parts
mr := multipart.NewReader(mailMsg.Body, boundary)
mr := multipart.NewReader(reader, boundary)
for {
part, err := mr.NextPart()
if err != nil {
@@ -48,34 +81,41 @@ func ParseMIMEMessage(mailMsg *mail.Message) (*MIMEMessage, error) {
// This is a clean end-of-message signal
break
}
return nil, err
return err
}
mediatype, params, err = mime.ParseMediaType(part.Header.Get("Content-Type"))
mediatype, params, err := mime.ParseMediaType(part.Header.Get("Content-Type"))
if err != nil {
return nil, err
return err
}
if mediatype == "text/plain" && mimeMsg.text == "" {
// First text section, we'll have that, but first we have to deal with
// the odd way Part lets us read data.
var buf bytes.Buffer
for {
n, err := part.Read(readBytes)
if err != nil {
if err == io.EOF {
// Clean end of part signal
break
}
return nil, err
}
// Extra data in readBytes is not cleared, so we must respect
// the value returned in 'n'
buf.Write(readBytes[:n])
}
mimeMsg.text = buf.String()
fmt.Printf("text: '%v'\n", mimeMsg.text)
// Insert ourselves into tree
node := NewMIMENode(parent, mediatype)
if prevSibling != nil {
prevSibling.NextSibling = node
} else {
parent.FirstChild = node
}
prevSibling = node
boundary := params["boundary"]
if boundary != "" {
// Content is another multipart
err = parseNodes(node, part, boundary)
if err != nil {
return err
}
} else {
// Content is data, allocate a buffer
node.Content = new(bytes.Buffer)
_, err = io.Copy(node.Content, part)
if err != nil {
return err
}
fmt.Printf("\n----\n")
io.Copy(os.Stdout, node.Content)
fmt.Printf("\n----\n")
}
}
return mimeMsg, nil
return nil
}