Skip to content

Commit

Permalink
Merge branch 'develop' of https://stash.open.ch/scm/mx/enmime into fe…
Browse files Browse the repository at this point in the history
…ature/MX-1198
  • Loading branch information
Pedro Mendez committed Nov 16, 2017
2 parents 20a8e25 + 7debb3d commit db23325
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 26 deletions.
5 changes: 5 additions & 0 deletions envelope.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package enmime

import (
"bytes"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -147,6 +148,7 @@ func parseTextOnlyBody(root *Part, e *Envelope) error {

// Read transcoded text
bodyBytes, err := ioutil.ReadAll(root)
root.Utf8Reader = bytes.NewReader(bodyBytes)
if err != nil {
return err
}
Expand Down Expand Up @@ -228,6 +230,7 @@ func parseMultiPartBody(root *Part, e *Envelope) error {
if ioerr != nil {
return ioerr
}
p.Utf8Reader = bytes.NewReader(allBytes)
e.Text = string(allBytes)
}
} else {
Expand All @@ -243,6 +246,7 @@ func parseMultiPartBody(root *Part, e *Envelope) error {
if ioerr != nil {
return ioerr
}
p.Utf8Reader = bytes.NewReader(allBytes)
e.Text += string(allBytes)
}
}
Expand All @@ -254,6 +258,7 @@ func parseMultiPartBody(root *Part, e *Envelope) error {
if ioerr != nil {
return ioerr
}
p.Utf8Reader = bytes.NewReader(allBytes)
e.HTML += string(allBytes)
}

Expand Down
61 changes: 35 additions & 26 deletions part.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"mime/quotedprintable"
"net/textproto"
"regexp"
"strconv"
"strings"
)

Expand All @@ -27,11 +28,12 @@ type Part struct {
Charset string // The content charset encoding label
Errors []Error // Errors encountered while parsing this part
Epilogue bytes.Buffer // This is the epilogue of the email.
PartID string // The ID representing the part's exact position within the MIME Tree
Utf8Reader io.Reader // The decoded content converted to UTF-8

boundary string // Boundary marker used within this part
rawReader io.Reader // The raw Part content, no decoding or charset conversion
decodedReader io.Reader // The content decoded from quoted-printable or base64
utf8Reader io.Reader // The decoded content converted to UTF-8
}

// NewPart creates a new Part object. It does not update the parents FirstChild attribute.
Expand All @@ -41,10 +43,10 @@ func NewPart(parent *Part, contentType string) *Part {

// Read returns the decoded & UTF-8 converted content; implements io.Reader.
func (p *Part) Read(b []byte) (n int, err error) {
if p.utf8Reader == nil {
if p.Utf8Reader == nil {
return 0, io.EOF
}
return p.utf8Reader.Read(b)
return p.Utf8Reader.Read(b)
}

// setupContentHeaders uses Content-Type media params and Content-Disposition headers to populate
Expand Down Expand Up @@ -78,13 +80,10 @@ func (p *Part) buildContentReaders(r io.Reader) error {
if _, err := buf.ReadFrom(r); err != nil {
return err
}

var contentReader io.Reader = buf
valid := true

// Raw content reader
p.rawReader = contentReader

// Build content decoding reader
encoding := p.Header.Get(hnContentEncoding)
switch strings.ToLower(encoding) {
Expand All @@ -105,7 +104,6 @@ func (p *Part) buildContentReaders(r io.Reader) error {
encoding)
}
p.decodedReader = contentReader

if valid {
// decodedReader is good; build character set conversion reader
if p.Charset != "" {
Expand All @@ -130,22 +128,20 @@ func (p *Part) buildContentReaders(r io.Reader) error {
}
}
}
p.utf8Reader = contentReader
p.Utf8Reader = contentReader
return nil
}

// ReadParts reads a MIME document from the provided reader and parses it into tree of Part objects.
func ReadParts(r io.Reader) (*Part, error) {
br := bufio.NewReader(r)
root := &Part{}

// Read header
header, err := readHeader(br, root)
if err != nil {
return nil, err
}
root.Header = header

// Content-Type
contentType := header.Get(hnContentType)
if contentType == "" {
Expand All @@ -159,7 +155,6 @@ func ReadParts(r io.Reader) (*Part, error) {
}
root.ContentType = mediatype
root.Charset = params[hpCharset]

if strings.HasPrefix(mediatype, ctMultipartPrefix) {
// Content is multipart, parse it
boundary := params[hpBoundary]
Expand All @@ -169,7 +164,10 @@ func ReadParts(r io.Reader) (*Part, error) {
return nil, err
}
root.Epilogue = epilogue
err = parseParts(root, bufio.NewReader(&emailContent), boundary)
// set the boundary for the root such that it's FirstChild / and NextSiblings
// of FirstChild part can access to their parent's i.e. the root's boundary
root.boundary = boundary
err = parseParts(root, bufio.NewReader(&emailContent))
if err != nil {
return nil, err
}
Expand All @@ -179,7 +177,6 @@ func ReadParts(r io.Reader) (*Part, error) {
return nil, err
}
}

return root, nil
}

Expand Down Expand Up @@ -239,7 +236,6 @@ func parseMediaType(ctype string) (string, map[string]string, error) {
}
return mtype, mparams, err
}

func parseBadContentType(ctype, sep string) string {
cp := strings.Split(ctype, sep)
mctype := ""
Expand All @@ -256,13 +252,21 @@ func parseBadContentType(ctype, sep string) string {
return mctype
}

// parseParts recursively parses a mime multipart document.
func parseParts(parent *Part, reader *bufio.Reader, boundary string) error {
// parseParts recursively parses a mime multipart document and sets each part's PartID
func parseParts(parent *Part, reader *bufio.Reader) error {
var prevSibling *Part

// Handle exception: indexID for root is 0
var indexID int = 0
firstRecursion := parent.Parent == nil
// root case
if firstRecursion {
parent.PartID = "0"
}
// Loop over MIME parts
br := newBoundaryReader(reader, boundary)
br := newBoundaryReader(reader, parent.boundary)
for {
// increment the index which serves for parts' IDs
indexID += 1
next, err := br.Next()
if err != nil && err != io.EOF {
return err
Expand All @@ -271,6 +275,12 @@ func parseParts(parent *Part, reader *bufio.Reader, boundary string) error {
break
}
p := &Part{Parent: parent}
// set this part ID, indicates its place in the tree
if firstRecursion {
p.PartID = strconv.Itoa(indexID)
} else {
p.PartID = p.Parent.PartID + "." + strconv.Itoa(indexID)
}
bbr := bufio.NewReader(br)
header, err := readHeader(bbr, p)
p.Header = header
Expand All @@ -288,15 +298,14 @@ func parseParts(parent *Part, reader *bufio.Reader, boundary string) error {
owner.addWarning(
errorMissingBoundary,
"Boundary %q was not closed correctly",
boundary)
parent.boundary)
break
}
return fmt.Errorf("Error at boundary %v: %v", boundary, err)
return fmt.Errorf("Error at boundary %v: %v", parent.boundary, err)
}
} else if err != nil {
return err
}

ctype := header.Get(hnContentType)
if ctype == "" {
p.addWarning(
Expand All @@ -309,23 +318,20 @@ func parseParts(parent *Part, reader *bufio.Reader, boundary string) error {
return err
}
p.ContentType = mtype

// Set disposition, filename, charset if available
p.setupContentHeaders(mparams)
p.boundary = mparams[hpBoundary]
}

// Insert this Part into the MIME tree
if prevSibling != nil {
prevSibling.NextSibling = p
} else {
parent.FirstChild = p
}
prevSibling = p

if p.boundary != "" {
// Content is another multipart
err = parseParts(p, bbr, p.boundary)
err = parseParts(p, bbr)
if err != nil {
return err
}
Expand All @@ -336,6 +342,9 @@ func parseParts(parent *Part, reader *bufio.Reader, boundary string) error {
}
}
}

// multipart content parts (except for the root) have .0 appended to their PartID
if !firstRecursion {
parent.PartID += ".0"
}
return nil
}

0 comments on commit db23325

Please sign in to comment.