mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-17 17:47:03 +00:00
Started work on MIME parser
I'm hoping to mostly rely on the official mime and mime/multipart packages, but Inbucket is still going to have to decided what to do with the various parts it reads.
This commit is contained in:
81
app/inbucket/mime.go
Normal file
81
app/inbucket/mime.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package inbucket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"mime"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/mail"
|
||||||
|
)
|
||||||
|
|
||||||
|
const MIME_BUF_BYTES = 1024
|
||||||
|
|
||||||
|
type MIMEMessage struct {
|
||||||
|
text string
|
||||||
|
html string
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Buffer used by Part.Read to hand us chunks of data
|
||||||
|
readBytes := make([]byte, MIME_BUF_BYTES)
|
||||||
|
|
||||||
|
// Loop over MIME parts
|
||||||
|
mr := multipart.NewReader(mailMsg.Body, boundary)
|
||||||
|
for {
|
||||||
|
part, err := mr.NextPart()
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
// This is a clean end-of-message signal
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
mediatype, params, err = mime.ParseMediaType(part.Header.Get("Content-Type"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mimeMsg, nil
|
||||||
|
}
|
||||||
28
app/inbucket/mime_test.go
Normal file
28
app/inbucket/mime_test.go
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package inbucket
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"net/mail"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSomething(t *testing.T) {
|
||||||
|
// Open test email for parsing
|
||||||
|
raw, err := os.Open("../../test-data/html-mime-attach.raw")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to open test data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse email into a mail.Message object like we do
|
||||||
|
reader := bufio.NewReader(raw)
|
||||||
|
msg, err := mail.ReadMessage(reader)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to read message: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ParseMIMEMessage(msg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to parse mime: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
54
test-data/html-mime-attach.raw
Normal file
54
test-data/html-mime-attach.raw
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
From: James Hillyerd <james@makita.skynet>
|
||||||
|
Content-Type: multipart/alternative; boundary="Apple-Mail=_E091454E-BCFA-43B4-99C0-678AEC9868D6"
|
||||||
|
Subject: MIME test 1
|
||||||
|
Date: Sat, 13 Oct 2012 15:33:07 -0700
|
||||||
|
Message-Id: <4E2E5A48-1A2C-4450-8663-D41B451DA93E@makita.skynet>
|
||||||
|
To: greg@nobody.com
|
||||||
|
Mime-Version: 1.0 (Apple Message framework v1283)
|
||||||
|
X-Mailer: Apple Mail (2.1283)
|
||||||
|
|
||||||
|
|
||||||
|
--Apple-Mail=_E091454E-BCFA-43B4-99C0-678AEC9868D6
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
Content-Type: text/plain;
|
||||||
|
charset=us-ascii
|
||||||
|
|
||||||
|
Test of HTML section
|
||||||
|
--Apple-Mail=_E091454E-BCFA-43B4-99C0-678AEC9868D6
|
||||||
|
Content-Type: multipart/related;
|
||||||
|
type="text/html";
|
||||||
|
boundary="Apple-Mail=_D2ABE25A-F0FE-404E-94EE-D98BD23448D5"
|
||||||
|
|
||||||
|
|
||||||
|
--Apple-Mail=_D2ABE25A-F0FE-404E-94EE-D98BD23448D5
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
Content-Type: text/html;
|
||||||
|
charset=us-ascii
|
||||||
|
|
||||||
|
<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><font class="Apple-style-span" face="'Comic Sans MS'">Test of HTML section</font><img height="16" width="16" apple-width="yes" apple-height="yes" id="4579722f-d53d-45d0-88bc-f8209a2ca569" src="cid:8B8481A2-25CA-4886-9B5A-8EB9115DD064@skynet"></body></html>
|
||||||
|
--Apple-Mail=_D2ABE25A-F0FE-404E-94EE-D98BD23448D5
|
||||||
|
Content-Transfer-Encoding: base64
|
||||||
|
Content-Disposition: inline;
|
||||||
|
filename=favicon.png
|
||||||
|
Content-Type: image/png;
|
||||||
|
x-unix-mode=0644;
|
||||||
|
name="favicon.png"
|
||||||
|
Content-Id: <8B8481A2-25CA-4886-9B5A-8EB9115DD064@skynet>
|
||||||
|
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ
|
||||||
|
bWFnZVJlYWR5ccllPAAAAlFJREFUeNqUU8tOFEEUPVVdNV3dPe8xYRBnjGhmBgKjKzCIiQvBoIaN
|
||||||
|
bly5Z+PSv3Aj7DSiP2B0rwkLGVdGgxITSCRIJGSMEQWZR3eVt5sEFBgTb/dN1yvnnHtPNTPG4Pqd
|
||||||
|
HgCMXnPRSZrpSuH8vUJu4DE4rYHDGAZDX62BZttHqTiIayM3gGiXQsgYLEvATaqxU+dy1U13YXap
|
||||||
|
XptpNHY8iwn8KyIAzm1KBdtRZWErpI5lEWTXp5Z/vHpZ3/wyKKwYGGOdAYwR0EZwoezTYApBEIOb
|
||||||
|
yELl/aE1/83cp40Pt5mxqCKrE4Ck+mVWKKcI5tA8BLEhRBKJLjez6a7MLq7XZtp+yyOawwCBtkiB
|
||||||
|
VZDKzRk4NN7NQBMYPHiZDFhXY+p9ff7F961vVcnl4R5I2ykJ5XFN7Ab7Gc61VoipNBKF+PDyztu5
|
||||||
|
lfrSLT/wIwCxq0CAGtXHZTzqR2jtwQiXONma6hHpj9sLT7YaPxfTXuZdBGA02Wi7FS48YiTfj+i2
|
||||||
|
NhqtdhP5RC8mh2/Op7y0v6eAcWVLFT8D7kWX5S9mepp+C450MV6aWL1cGnvkxbwHtLW2B9AOkLeU
|
||||||
|
d9KEDuh9fl/7CEj7YH5g+3r/lWfF9In7tPz6T4IIwBJOr1SJyIGQMZQbsh5P9uBq5VJtqHh2mo49
|
||||||
|
pdw5WFoEwKWqWHacaWOjQXWGcifKo6vj5RGS6zykI587XeUIQDqJSmAp+lE4qt19W5P9o8+Lma5D
|
||||||
|
cjsC8JiT607lMVkdqQ0Vyh3lHhmh52tfNy78ajXv0rgYzv8nfwswANuk+7sD/Q0aAAAAAElFTkSu
|
||||||
|
QmCC
|
||||||
|
|
||||||
|
--Apple-Mail=_D2ABE25A-F0FE-404E-94EE-D98BD23448D5--
|
||||||
|
|
||||||
|
--Apple-Mail=_E091454E-BCFA-43B4-99C0-678AEC9868D6--
|
||||||
Reference in New Issue
Block a user