Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/EdwinLove/parsemail
module github.com/Dragomir-Ivanov/parsemail

go 1.12
74 changes: 50 additions & 24 deletions parsemail.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io/ioutil"
"mime"
"mime/multipart"
"mime/quotedprintable"
"net/mail"
"regexp"
"strings"
Expand Down Expand Up @@ -36,17 +37,28 @@ func Parse(r io.Reader) (email Email, err error) {
if err != nil {
return
}
encoding := msg.Header.Get("Content-Transfer-Encoding")

switch contentType {
case contentTypeMultipartMixed:
email.TextBody, email.HTMLBody, email.Attachments, email.EmbeddedFiles, err = parseMultipartMixed(msg.Body, params["boundary"])
case contentTypeMultipartAlternative:
email.TextBody, email.HTMLBody, email.EmbeddedFiles, err = parseMultipartAlternative(msg.Body, params["boundary"])
case contentTypeMultipartRelated:
email.TextBody, email.HTMLBody, email.EmbeddedFiles, err = parseMultipartRelated(msg.Body, params["boundary"])
case contentTypeTextPlain:
message, _ := ioutil.ReadAll(msg.Body)
var message []byte
message, err = decodeData(msg.Body, encoding)
if err != nil {
return
}
email.TextBody = strings.TrimSuffix(string(message[:]), "\n")
case contentTypeTextHtml:
message, _ := ioutil.ReadAll(msg.Body)
var message []byte
message, err = decodeData(msg.Body, encoding)
if err != nil {
return
}
email.HTMLBody = strings.TrimSuffix(string(message[:]), "\n")
default:
err = fmt.Errorf("Unknown top level mime type: %s", contentType)
Expand Down Expand Up @@ -119,14 +131,22 @@ func parseMultipartRelated(msg io.Reader, boundary string) (textBody, htmlBody s

switch contentType {
case contentTypeTextPlain:
ppContent, err := ioutil.ReadAll(part)
ppContentReader, err := decodePartData(part)
if err != nil {
return textBody, htmlBody, embeddedFiles, err
}
ppContent, err := ioutil.ReadAll(ppContentReader)
if err != nil {
return textBody, htmlBody, embeddedFiles, err
}

textBody += strings.TrimSuffix(string(ppContent[:]), "\n")
case contentTypeTextHtml:
ppContent, err := ioutil.ReadAll(part)
ppContentReader, err := decodePartData(part)
if err != nil {
return textBody, htmlBody, embeddedFiles, err
}
ppContent, err := ioutil.ReadAll(ppContentReader)
if err != nil {
return textBody, htmlBody, embeddedFiles, err
}
Expand Down Expand Up @@ -176,14 +196,22 @@ func parseMultipartAlternative(msg io.Reader, boundary string) (textBody, htmlBo

switch contentType {
case contentTypeTextPlain:
ppContent, err := ioutil.ReadAll(part)
ppContentReader, err := decodePartData(part)
if err != nil {
return textBody, htmlBody, embeddedFiles, err
}
ppContent, err := ioutil.ReadAll(ppContentReader)
if err != nil {
return textBody, htmlBody, embeddedFiles, err
}

textBody += strings.TrimSuffix(string(ppContent[:]), "\n")
case contentTypeTextHtml:
ppContent, err := ioutil.ReadAll(part)
ppContentReader, err := decodePartData(part)
if err != nil {
return textBody, htmlBody, embeddedFiles, err
}
ppContent, err := ioutil.ReadAll(ppContentReader)
if err != nil {
return textBody, htmlBody, embeddedFiles, err
}
Expand Down Expand Up @@ -314,29 +342,27 @@ func decodeHeaderMime(header mail.Header) (mail.Header, error) {
return mail.Header(parsedHeader), nil
}

func decodePartData(part *multipart.Part) (io.Reader, error) {
encoding := part.Header.Get("Content-Transfer-Encoding")

func decodeData(data io.Reader, encoding string) ([]byte, error) {
if strings.EqualFold(encoding, "base64") {
dr := base64.NewDecoder(base64.StdEncoding, part)
dd, err := ioutil.ReadAll(dr)
if err != nil {
return nil, err
}

return bytes.NewReader(dd), nil
} else if strings.EqualFold(encoding, "7bit") {
dd, err := ioutil.ReadAll(part)
if err != nil {
return nil, err
}

return bytes.NewReader(dd), nil
dr := base64.NewDecoder(base64.StdEncoding, data)
return ioutil.ReadAll(dr)
} else if strings.EqualFold(encoding, "7bit") || strings.EqualFold(encoding, "8bit") || encoding == "" {
return ioutil.ReadAll(data)
} else if strings.EqualFold(encoding, "quoted-printable") {
return ioutil.ReadAll(quotedprintable.NewReader(data))
}

return nil, fmt.Errorf("Unknown encoding: %s", encoding)
}

func decodePartData(part *multipart.Part) (io.Reader, error) {
encoding := part.Header.Get("Content-Transfer-Encoding")
dd, err := decodeData(part, encoding)
if err != nil {
return nil, err
}
return bytes.NewReader(dd), nil
}

func isEmbeddedFile(part *multipart.Part) bool {
return part.Header.Get("Content-Transfer-Encoding") != ""
}
Expand Down