From 0ccbd5bafb0ebaa6456eb2935156a2ebb1536c11 Mon Sep 17 00:00:00 2001 From: sysops Date: Tue, 30 Jun 2026 20:17:33 +0200 Subject: [PATCH] fix(mailparser): kaputte/truncated Multipart-Mails partiell indexieren statt scheitern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bei NextPart()-Fehler (EOF, malformed header) wird der bereits geparste Inhalt zurückgegeben statt der gesamte Parse abgebrochen. parseMultipart() gibt keinen Fehler mehr zurück — partieller Text/HTML ist besser als gar kein Index-Eintrag. Co-Authored-By: Claude Sonnet 4.6 --- pkg/mailparser/parser.go | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/pkg/mailparser/parser.go b/pkg/mailparser/parser.go index c554535..32141df 100644 --- a/pkg/mailparser/parser.go +++ b/pkg/mailparser/parser.go @@ -180,9 +180,9 @@ func Parse(raw []byte) (*ParsedMail, error) { if strings.HasPrefix(mediaType, "multipart/") { boundary := params["boundary"] - if err := parseMultipart(pm, msg.Body, boundary); err != nil { - return nil, fmt.Errorf("mailparser: multipart: %w", err) - } + // Ignore parse errors — return partial content instead of failing completely. + // Malformed/truncated multipart emails still get metadata + whatever parts parsed. + parseMultipart(pm, msg.Body, boundary) //nolint:errcheck } else { body, _ := io.ReadAll(msg.Body) decoded := decodeBody(body, msg.Header.Get("Content-Transfer-Encoding")) @@ -198,15 +198,14 @@ func Parse(raw []byte) (*ParsedMail, error) { } // parseMultipart walks MIME parts and fills text, html, and attachments. -func parseMultipart(pm *ParsedMail, body io.Reader, boundary string) error { +// Truncated or malformed parts are skipped — partial content is better than nothing. +func parseMultipart(pm *ParsedMail, body io.Reader, boundary string) { mr := multipart.NewReader(body, boundary) for { part, err := mr.NextPart() - if err == io.EOF { - break - } if err != nil { - return err + // io.EOF = normal end; any other error = truncated/malformed — stop here. + break } ct := part.Header.Get("Content-Type") @@ -244,9 +243,7 @@ func parseMultipart(pm *ParsedMail, body io.Reader, boundary string) error { // Nested multipart if strings.HasPrefix(mediaType, "multipart/") { - if err := parseMultipart(pm, bytes.NewReader(decoded), params["boundary"]); err != nil { - return err - } + parseMultipart(pm, bytes.NewReader(decoded), params["boundary"]) continue } @@ -257,7 +254,6 @@ func parseMultipart(pm *ParsedMail, body io.Reader, boundary string) error { pm.HTMLBody += string(decoded) } } - return nil } // decodeCharset converts data from the declared MIME charset to UTF-8.