Refactor multipart encoding handling and improve content parsing

Refactored the processing of multipart encoding to be robust and easily maintainable. The changes include setting 'QP' encoding as default when the Content-Transfer-Encoding header is empty, accounting for the removal of this header by the standard Go multipart package. Also, parser functions for content type and charset are now independently handling the headers, replacing the split-string approach, thus improving efficiency and code readability.
This commit is contained in:
Winni Neessen 2024-02-10 13:22:38 +01:00
parent 53566a93cd
commit 59e85809f7
Signed by: wneessen
GPG key ID: 385AC9889632126E

28
eml.go
View file

@ -261,24 +261,33 @@ func parseEMLMultipartAlternative(params map[string]string, bodybuf *bytes.Buffe
_ = mpart.Close() _ = mpart.Close()
return fmt.Errorf("failed to read multipart: %w", err) return fmt.Errorf("failed to read multipart: %w", err)
} }
fmt.Printf("CTE: %+v", params)
mpContentType, ok := mpart.Header[HeaderContentType.String()] mpContentType, ok := mpart.Header[HeaderContentType.String()]
if !ok { if !ok {
return fmt.Errorf("failed to get content-type from part") return fmt.Errorf("failed to get content-type from part")
} }
mpContentTypeSplit := strings.Split(mpContentType[0], "; ") conType, charSet := parseContentType(mpContentType[0])
p := m.newPart(ContentType(mpContentTypeSplit[0])) p := m.newPart(ContentType(conType))
parseEMLMultiPartCharset(mpContentTypeSplit, p) p.SetCharset(Charset(charSet))
mpTransferEnc, ok := mpart.Header[HeaderContentTransferEnc.String()] mpTransferEnc, ok := mpart.Header[HeaderContentTransferEnc.String()]
if !ok { if !ok {
return fmt.Errorf("failed to get content-transfer-encoding from part") // If CTE is empty we can assume that it's a quoted-printable CTE since the
// GO stdlib multipart packages deletes that header
// See: https://cs.opensource.google/go/go/+/refs/tags/go1.22.0:src/mime/multipart/multipart.go;l=161
mpTransferEnc = []string{EncodingQP.String()}
} }
switch { switch {
case strings.EqualFold(mpTransferEnc[0], EncodingB64.String()): case strings.EqualFold(mpTransferEnc[0], EncodingB64.String()):
if err := handleEMLMultiPartBase64Encoding(mpdata, p); err != nil { if err := handleEMLMultiPartBase64Encoding(mpdata, p); err != nil {
return fmt.Errorf("failed to handle multipart base64 transfer-encoding: %w", err) return fmt.Errorf("failed to handle multipart base64 transfer-encoding: %w", err)
} }
case strings.EqualFold(mpTransferEnc[0], EncodingQP.String()):
p.SetContent(string(mpdata))
default:
return fmt.Errorf("unsupported Content-Transfer-Encoding")
} }
m.parts = append(m.parts, p) m.parts = append(m.parts, p)
@ -291,17 +300,6 @@ func parseEMLMultipartAlternative(params map[string]string, bodybuf *bytes.Buffe
return nil return nil
} }
// parseEMLMultiPartCharset parses the Charset from a ContentType header and assigns it to a Part
// TODO: This might be redundant to parseContentType
func parseEMLMultiPartCharset(mpContentTypeSplit []string, p *Part) {
if len(mpContentTypeSplit) > 1 && strings.HasPrefix(strings.ToLower(mpContentTypeSplit[1]), "charset=") {
valSplit := strings.Split(mpContentTypeSplit[1], "=")
if len(valSplit) > 1 {
p.SetCharset(Charset(valSplit[1]))
}
}
}
// handleEMLMultiPartBase64Encoding sets the content body of a base64 encoded Part // handleEMLMultiPartBase64Encoding sets the content body of a base64 encoded Part
func handleEMLMultiPartBase64Encoding(mpdata []byte, p *Part) error { func handleEMLMultiPartBase64Encoding(mpdata []byte, p *Part) error {
p.SetEncoding(EncodingB64) p.SetEncoding(EncodingB64)