mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 09:37:02 +00:00
122 lines
2.4 KiB
Go
122 lines
2.4 KiB
Go
package inbucket
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"mime"
|
|
"mime/multipart"
|
|
"net/mail"
|
|
"os"
|
|
)
|
|
|
|
const MIME_BUF_BYTES = 1024
|
|
|
|
type MIMEMessage struct {
|
|
text 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) {
|
|
mimeMsg := new(MIMEMessage)
|
|
|
|
// Parse top-level multipart
|
|
ctype := mailMsg.Header.Get("Content-Type")
|
|
mediatype, params, err := mime.ParseMediaType(ctype)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch mediatype {
|
|
case "multipart/alternative":
|
|
// Good
|
|
default:
|
|
return nil, fmt.Errorf("Unknown mediatype: %v", mediatype)
|
|
}
|
|
boundary := params["boundary"]
|
|
if boundary == "" {
|
|
return nil, fmt.Errorf("Unable to locate boundary param in Content-Type header")
|
|
}
|
|
|
|
// 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(reader, boundary)
|
|
for {
|
|
part, err := mr.NextPart()
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
// This is a clean end-of-message signal
|
|
break
|
|
}
|
|
return err
|
|
}
|
|
mediatype, params, err := mime.ParseMediaType(part.Header.Get("Content-Type"))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 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 nil
|
|
}
|