Skip to content

Commit 57140c5

Browse files
committedJul 9, 2019
Added work-around for IMAP BODYSTRUCTURE responses that treat multiparts as basic parts
Fixes issue #878
1 parent 0be22b5 commit 57140c5

File tree

2 files changed

+47
-4
lines changed

2 files changed

+47
-4
lines changed
 

‎MailKit/Net/Imap/ImapUtils.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -1053,22 +1053,22 @@ public static async Task<BodyPart> ParseBodyAsync (ImapEngine engine, string for
10531053
// if we are parsing a BODYSTRUCTURE, we may get some more tokens before the ')'
10541054
token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false);
10551055

1056-
if (token.Type != ImapTokenType.CloseParen) {
1056+
if (token.Type != ImapTokenType.OpenParen && token.Type != ImapTokenType.CloseParen) {
10571057
body.ContentMd5 = await ReadNStringTokenAsync (engine, format, false, doAsync, cancellationToken).ConfigureAwait (false);
10581058
token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false);
10591059
}
10601060

1061-
if (token.Type != ImapTokenType.CloseParen) {
1061+
if (token.Type != ImapTokenType.OpenParen && token.Type != ImapTokenType.CloseParen) {
10621062
body.ContentDisposition = await ParseContentDispositionAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false);
10631063
token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false);
10641064
}
10651065

1066-
if (token.Type != ImapTokenType.CloseParen) {
1066+
if (token.Type != ImapTokenType.OpenParen && token.Type != ImapTokenType.CloseParen) {
10671067
body.ContentLanguage = await ParseContentLanguageAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false);
10681068
token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false);
10691069
}
10701070

1071-
if (token.Type != ImapTokenType.CloseParen) {
1071+
if (token.Type != ImapTokenType.OpenParen && token.Type != ImapTokenType.CloseParen) {
10721072
body.ContentLocation = await ParseContentLocationAsync (engine, format, doAsync, cancellationToken).ConfigureAwait (false);
10731073
token = await engine.PeekTokenAsync (doAsync, cancellationToken).ConfigureAwait (false);
10741074
}

‎UnitTests/Net/Imap/ImapUtilsTests.cs

+43
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,49 @@ public void TestParseExampleMultiLevelDovecotBodyStructure ()
492492
}
493493
}
494494

495+
// This tests the work-around for issue #878
496+
[Test]
497+
public void TestParseBrokenMultipartRelatedBodyStructure ()
498+
{
499+
const string text = "((\"multipart\" \"related\" (\"boundary\" \"----=_@@@@BeautyqueenS87@_@147836_6893840099.85426606923635\") NIL NIL \"7BIT\" 400 (\"boundary\" \"----=_@@@@BeautyqueenS87@_@147836_6893840099.85426606923635\") NIL NIL NIL)(\"TEXT\" \"html\" (\"charset\" \"UTF8\") NIL NIL \"7BIT\" 1115 70 NIL NIL NIL NIL)(\"TEXT\" \"html\" (\"charset\" \"UTF8\") NIL NIL \"QUOTED-PRINTABLE\" 16 2 NIL NIL NIL NIL) \"mixed\" (\"boundary\" \"----=--_DRYORTABLE@@@_@@@8957836_03253840099.78526606923635\") NIL NIL NIL)\r\n";
500+
using (var memory = new MemoryStream (Encoding.ASCII.GetBytes (text), false)) {
501+
using (var tokenizer = new ImapStream (memory, null, new NullProtocolLogger ())) {
502+
using (var engine = new ImapEngine (null)) {
503+
BodyPart body;
504+
505+
engine.SetStream (tokenizer);
506+
507+
try {
508+
body = ImapUtils.ParseBodyAsync (engine, "Unexpected token: {0}", string.Empty, false, CancellationToken.None).GetAwaiter ().GetResult ();
509+
} catch (Exception ex) {
510+
Assert.Fail ("Parsing BODYSTRUCTURE failed: {0}", ex);
511+
return;
512+
}
513+
514+
var token = engine.ReadToken (CancellationToken.None);
515+
Assert.AreEqual (ImapTokenType.Eoln, token.Type, "Expected new-line, but got: {0}", token);
516+
517+
Assert.IsInstanceOf<BodyPartMultipart> (body, "Body types did not match.");
518+
var multipart = (BodyPartMultipart) body;
519+
520+
Assert.IsTrue (multipart.ContentType.IsMimeType ("multipart", "mixed"), "multipart/mixed Content-Type did not match.");
521+
Assert.AreEqual ("----=--_DRYORTABLE@@@_@@@8957836_03253840099.78526606923635", multipart.ContentType.Parameters["boundary"], "boundary param did not match");
522+
Assert.AreEqual (3, multipart.BodyParts.Count, "BodyParts count did not match.");
523+
524+
Assert.IsInstanceOf<BodyPartBasic> (multipart.BodyParts[0], "The type of the first child did not match.");
525+
Assert.IsInstanceOf<BodyPartText> (multipart.BodyParts[1], "The type of the second child did not match.");
526+
Assert.IsInstanceOf<BodyPartText> (multipart.BodyParts[2], "The type of the third child did not match.");
527+
528+
var related = (BodyPartBasic) multipart.BodyParts[0];
529+
Assert.IsTrue (related.ContentType.IsMimeType ("multipart", "related"), "multipart/related Content-Type did not match.");
530+
Assert.AreEqual ("----=_@@@@BeautyqueenS87@_@147836_6893840099.85426606923635", related.ContentType.Parameters["boundary"], "multipart/related boundary param did not match");
531+
Assert.AreEqual ("7BIT", related.ContentTransferEncoding, "multipart/related Content-Transfer-Encoding did not match.");
532+
Assert.AreEqual (400, related.Octets, "multipart/related octets do not match.");
533+
}
534+
}
535+
}
536+
}
537+
495538
[Test]
496539
public void TestParseBodyStructureWithContentMd5DspLanguageAndLocation ()
497540
{

0 commit comments

Comments
 (0)