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"
"mime/multipart" "mime/multipart"
"net/mail" "net/mail"
"os"
) )
const MIME_BUF_BYTES = 1024 const MIME_BUF_BYTES = 1024
@@ -16,6 +17,30 @@ type MIMEMessage struct {
html string 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) { func ParseMIMEMessage(mailMsg *mail.Message) (*MIMEMessage, error) {
mimeMsg := new(MIMEMessage) 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") return nil, fmt.Errorf("Unable to locate boundary param in Content-Type header")
} }
// Buffer used by Part.Read to hand us chunks of data // Root Node of our tree
readBytes := make([]byte, MIME_BUF_BYTES) 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 // Loop over MIME parts
mr := multipart.NewReader(mailMsg.Body, boundary) mr := multipart.NewReader(reader, boundary)
for { for {
part, err := mr.NextPart() part, err := mr.NextPart()
if err != nil { if err != nil {
@@ -48,34 +81,41 @@ func ParseMIMEMessage(mailMsg *mail.Message) (*MIMEMessage, error) {
// This is a clean end-of-message signal // This is a clean end-of-message signal
break 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 { 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() // Insert ourselves into tree
fmt.Printf("text: '%v'\n", mimeMsg.text) 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
} }