Skip to content

Commit

Permalink
Keep better track of multipart epilogues
Browse files Browse the repository at this point in the history
Fixes issue #181
  • Loading branch information
jstedfast committed Oct 16, 2015
1 parent 5796905 commit 9a558da
Show file tree
Hide file tree
Showing 5 changed files with 2,868 additions and 7 deletions.
25 changes: 24 additions & 1 deletion MimeKit/MimeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1210,10 +1210,33 @@ unsafe BoundaryType MultipartScanPreamble (Multipart multipart, byte* inbuf)
}
}

static bool EndsWithLineFeed (MemoryStream memory)
{
byte[] buffer;

if (memory.Length == 0)
return false;

#if !PORTABLE && !COREFX
buffer = memory.GetBuffer ();
#else
buffer = new byte[1];

memory.Seek (-1, SeekOrigin.End);
memory.Read (buffer, 0, 1);
#endif

return buffer[buffer.Length - 1] == (byte) '\n';
}

unsafe BoundaryType MultipartScanEpilogue (Multipart multipart, byte* inbuf)
{
using (var memory = new MemoryStream ()) {
var found = ScanContent (inbuf, memory, true);
var found = ScanContent (inbuf, memory, false);

if (found == BoundaryType.Eos && !EndsWithLineFeed (memory))
memory.Write (FormatOptions.Default.NewLineBytes, 0, FormatOptions.Default.NewLineBytes.Length);

multipart.RawEpilogue = memory.ToArray ();
return found;
}
Expand Down
17 changes: 11 additions & 6 deletions MimeKit/Multipart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public string Preamble {
return preamble;
}
set {
if (preamble == value)
if (Preamble == value)
return;

if (value != null) {
Expand All @@ -227,8 +227,9 @@ internal byte[] RawEpilogue {
/// Gets or sets the epilogue.
/// </summary>
/// <remarks>
/// A multipart epiloque is the text that ppears after the last
/// child of the multipart and is rarely ever used.
/// A multipart epiloque is the text that appears after the closing boundary
/// of the multipart and is typically either empty or a single new line
/// character sequence.
/// </remarks>
/// <value>The epilogue.</value>
public string Epilogue {
Expand All @@ -239,7 +240,7 @@ public string Epilogue {
return epilogue;
}
set {
if (epilogue == value)
if (Epilogue == value)
return;

if (value != null) {
Expand Down Expand Up @@ -415,7 +416,9 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength =
}

cancellable.Write (boundary, 0, boundary.Length, cancellationToken);
cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);

if (RawEpilogue == null)
cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
} else {
for (int i = 0; i < children.Count; i++) {
cancellationToken.ThrowIfCancellationRequested ();
Expand All @@ -427,7 +430,9 @@ public override void Prepare (EncodingConstraint constraint, int maxLineLength =

cancellationToken.ThrowIfCancellationRequested ();
stream.Write (boundary, 0, boundary.Length);
stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);

if (RawEpilogue == null)
stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
}

if (RawEpilogue != null && RawEpilogue.Length > 0)
Expand Down
19 changes: 19 additions & 0 deletions UnitTests/DkimTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,25 @@ public void TestVerifyGoogleMailDkimSignature ()
Assert.IsTrue (message.Verify (message.Headers[index], new DummyPublicKeyLocator (key)), "Failed to verify GMail signature.");
}

[Test]
public void TestVerifyGoogleMultipartRelatedDkimSignature ()
{
var message = MimeMessage.Load (Path.Combine ("..", "..", "TestData", "dkim", "related.msg"));
int index = message.Headers.IndexOf (HeaderId.DkimSignature);
AsymmetricKeyParameter key;

var txt = message.ToString ().Substring (1100, 2400);

// Note: you can use http://dkimcore.org/tools/dkimrecordcheck.html to get public keys manually
using (var stream = new StreamReader (Path.Combine ("..", "..", "TestData", "dkim", "gmail.pub"))) {
var reader = new PemReader (stream);

key = reader.ReadObject () as AsymmetricKeyParameter;
}

Assert.IsTrue (message.Verify (message.Headers[index], new DummyPublicKeyLocator (key)), "Failed to verify GMail signature.");
}

static void TestDkimSignVerify (MimeMessage message, DkimSignatureAlgorithm signatureAlgorithm, DkimCanonicalizationAlgorithm headerAlgorithm, DkimCanonicalizationAlgorithm bodyAlgorithm)
{
var headers = new HeaderId[] { HeaderId.From, HeaderId.Subject, HeaderId.Date };
Expand Down
Loading

0 comments on commit 9a558da

Please sign in to comment.