Conversation
newPart sets HeaderOffset and BodyOffset to the start of the header, and only moves BodyOffset once it has read the empty line that ends the header. When parsing stops before that, e.g. on a header line longer than maxLineLength, both offsets are still at the start. fallbackPart copies them, so the part it returns has a zero-length header and a body that is the whole message. Over IMAP that means FETCH BODY[HEADER] and BODY[HEADER.FIELDS] return nothing, while BODY[TEXT] returns the header as well. The bytes are not withheld, they are served in the wrong place, and a client that reads headers to identify a message sees none. Messages like this arrive over IMAP APPEND, which is not gated by the line length limits that SMTP submission applies. Apple Mail files its own copy of a sent message into the Sent mailbox that way and writes References unfolded, so a long enough thread gives a header line over 998 bytes. Such a message is malformed, but mox has already decided to store it rather than reject it, and MessageAdd notes that p is still valid where it does so. So look for the end of the header again in the fallback, without a limit on the length of a line. Only when parsing stopped before the split was established: a part whose header parsed and that failed later keeps the offsets it has, and if there is no empty line at all nothing changes. Messages already stored with the old offsets can be repaired with "mox reparse".
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A message whose parse fails before the end of the header is found is stored with
HeaderOffset == BodyOffset, so the whole message becomes its body. Over IMAP,FETCH BODY[HEADER]andBODY[HEADER.FIELDS (...)]return a zero-lengthliteral, while
BODY[TEXT]returns the header as well.newPartsets both offsets to the start of the header and only movesBodyOffsetafter reading the empty line that ends it:fallbackPartcopies those offsets verbatim.Reproducing
Append a message with one header line longer than
maxLineLengthand fetch itback. Against
mox localserve, which setsPedantic: trueand so uses the 1000byte limit:
With
Pedantic: falsethe threshold moves to 8192, asmaxLineLength()says,and the behaviour above 8192 is the same. Same results on a clean checkout of
main and on a branch of my own, so it is not something local to me.
The APPEND is accepted, so the message is stored this way permanently.
Why such a message shows up
SMTP submission rejects it:
550 5.6.0 cannot parse header or From address: parsing message: reading header line: line too long. Inbound delivery logs theparse error and continues (
smtpserver/server.go, indeliver), and IMAPAPPEND has no such gate at all.
I ran into it migrating a mailbox into mox with an IMAP client: 19 messages out
of 11770 in a Sent mailbox, all filed there by Apple Mail, which appends its own
copy of a sent message and writes
Referencesunfolded. A long enough threadgives a header line over 998 bytes. The messages are malformed, but mox has
decided to keep them rather than reject them, and
MessageAddnotes "p is stillvalid" where it stores the part.
The change
Look for the end of the header again in the fallback, without a limit on line
length. Only when parsing stopped before the split was established, so a part
whose header parsed and that failed later keeps the offsets it has; if there is
no empty line at all, nothing changes. Messages that parse today are unaffected,
since
fallbackPartis only reached after an error.Existing messages can be repaired with
mox reparse.The test covers the basic case and the empty line straddling two chunks of the
scan.
go test ./message/ ./imapserver/ ./store/ ./smtpserver/ ./webmail/passes.Not addressed
The fallback still reports
application/octet-streamwith an empty envelope, soENVELOPEandBODYSTRUCTUREstay uninformative for these messages even thoughthe header is now readable. Parsing the recovered header to fill those in, or
skipping just the over-long field so the message parses normally, both seemed
like bigger decisions than this fix, and I would rather leave them to you.