-
Notifications
You must be signed in to change notification settings - Fork 260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Add missing err checks to inbound.Parse #461
base: main
Are you sure you want to change the base?
Conversation
inbound.Parse: fix panic when Content-Type is not multipart
I ran into the exact same problem. This PR fixes it. I'm urging the maintainers to please merge this PR. |
Hi all, Doing a few tests, I noticed that yes, this PR fixes the panics, but leads to leave out parts of the email, I had better luck with this version of the func (email *ParsedEmail) parseRawEmail(rawEmail string) error {
sections := strings.SplitN(rawEmail, "\n\n", 2)
email.parseHeaders(sections[0])
raw, err := parseMultipart(strings.NewReader(sections[1]), email.Headers["Content-Type"])
if err != nil {
return err
}
// if Content-Type is not multipart just set the whole email
if raw == nil {
if len(sections) < 2 {
return nil
}
wholeEmail := sections[1]
// decode if needed
if email.Headers["Content-Transfer-Encoding"] == "quoted-printable" {
decoded, err := io.ReadAll(quotedprintable.NewReader(strings.NewReader(wholeEmail)))
if err != nil {
return err
}
wholeEmail = string(decoded)
}
email.Body[email.Headers["Content-Type"]] = wholeEmail
return nil
}
for {
emailPart, err := raw.NextPart()
// Check for both io.EOF and the wrapped multipart: NextPart: EOF
if err == io.EOF || (err != nil && err.Error() == "multipart: NextPart: EOF") {
break
}
if err != nil {
return err
}
rawEmailBody, err := parseMultipart(emailPart, emailPart.Header.Get("Content-Type"))
if err != nil {
return err
}
if rawEmailBody != nil {
for {
emailBodyPart, err := rawEmailBody.NextPart()
// Check for both io.EOF and the wrapped multipart: NextPart: EOF
if err == io.EOF || (err != nil && err.Error() == "multipart: NextPart: EOF") {
break
}
if err != nil {
return err
}
header := emailBodyPart.Header.Get("Content-Type")
b, err := io.ReadAll(emailPart)
if err != nil {
return err
}
email.Body[header] = string(b)
}
} else if emailPart.FileName() != "" {
b, err := io.ReadAll(emailPart)
if err != nil {
return err
}
email.Attachments[emailPart.FileName()] = b
} else {
header := emailPart.Header.Get("Content-Type")
b, err := io.ReadAll(emailPart)
if err != nil {
return err
}
email.Body[header] = string(b)
}
}
return nil
} Basically, the diffence is that I added the (horrible, but For the record, the wrapped error It's not clear to me why I can't just do if err == io.EOF || (err != nil && errors.Unwrap(err) == io.EOF) {
break
} |
If it can be useful, this is the same in patch format to highlight the changes: diff --git a/helpers/inbound/inbound.go b/helpers/inbound/inbound.go
index fdda393..ab99188 100644
--- a/helpers/inbound/inbound.go
+++ b/helpers/inbound/inbound.go
@@ -4,7 +4,6 @@ import (
"encoding/json"
"fmt"
"io"
- "io/ioutil"
"mime"
"mime/multipart"
"mime/quotedprintable"
@@ -178,7 +177,7 @@ func (email *ParsedEmail) parseRawEmail(rawEmail string) error {
wholeEmail := sections[1]
// decode if needed
if email.Headers["Content-Transfer-Encoding"] == "quoted-printable" {
- decoded, err := ioutil.ReadAll(quotedprintable.NewReader(strings.NewReader(wholeEmail)))
+ decoded, err := io.ReadAll(quotedprintable.NewReader(strings.NewReader(wholeEmail)))
if err != nil {
return err
}
@@ -191,8 +190,12 @@ func (email *ParsedEmail) parseRawEmail(rawEmail string) error {
for {
emailPart, err := raw.NextPart()
- if err == io.EOF {
- return nil
+ // Check for both io.EOF and the wrapped multipart: NextPart: EOF
+ if err == io.EOF || (err != nil && err.Error() == "multipart: NextPart: EOF") {
+ break
+ }
+ if err != nil {
+ return err
}
rawEmailBody, err := parseMultipart(emailPart, emailPart.Header.Get("Content-Type"))
if err != nil {
@@ -201,9 +204,13 @@ func (email *ParsedEmail) parseRawEmail(rawEmail string) error {
if rawEmailBody != nil {
for {
emailBodyPart, err := rawEmailBody.NextPart()
- if err == io.EOF {
+ // Check for both io.EOF and the wrapped multipart: NextPart: EOF
+ if err == io.EOF || (err != nil && err.Error() == "multipart: NextPart: EOF") {
break
}
+ if err != nil {
+ return err
+ }
header := emailBodyPart.Header.Get("Content-Type")
b, err := io.ReadAll(emailPart)
if err != nil {
@@ -228,6 +235,7 @@ func (email *ParsedEmail) parseRawEmail(rawEmail string) error {
email.Body[header] = string(b)
}
}
+ return nil
}
func parseMultipart(body io.Reader, contentType string) (*multipart.Reader, error) {
|
@darioleanbit's changes didn't fix the issue for me. Turns out that I'm getting inbound email requests with headers but an entirely empty body, so the crash is happening at the indexing of sections := strings.SplitN(rawEmail, "\n\n", 2)
email.parseHeaders(sections[0])
raw, err := parseMultipart(strings.NewReader(sections[1]), email.Headers["Content-Type"])
if err != nil {
return err
} In my fork, I fixed this (on top of @darioleanbit's changes), and added a testcase to reproduce the crash and make sure it's fixed. |
Fixes
Added missing err checks that cause panic for some emails.
Checklist