mirror of
https://github.com/jhillyerd/inbucket.git
synced 2025-12-18 10:07:02 +00:00
Replace internal MIME with go.enmime
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package smtpd
|
package smtpd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/jhillyerd/go.enmime"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -23,11 +24,10 @@ type Message interface {
|
|||||||
Date() time.Time
|
Date() time.Time
|
||||||
Subject() string
|
Subject() string
|
||||||
ReadHeader() (msg *mail.Message, err error)
|
ReadHeader() (msg *mail.Message, err error)
|
||||||
ReadBody() (msg *mail.Message, body *MIMEBody, err error)
|
ReadBody() (msg *mail.Message, body *enmime.MIMEBody, err error)
|
||||||
ReadRaw() (raw *string, err error)
|
ReadRaw() (raw *string, err error)
|
||||||
Append(data []byte) error
|
Append(data []byte) error
|
||||||
Close() error
|
Close() error
|
||||||
Delete() error
|
Delete() error
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/jhillyerd/go.enmime"
|
||||||
"github.com/jhillyerd/inbucket/config"
|
"github.com/jhillyerd/inbucket/config"
|
||||||
"github.com/jhillyerd/inbucket/log"
|
"github.com/jhillyerd/inbucket/log"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
@@ -239,7 +240,7 @@ func (m *FileMessage) ReadHeader() (msg *mail.Message, err error) {
|
|||||||
// ReadBody opens the .raw portion of a Message and returns a MIMEBody object, along
|
// ReadBody opens the .raw portion of a Message and returns a MIMEBody object, along
|
||||||
// with a free mail.Message containing the Headers, since we had to make one of those
|
// with a free mail.Message containing the Headers, since we had to make one of those
|
||||||
// anyway.
|
// anyway.
|
||||||
func (m *FileMessage) ReadBody() (msg *mail.Message, body *MIMEBody, err error) {
|
func (m *FileMessage) ReadBody() (msg *mail.Message, body *enmime.MIMEBody, err error) {
|
||||||
file, err := os.Open(m.rawPath())
|
file, err := os.Open(m.rawPath())
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -250,7 +251,7 @@ func (m *FileMessage) ReadBody() (msg *mail.Message, body *MIMEBody, err error)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
mime, err := ParseMIMEBody(msg)
|
mime, err := enmime.ParseMIMEBody(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
209
smtpd/mime.go
209
smtpd/mime.go
@@ -1,209 +0,0 @@
|
|||||||
package smtpd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"container/list"
|
|
||||||
"fmt"
|
|
||||||
"github.com/sloonz/go-qprintable"
|
|
||||||
"io"
|
|
||||||
"mime"
|
|
||||||
"mime/multipart"
|
|
||||||
"net/mail"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MIMENodeMatcher func(node *MIMENode) bool
|
|
||||||
|
|
||||||
type MIMENode struct {
|
|
||||||
Parent *MIMENode
|
|
||||||
FirstChild *MIMENode
|
|
||||||
NextSibling *MIMENode
|
|
||||||
Type string
|
|
||||||
Content []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type MIMEBody struct {
|
|
||||||
Text string
|
|
||||||
Html string
|
|
||||||
Root *MIMENode
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewMIMENode(parent *MIMENode, contentType string) *MIMENode {
|
|
||||||
return &MIMENode{Parent: parent, Type: contentType}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *MIMENode) BreadthFirstSearch(matcher MIMENodeMatcher) *MIMENode {
|
|
||||||
q := list.New()
|
|
||||||
q.PushBack(n)
|
|
||||||
|
|
||||||
// Push children onto queue and attempt to match in that order
|
|
||||||
for q.Len() > 0 {
|
|
||||||
e := q.Front()
|
|
||||||
n := e.Value.(*MIMENode)
|
|
||||||
if matcher(n) {
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
q.Remove(e)
|
|
||||||
c := n.FirstChild
|
|
||||||
for c != nil {
|
|
||||||
q.PushBack(c)
|
|
||||||
c = c.NextSibling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 IsMIMEMessage(mailMsg *mail.Message) bool {
|
|
||||||
// Parse top-level multipart
|
|
||||||
ctype := mailMsg.Header.Get("Content-Type")
|
|
||||||
mediatype, _, err := mime.ParseMediaType(ctype)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch mediatype {
|
|
||||||
case "multipart/alternative":
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseMIMEBody(mailMsg *mail.Message) (*MIMEBody, error) {
|
|
||||||
mimeMsg := new(MIMEBody)
|
|
||||||
|
|
||||||
if !IsMIMEMessage(mailMsg) {
|
|
||||||
// Parse as text only
|
|
||||||
bodyBytes, err := decodeSection(mailMsg.Header.Get("Content-Transfer-Encoding"), mailMsg.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
mimeMsg.Text = string(bodyBytes)
|
|
||||||
} else {
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
// Locate text body
|
|
||||||
match := root.BreadthFirstSearch(func(node *MIMENode) bool {
|
|
||||||
return node.Type == "text/plain"
|
|
||||||
})
|
|
||||||
if match != nil {
|
|
||||||
mimeMsg.Text = string(match.Content)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Locate HTML body
|
|
||||||
match = root.BreadthFirstSearch(func(node *MIMENode) bool {
|
|
||||||
return node.Type == "text/html"
|
|
||||||
})
|
|
||||||
if match != nil {
|
|
||||||
mimeMsg.Html = string(match.Content)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mimeMsg, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MIMEBody) String() string {
|
|
||||||
return fmt.Sprintf("----TEXT----\n%v\n----HTML----\n%v\n----END----\n", m.Text, m.Html)
|
|
||||||
}
|
|
||||||
|
|
||||||
// decodeSection attempts to decode the data from reader using the algorithm listed in
|
|
||||||
// the Content-Transfer-Encoding header, returning the raw data if it does not know
|
|
||||||
// the encoding type.
|
|
||||||
func decodeSection(encoding string, reader io.Reader) ([]byte, error) {
|
|
||||||
switch strings.ToLower(encoding) {
|
|
||||||
case "quoted-printable":
|
|
||||||
decoder := qprintable.NewDecoder(qprintable.WindowsTextEncoding, reader)
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
_, err := buf.ReadFrom(decoder)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
// Don't know this type, just return bytes
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
_, err := buf.ReadFrom(reader)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 text or data, decode it
|
|
||||||
data, err := decodeSection(part.Header.Get("Content-Transfer-Encoding"), part)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
node.Content = data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
package smtpd
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"github.com/stretchrcom/testify/assert"
|
|
||||||
"net/mail"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIdentifyNonMime(t *testing.T) {
|
|
||||||
msg := readMessage("non-mime.raw")
|
|
||||||
assert.False(t, IsMIMEMessage(msg), "Failed to identify non-MIME message")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIdentifyMime(t *testing.T) {
|
|
||||||
msg := readMessage("html-mime-inline.raw")
|
|
||||||
assert.True(t, IsMIMEMessage(msg), "Failed to identify MIME message")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseNonMime(t *testing.T) {
|
|
||||||
msg := readMessage("non-mime.raw")
|
|
||||||
|
|
||||||
mime, err := ParseMIMEBody(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to parse non-MIME: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Contains(t, mime.Text, "This is a test mailing")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseInlineText(t *testing.T) {
|
|
||||||
msg := readMessage("html-mime-inline.raw")
|
|
||||||
|
|
||||||
mime, err := ParseMIMEBody(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to parse MIME: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, mime.Text, "Test of HTML section")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseQuotedPrintable(t *testing.T) {
|
|
||||||
msg := readMessage("quoted-printable.raw")
|
|
||||||
|
|
||||||
mime, err := ParseMIMEBody(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to parse MIME: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Contains(t, mime.Text, "Phasellus sit amet arcu")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseQuotedPrintableMime(t *testing.T) {
|
|
||||||
msg := readMessage("quoted-printable-mime.raw")
|
|
||||||
|
|
||||||
mime, err := ParseMIMEBody(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to parse MIME: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Contains(t, mime.Text, "Nullam venenatis ante")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseInlineHtml(t *testing.T) {
|
|
||||||
msg := readMessage("html-mime-inline.raw")
|
|
||||||
|
|
||||||
mime, err := ParseMIMEBody(msg)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to parse MIME: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Contains(t, mime.Html, "<html>")
|
|
||||||
assert.Contains(t, mime.Html, "Test of HTML section")
|
|
||||||
}
|
|
||||||
|
|
||||||
// readMessage is a test utility function to fetch a mail.Message object.
|
|
||||||
func readMessage(filename string) *mail.Message {
|
|
||||||
// Open test email for parsing
|
|
||||||
raw, err := os.Open(filepath.Join("..", "test-data", filename))
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("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 {
|
|
||||||
panic(fmt.Sprintf("Failed to read message: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
@@ -2,6 +2,7 @@ package smtpd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/jhillyerd/go.enmime"
|
||||||
"github.com/stretchrcom/testify/mock"
|
"github.com/stretchrcom/testify/mock"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -130,9 +131,9 @@ func (m *MockMessage) ReadHeader() (msg *mail.Message, err error) {
|
|||||||
return args.Get(0).(*mail.Message), args.Error(1)
|
return args.Get(0).(*mail.Message), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockMessage) ReadBody() (msg *mail.Message, body *MIMEBody, err error) {
|
func (m *MockMessage) ReadBody() (msg *mail.Message, body *enmime.MIMEBody, err error) {
|
||||||
args := m.Called()
|
args := m.Called()
|
||||||
return args.Get(0).(*mail.Message), args.Get(1).(*MIMEBody), args.Error(2)
|
return args.Get(0).(*mail.Message), args.Get(1).(*enmime.MIMEBody), args.Error(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockMessage) ReadRaw() (raw *string, err error) {
|
func (m *MockMessage) ReadRaw() (raw *string, err error) {
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
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--
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
Date: Sun, 14 Oct 2012 16:09:01 -0700
|
|
||||||
To: greg@inbucket.com
|
|
||||||
From: James Hillyerd <james@hillyerd.com>
|
|
||||||
Subject: test Sun, 14 Oct 2012 16:09:01 -0700
|
|
||||||
X-Mailer: swaks v20120320.0 jetmore.org/john/code/swaks/
|
|
||||||
|
|
||||||
This is a test mailing
|
|
||||||
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
Message-ID: <5081A889.3020108@jamehi03lx.noa.com>
|
|
||||||
Date: Fri, 19 Oct 2012 12:22:49 -0700
|
|
||||||
From: James Hillyerd <jamehi03@jamehi03lx.noa.com>
|
|
||||||
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:16.0) Gecko/20121010 Thunderbird/16.0.1
|
|
||||||
MIME-Version: 1.0
|
|
||||||
To: greg@inbucket.com
|
|
||||||
Subject: MIME Quoted Printable
|
|
||||||
Content-Type: multipart/alternative;
|
|
||||||
boundary="------------020203040006070307010003"
|
|
||||||
|
|
||||||
This is a multi-part message in MIME format.
|
|
||||||
--------------020203040006070307010003
|
|
||||||
Content-Type: text/plain; charset=ISO-8859-1
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam =
|
|
||||||
venenatis ante fermentum justo varius hendrerit. Sed adipiscing =
|
|
||||||
adipiscing nisi at placerat. Sed luctus neque pellentesque sem laoreet =
|
|
||||||
dapibus. Class aptent taciti sociosqu ad litora torquent per conubia =
|
|
||||||
nostra, per inceptos himenaeos. In eu scelerisque nibh. Fusce faucibus, =
|
|
||||||
nisl vel tincidunt sodales, quam est condimentum lorem, vitae semper =
|
|
||||||
lacus nisl vel lacus. Vestibulum vitae iaculis urna. Donec pellentesque =
|
|
||||||
pellentesque ipsum sit amet suscipit. Ut aliquam vestibulum justo sit =
|
|
||||||
amet congue. Mauris nisl lacus, varius a ultrices id, suscipit mattis =
|
|
||||||
libero.
|
|
||||||
|
|
||||||
Donec iaculis dapibus purus in accumsan. Cras faucibus, orci dictum =
|
|
||||||
molestie congue, neque magna adipiscing lacus, ut laoreet sapien ante et =
|
|
||||||
mi. Suspendisse potenti. Nullam sed dui magna. Nullam vel purus augue, =
|
|
||||||
imperdiet vehicula purus. Lorem ipsum dolor sit amet, consectetur =
|
|
||||||
adipiscing elit. In aliquet, ante at tincidunt ultricies, arcu dolor =
|
|
||||||
rutrum odio, quis tempus lacus leo a quam. Fusce hendrerit, urna sed =
|
|
||||||
elementum pulvinar, dolor sem imperdiet arcu, ut ornare erat metus sit =
|
|
||||||
amet diam. Duis sagittis libero ut metus vulputate dictum. Etiam eget =
|
|
||||||
augue dolor, in lacinia felis. Nulla facilisi. Proin sollicitudin =
|
|
||||||
laoreet vehicula. Praesent non nibh odio.
|
|
||||||
|
|
||||||
|
|
||||||
--------------020203040006070307010003
|
|
||||||
Content-Type: text/html; charset=ISO-8859-1
|
|
||||||
Content-Transfer-Encoding: 7bit
|
|
||||||
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
|
|
||||||
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
|
|
||||||
</head>
|
|
||||||
<body bgcolor="#FFFFFF" text="#000000">
|
|
||||||
<div id="lipsum">
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam
|
|
||||||
venenatis ante fermentum justo varius hendrerit. Sed adipiscing
|
|
||||||
adipiscing nisi at placerat. Sed luctus neque pellentesque sem
|
|
||||||
laoreet dapibus. Class aptent taciti sociosqu ad litora torquent
|
|
||||||
per conubia nostra, per inceptos himenaeos. In eu scelerisque
|
|
||||||
nibh. Fusce faucibus, nisl vel tincidunt sodales, quam est
|
|
||||||
condimentum lorem, vitae semper lacus nisl vel lacus. Vestibulum
|
|
||||||
vitae iaculis urna. Donec pellentesque pellentesque ipsum sit
|
|
||||||
amet suscipit. Ut aliquam vestibulum justo sit amet congue.
|
|
||||||
Mauris nisl lacus, varius a ultrices id, suscipit mattis libero.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Donec iaculis dapibus purus in accumsan. Cras faucibus, orci
|
|
||||||
dictum molestie congue, neque magna adipiscing lacus, ut laoreet
|
|
||||||
sapien ante et mi. Suspendisse potenti. Nullam sed dui magna.
|
|
||||||
Nullam vel purus augue, imperdiet vehicula purus. Lorem ipsum
|
|
||||||
dolor sit amet, consectetur adipiscing elit. In aliquet, ante at
|
|
||||||
tincidunt ultricies, arcu dolor rutrum odio, quis tempus lacus
|
|
||||||
leo a quam. Fusce hendrerit, urna sed elementum pulvinar, dolor
|
|
||||||
sem imperdiet arcu, ut ornare erat metus sit amet diam. Duis
|
|
||||||
sagittis libero ut metus vulputate dictum. Etiam eget augue
|
|
||||||
dolor, in lacinia felis. Nulla facilisi. Proin sollicitudin
|
|
||||||
laoreet vehicula. Praesent non nibh odio.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
--------------020203040006070307010003--
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
From: James Hillyerd <james@makita.skynet>
|
|
||||||
Content-Type: text/plain; charset=us-ascii
|
|
||||||
Content-Transfer-Encoding: quoted-printable
|
|
||||||
Subject: Quoted Printable
|
|
||||||
Date: Thu, 18 Oct 2012 22:48:39 -0700
|
|
||||||
Message-Id: <07B7061D-2676-487E-942E-C341CE4D13DC@makita.skynet>
|
|
||||||
To: greg@inbucket
|
|
||||||
Mime-Version: 1.0 (Apple Message framework v1283)
|
|
||||||
X-Mailer: Apple Mail (2.1283)
|
|
||||||
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sit =
|
|
||||||
amet arcu non lacus porta faucibus. Nulla gravida tempus rutrum. =
|
|
||||||
Maecenas vehicula cursus libero sed faucibus. Morbi iaculis interdum =
|
|
||||||
lacus, eget porta turpis ultrices id. Nulla sit amet massa mauris. Morbi =
|
|
||||||
augue tellus, pharetra a varius at, dignissim eget orci. Nulla molestie =
|
|
||||||
interdum tortor, id tincidunt purus lacinia ac. Integer sodales velit =
|
|
||||||
sed neque faucibus egestas eu vel dolor.=20=
|
|
||||||
Reference in New Issue
Block a user