Bug Report
Steps to Reproduce
Connect directly to raven's IMAPS port, authenticate, select INBOX, and issue a FETCH requesting two literal-bearing items in one command — the exact request Nextcloud's Mail app (Horde IMAP client) sends when opening a message body:
1 AUTHENTICATE PLAIN <creds>
2 SELECT INBOX
3 UID FETCH 6 (BODY.PEEK[1] BODY.PEEK[1.MIME])
Expected Behavior
Per RFC 3501 §4.3, each literal must immediately follow the item name it belongs to:
* 6 FETCH (UID 6 BODY[1] {197}
<197 bytes of body text>
BODY[1.MIME] {43}
Content-Type: text/plain; charset=UTF-8
)
3 OK UID FETCH completed
Actual Behavior
All item names are emitted first, then all literals concatenated together afterward — captured byte-for-byte from a live raven:dev instance:
* 6 FETCH (UID 6 BODY[1] BODY[1.MIME] {197}
<197 bytes of body text> {43}
Content-Type: text/plain; charset=UTF-8
)
3 OK UID FETCH completed
This is not valid IMAP. A client parsing positionally has no way to associate the trailing literals with BODY[1] and BODY[1.MIME]. Confirmed against Nextcloud 34.0.2.1's Mail app, which throws Cannot access offset of type Horde_Stream_Temp in isset or empty in Horde_Imap_Client_Socket::_parseFetch and fails to open the message. Any FETCH requesting a single literal item works fine — this only breaks once two or more literal-bearing items are requested together.
Root cause: internal/server/message/fetch.go:212 declares one shared literalData string for the whole FETCH response. Every literal-bearing block appends its item name to responseParts, but appends its literal bytes to that separate shared string instead:
- numeric body sections,
BODY[n] / BODY[n.MIME] (~line 404-416)
BODY[HEADER.FIELDS (...)] (~line 500)
BODY[TEXT] (~line 537)
BODY[HEADER] (~line 553)
RFC822.HEADER (~line 568)
RFC822.TEXT (~line 583)
- full
BODY[] (~line 596)
The final response (~line 600) joins all names first, then appends the concatenated literals. Correct only when exactly one literal item is requested per FETCH.
Suggested fix: remove the shared literalData accumulator. At each append site, build the item's {size}\r\n<data> inline as part of the same responseParts entry, in call order, then join responseParts alone at the end.
Relevant Logs / Screenshots
- raven image:
ghcr.io/opengovmail/raven:dev
- raven source:
github.com/OpenGovMail/raven, repro'd against 0df808b on main
- Client: Nextcloud 34.0.2.1, Mail app (Horde_Imap_Client)
Bug Report
Steps to Reproduce
Connect directly to raven's IMAPS port, authenticate, select INBOX, and issue a FETCH requesting two literal-bearing items in one command — the exact request Nextcloud's Mail app (Horde IMAP client) sends when opening a message body:
Expected Behavior
Per RFC 3501 §4.3, each literal must immediately follow the item name it belongs to:
Actual Behavior
All item names are emitted first, then all literals concatenated together afterward — captured byte-for-byte from a live
raven:devinstance:This is not valid IMAP. A client parsing positionally has no way to associate the trailing literals with
BODY[1]andBODY[1.MIME]. Confirmed against Nextcloud 34.0.2.1's Mail app, which throwsCannot access offset of type Horde_Stream_Temp in isset or emptyinHorde_Imap_Client_Socket::_parseFetchand fails to open the message. Any FETCH requesting a single literal item works fine — this only breaks once two or more literal-bearing items are requested together.Root cause:
internal/server/message/fetch.go:212declares one sharedliteralDatastring for the whole FETCH response. Every literal-bearing block appends its item name toresponseParts, but appends its literal bytes to that separate shared string instead:BODY[n]/BODY[n.MIME](~line 404-416)BODY[HEADER.FIELDS (...)](~line 500)BODY[TEXT](~line 537)BODY[HEADER](~line 553)RFC822.HEADER(~line 568)RFC822.TEXT(~line 583)BODY[](~line 596)The final response (~line 600) joins all names first, then appends the concatenated literals. Correct only when exactly one literal item is requested per FETCH.
Suggested fix: remove the shared
literalDataaccumulator. At each append site, build the item's{size}\r\n<data>inline as part of the sameresponsePartsentry, in call order, then joinresponsePartsalone at the end.Relevant Logs / Screenshots
ghcr.io/opengovmail/raven:devgithub.com/OpenGovMail/raven, repro'd against0df808bonmain