Skip to content

Commit

Permalink
Work around a QQMail/Yandex IMAP BODYSTRUCTURE response for empty mul…
Browse files Browse the repository at this point in the history
…tipart

Fixes issue #1861
  • Loading branch information
jstedfast committed Dec 28, 2024
1 parent 1ffd722 commit 19c4357
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 2 deletions.
3 changes: 3 additions & 0 deletions MailKit/Net/Imap/ImapEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ enum ImapQuirksMode {
GMail,
hMailServer,
ProtonMail,
QQMail,
SmarterMail,
SunMicrosystems,
UW,
Expand Down Expand Up @@ -693,6 +694,8 @@ void DetectQuirksMode (string text)
QuirksMode = ImapQuirksMode.Exchange;
else if (text.StartsWith ("Gimap ready", StringComparison.Ordinal))
QuirksMode = ImapQuirksMode.GMail;
else if (text.Contains ("QQMail IMAP4 Server ready"))
QuirksMode = ImapQuirksMode.QQMail;
else if (text.StartsWith ("IMAPrev1", StringComparison.Ordinal)) // https://github.com/hmailserver/hmailserver/blob/master/hmailserver/source/Server/IMAP/IMAPConnection.cpp#L127
QuirksMode = ImapQuirksMode.hMailServer;
else if (text.Contains (" IMAP4rev1 2007f.") || text.Contains (" Panda IMAP "))
Expand Down
10 changes: 8 additions & 2 deletions MailKit/Net/Imap/ImapUtils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1693,7 +1693,7 @@ static bool ShouldParseMultipart (ImapEngine engine, CancellationToken cancellat
case ImapTokenType.Atom: // Note: Technically, we should never get an Atom here, but if we do, we'll treat it as a QString.
case ImapTokenType.QString:
case ImapTokenType.Literal:
if (engine.QuirksMode == ImapQuirksMode.GMail && token.Type != ImapTokenType.Literal) {
if (engine.QuirksMode is ImapQuirksMode.GMail or ImapQuirksMode.QQMail or ImapQuirksMode.Yandex && token.Type != ImapTokenType.Literal) {
// Note: GMail's IMAP server implementation breaks when it encounters nested multiparts with the same
// boundary and returns a BODYSTRUCTURE like the example in https://github.com/jstedfast/MailKit/issues/205
// or like the example in https://github.com/jstedfast/MailKit/issues/777. There's also an issue with BODY
Expand All @@ -1710,6 +1710,9 @@ static bool ShouldParseMultipart (ImapEngine engine, CancellationToken cancellat
// If it is '(', then that would indicate the start of the Content-Type parameter list.
// If it is ')', then that would indicate a BODY response without a Content-Type parameter list.
// If it is NIL, then it would signify that the Content-Type has no parameters.
//
// Note: Yandex also has this problem. See https://github.com/jstedfast/MailKit/issues/1861
// As does QQMail: https://github.com/jstedfast/MailKit/issues/1076

// Peek at the next token to see what we've got. If we get a '(' or NIL, then treat this as a multipart.
nextToken = engine.PeekToken (cancellationToken);
Expand Down Expand Up @@ -1780,7 +1783,7 @@ static async Task<bool> ShouldParseMultipartAsync (ImapEngine engine, Cancellati
case ImapTokenType.Atom: // Note: Technically, we should never get an Atom here, but if we do, we'll treat it as a QString.
case ImapTokenType.QString:
case ImapTokenType.Literal:
if (engine.QuirksMode == ImapQuirksMode.GMail && token.Type != ImapTokenType.Literal) {
if (engine.QuirksMode is ImapQuirksMode.GMail or ImapQuirksMode.QQMail or ImapQuirksMode.Yandex && token.Type != ImapTokenType.Literal) {
// Note: GMail's IMAP server implementation breaks when it encounters nested multiparts with the same
// boundary and returns a BODYSTRUCTURE like the example in https://github.com/jstedfast/MailKit/issues/205
// or like the example in https://github.com/jstedfast/MailKit/issues/777. There's also an issue with BODY
Expand All @@ -1797,6 +1800,9 @@ static async Task<bool> ShouldParseMultipartAsync (ImapEngine engine, Cancellati
// If it is '(', then that would indicate the start of the Content-Type parameter list.
// If it is ')', then that would indicate a BODY response without a Content-Type parameter list.
// If it is NIL, then it would signify that the Content-Type has no parameters.
//
// Note: Yandex also has this problem. See https://github.com/jstedfast/MailKit/issues/1861
// As does QQMail: https://github.com/jstedfast/MailKit/issues/1076

// Peek at the next token to see what we've got. If we get a '(' or NIL, then treat this as a multipart.
nextToken = await engine.PeekTokenAsync (cancellationToken).ConfigureAwait (false);
Expand Down

0 comments on commit 19c4357

Please sign in to comment.