From d4d2a0fd99c7a6aad66f3a59e7ddb7b448173832 Mon Sep 17 00:00:00 2001 From: Mechiel Lukkien Date: Mon, 13 Jan 2025 16:13:25 +0100 Subject: [PATCH] webmail: when listing messages in backend to send to frontend, don't error out when there's a large plain text part by not trying to parse the full message for the MessageItem, but only reading headers when needed. before previous commit, we wouldn't try reading such messages in full either. --- webmail/api.go | 4 ++-- webmail/message.go | 12 ++++++------ webmail/view.go | 8 ++++---- webmail/webmail.go | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/webmail/api.go b/webmail/api.go index 3bfa309..760ef89 100644 --- a/webmail/api.go +++ b/webmail/api.go @@ -172,7 +172,7 @@ func (Webmail) ParsedMessage(ctx context.Context, msgID int64) (pm ParsedMessage state := msgState{acc: acc} defer state.clear() var err error - pm, err = parsedMessage(log, m, &state, true, false) + pm, err = parsedMessage(log, m, &state, true, false, false) xcheckf(ctx, err, "parsing message") if len(pm.envelope.From) == 1 { @@ -1853,7 +1853,7 @@ func (Webmail) RulesetSuggestMove(ctx context.Context, msgID, mbSrcID, mbDstID i // Parse message for List-Id header. state := msgState{acc: acc} defer state.clear() - pm, err := parsedMessage(log, m, &state, true, false) + pm, err := parsedMessage(log, m, &state, true, false, false) xcheckf(ctx, err, "parsing message") // The suggested ruleset. Once all is checked, we'll return it. diff --git a/webmail/message.go b/webmail/message.go index 0b83b2a..f54ebdf 100644 --- a/webmail/message.go +++ b/webmail/message.go @@ -84,11 +84,11 @@ func messageItemMoreHeaders(moreHeaders []string, pm ParsedMessage) (l [][2]stri } func messageItem(log mlog.Log, m store.Message, state *msgState, moreHeaders []string) (MessageItem, error) { - full := len(moreHeaders) > 0 - pm, err := parsedMessage(log, m, state, full, true) - if err != nil && errors.Is(err, message.ErrHeader) && full { + headers := len(moreHeaders) > 0 + pm, err := parsedMessage(log, m, state, false, true, headers) + if err != nil && errors.Is(err, message.ErrHeader) && headers { log.Debugx("load message item without parsing headers after error", err, slog.Int64("msgid", m.ID)) - pm, err = parsedMessage(log, m, state, false, true) + pm, err = parsedMessage(log, m, state, false, true, false) } if err != nil { return MessageItem{}, fmt.Errorf("parsing message %d for item: %v", m.ID, err) @@ -198,7 +198,7 @@ func formatFirstLine(r io.Reader) (string, error) { return result, scanner.Err() } -func parsedMessage(log mlog.Log, m store.Message, state *msgState, full, msgitem bool) (pm ParsedMessage, rerr error) { +func parsedMessage(log mlog.Log, m store.Message, state *msgState, full, msgitem, msgitemHeaders bool) (pm ParsedMessage, rerr error) { pm.ViewMode = store.ModeText // Valid default, in case this makes it to frontend. if full || msgitem { @@ -247,7 +247,7 @@ func parsedMessage(log mlog.Log, m store.Message, state *msgState, full, msgitem pm.envelope = env } - if full && state.part.BodyOffset > 0 { + if (full || msgitemHeaders) && state.part.BodyOffset > 0 { hdrs, err := state.part.Header() if err != nil { return ParsedMessage{}, fmt.Errorf("parsing headers: %w", err) diff --git a/webmail/view.go b/webmail/view.go index b6c7d44..13a7869 100644 --- a/webmail/view.go +++ b/webmail/view.go @@ -1523,7 +1523,7 @@ func queryMessages(ctx context.Context, log mlog.Log, acc *store.Account, tx *bs // expected to read first, that would be the first unread, which we'll get below // when gathering the thread. found = true - xpm, err := parsedMessage(log, m, &state, true, false) + xpm, err := parsedMessage(log, m, &state, true, false, false) if err != nil && errors.Is(err, message.ErrHeader) { log.Debug("not returning parsed message due to invalid headers", slog.Int64("msgid", m.ID), slog.Any("err", err)) } else if err != nil { @@ -1653,7 +1653,7 @@ func gatherThread(log mlog.Log, tx *bstore.Tx, acc *store.Account, v view, m sto if tm.ID == destMessageID || destMessageID == 0 && first && (pm == nil || !firstUnread && !tm.Seen) { firstUnread = !tm.Seen - xpm, err := parsedMessage(log, tm, &xstate, true, false) + xpm, err := parsedMessage(log, tm, &xstate, true, false, false) if err != nil && errors.Is(err, message.ErrHeader) { log.Debug("not returning parsed message due to invalid headers", slog.Int64("msgid", m.ID), slog.Any("err", err)) } else if err != nil { @@ -1674,7 +1674,7 @@ func gatherThread(log mlog.Log, tx *bstore.Tx, acc *store.Account, v view, m sto if destMessageID == 0 && first && !m.Seen && !firstUnread { xstate := msgState{acc: acc} defer xstate.clear() - xpm, err := parsedMessage(log, m, &xstate, true, false) + xpm, err := parsedMessage(log, m, &xstate, true, false, false) if err != nil && errors.Is(err, message.ErrHeader) { log.Debug("not returning parsed message due to invalid headers", slog.Int64("msgid", m.ID), slog.Any("err", err)) } else if err != nil { @@ -1855,7 +1855,7 @@ var attachmentExtensions = map[string]AttachmentType{ func attachmentTypes(log mlog.Log, m store.Message, state *msgState) (map[AttachmentType]bool, error) { types := map[AttachmentType]bool{} - pm, err := parsedMessage(log, m, state, false, false) + pm, err := parsedMessage(log, m, state, false, false, false) if err != nil { return nil, fmt.Errorf("parsing message for attachments: %w", err) } diff --git a/webmail/webmail.go b/webmail/webmail.go index 1e5ec60..954a87f 100644 --- a/webmail/webmail.go +++ b/webmail/webmail.go @@ -650,7 +650,7 @@ func handle(apiHandler http.Handler, isForwarded bool, accountPath string, w htt state := msgState{acc: acc, m: m, msgr: msgr, part: &p} // note: state is cleared by cleanup - pm, err := parsedMessage(log, m, &state, true, true) + pm, err := parsedMessage(log, m, &state, true, true, true) xcheckf(ctx, err, "getting parsed message") if t[1] == "msgtext" && len(pm.Texts) == 0 || t[1] != "msgtext" && !pm.HasHTML { http.Error(w, "400 - bad request - no such part", http.StatusBadRequest) @@ -682,7 +682,7 @@ func handle(apiHandler http.Handler, isForwarded bool, accountPath string, w htt state := msgState{acc: acc, m: m, msgr: msgr, part: &p} // note: state is cleared by cleanup - pm, err := parsedMessage(log, m, &state, true, true) + pm, err := parsedMessage(log, m, &state, true, true, true) xcheckf(ctx, err, "parsing parsedmessage") pmjson, err := json.Marshal(pm) xcheckf(ctx, err, "marshal parsedmessage") @@ -715,7 +715,7 @@ func handle(apiHandler http.Handler, isForwarded bool, accountPath string, w htt state := msgState{acc: acc, m: m, msgr: msgr, part: &p} // note: state is cleared by cleanup - pm, err := parsedMessage(log, m, &state, true, true) + pm, err := parsedMessage(log, m, &state, true, true, true) xcheckf(ctx, err, "parsing parsedmessage") if len(pm.Texts) == 0 {