mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 09:37:02 +00:00
Parse MIME parts into a node tree
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user