Skip to content

Commit

Permalink
Modified HeaderList.HasBodySeparator logic a bit
Browse files Browse the repository at this point in the history
Also modified MimeMessage.WriteTo() to check this property to fix more
ExperimentalMimeParserTests.
  • Loading branch information
jstedfast committed Feb 22, 2025
1 parent 893a29c commit 7c53ed8
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 28 deletions.
2 changes: 1 addition & 1 deletion MimeKit/AsyncMimeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ public async Task<HeaderList> ParseHeadersAsync (CancellationToken cancellationT

state = eos ? MimeParserState.Eos : MimeParserState.Complete;

var parsed = new HeaderList (options, true);
var parsed = new HeaderList (options);
foreach (var header in headers)
parsed.Add (header);

Expand Down
8 changes: 6 additions & 2 deletions MimeKit/ExperimentalMimeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -856,10 +856,12 @@ public HeaderList ParseHeaders (CancellationToken cancellationToken = default)
throw;
}

var parsed = new HeaderList (Options, hasBodySeparator);
var parsed = new HeaderList (Options);
foreach (var header in headers)
parsed.Add (header);

parsed.HasBodySeparator = hasBodySeparator;

return parsed;
}

Expand Down Expand Up @@ -891,10 +893,12 @@ public async Task<HeaderList> ParseHeadersAsync (CancellationToken cancellationT
throw;
}

var parsed = new HeaderList (Options, hasBodySeparator);
var parsed = new HeaderList (Options);
foreach (var header in headers)
parsed.Add (header);

parsed.HasBodySeparator = hasBodySeparator;

return parsed;
}

Expand Down
34 changes: 28 additions & 6 deletions MimeKit/HeaderList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,12 @@ public sealed class HeaderList : IList<Header>
// this table references the first header of each field
readonly Dictionary<string, Header> table;
readonly List<Header> headers;
bool hasBodySeparator;

internal HeaderList (ParserOptions options, bool hasBodySeparator)
internal HeaderList (ParserOptions options)
{
table = new Dictionary<string, Header> (MimeUtils.OrdinalIgnoreCase);
this.hasBodySeparator = hasBodySeparator;
headers = new List<Header> ();
HasBodySeparator = true;
Options = options;
}

Expand All @@ -66,10 +65,21 @@ internal HeaderList (ParserOptions options, bool hasBodySeparator)
/// <remarks>
/// Creates a new empty header list.
/// </remarks>
public HeaderList () : this (ParserOptions.Default.Clone (), true)
public HeaderList () : this (ParserOptions.Default.Clone ())
{
}

/// <summary>
/// Get or set whether or not the header list has a body separator.
/// </summary>
/// <remarks>
/// Get or set whether or not the header list has a body separator.
/// </remarks>
/// <value><see langword="true"/> if the header list has a body separator; otherwise, <see langword="false"/>.</value>
internal bool HasBodySeparator {
get; set;
}

/// <summary>
/// Add a header with the specified field and value.
/// </summary>
Expand Down Expand Up @@ -456,6 +466,8 @@ public void RemoveAll (HeaderId id)
if (!table.Remove (id.ToHeaderName ()))
return;

HasBodySeparator = true;

for (int i = headers.Count - 1; i >= 0; i--) {
if (headers[i].Id != id)
continue;
Expand Down Expand Up @@ -485,6 +497,8 @@ public void RemoveAll (string field)
if (!table.Remove (field))
return;

HasBodySeparator = true;

for (int i = headers.Count - 1; i >= 0; i--) {
if (!headers[i].Field.Equals (field, StringComparison.OrdinalIgnoreCase))
continue;
Expand Down Expand Up @@ -706,7 +720,7 @@ public void WriteTo (FormatOptions options, Stream stream, CancellationToken can
filtered.Flush (cancellationToken);
}

if (!hasBodySeparator)
if (!HasBodySeparator)
return;

if (stream is ICancellableStream cancellable) {
Expand Down Expand Up @@ -763,7 +777,7 @@ public async Task WriteToAsync (FormatOptions options, Stream stream, Cancellati
await filtered.FlushAsync (cancellationToken).ConfigureAwait (false);
}

if (!hasBodySeparator)
if (!HasBodySeparator)
return;

await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false);
Expand Down Expand Up @@ -858,6 +872,7 @@ public void Add (Header header)

header.Changed += HeaderChanged;
headers.Add (header);
HasBodySeparator = true;

OnChanged (header, HeaderListChangedAction.Added);
}
Expand All @@ -873,6 +888,7 @@ public void Clear ()
foreach (var header in headers)
header.Changed -= HeaderChanged;

HasBodySeparator = true;
headers.Clear ();
table.Clear ();

Expand Down Expand Up @@ -956,6 +972,7 @@ public bool Remove (Header header)
}

headers.RemoveAt (index);
HasBodySeparator = true;

OnChanged (header, HeaderListChangedAction.Removed);

Expand Down Expand Up @@ -1002,6 +1019,8 @@ public void Replace (Header header)
table[header.Field] = header;
headers[i] = header;

HasBodySeparator = true;

OnChanged (first, HeaderListChangedAction.Removed);
OnChanged (header, HeaderListChangedAction.Added);
}
Expand Down Expand Up @@ -1063,6 +1082,7 @@ public void Insert (int index, Header header)

headers.Insert (index, header);
header.Changed += HeaderChanged;
HasBodySeparator = true;

OnChanged (header, HeaderListChangedAction.Added);
}
Expand Down Expand Up @@ -1099,6 +1119,7 @@ public void RemoveAt (int index)
}

headers.RemoveAt (index);
HasBodySeparator = true;

OnChanged (header, HeaderListChangedAction.Removed);
}
Expand Down Expand Up @@ -1169,6 +1190,7 @@ public Header this [int index] {
}

headers[index] = value;
HasBodySeparator = true;

if (header.Field.Equals (value.Field, StringComparison.OrdinalIgnoreCase)) {
OnChanged (value, HeaderListChangedAction.Changed);
Expand Down
3 changes: 2 additions & 1 deletion MimeKit/MimeEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ protected MimeEntity (MimeEntityConstructorArgs args)
if (args is null)
throw new ArgumentNullException (nameof (args));

Headers = new HeaderList (args.ParserOptions, args.HasBodySeparator);
Headers = new HeaderList (args.ParserOptions);
ContentType = args.ContentType;

foreach (var header in args.Headers) {
Expand All @@ -102,6 +102,7 @@ protected MimeEntity (MimeEntityConstructorArgs args)
Headers.Add (header);
}

Headers.HasBodySeparator = args.HasBodySeparator;
ContentType.Changed += ContentTypeChanged;
Headers.Changed += HeadersChanged;
}
Expand Down
21 changes: 12 additions & 9 deletions MimeKit/MimeMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ enum LazyLoadedFields {
internal MimeMessage (ParserOptions options, IEnumerable<Header> headers, RfcComplianceMode mode)
{
addresses = new Dictionary<HeaderId, InternetAddressList> ();
Headers = new HeaderList (options, true);
Headers = new HeaderList (options);

compliance = mode;

Expand Down Expand Up @@ -135,7 +135,7 @@ internal MimeMessage (ParserOptions options, IEnumerable<Header> headers, RfcCom
internal MimeMessage (ParserOptions options)
{
addresses = new Dictionary<HeaderId, InternetAddressList> ();
Headers = new HeaderList (options, true);
Headers = new HeaderList (options);

compliance = RfcComplianceMode.Strict;

Expand Down Expand Up @@ -252,7 +252,7 @@ public MimeMessage (IEnumerable<Header> headers)
if (headers is HeaderList headerList) {
Headers = headerList;
} else {
Headers = new HeaderList (ParserOptions.Default.Clone (), true);
Headers = new HeaderList (ParserOptions.Default.Clone ());

foreach (var header in headers)
Headers.Add (header);
Expand Down Expand Up @@ -1351,11 +1351,13 @@ public void WriteTo (FormatOptions options, Stream stream, bool headersOnly, Can
filtered.Flush (cancellationToken);
}

if (stream is ICancellableStream cancellable) {
cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
} else {
cancellationToken.ThrowIfCancellationRequested ();
stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
if (compliance == RfcComplianceMode.Strict || Body.Headers.HasBodySeparator) {
if (stream is ICancellableStream cancellable) {
cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
} else {
cancellationToken.ThrowIfCancellationRequested ();
stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
}
}

if (!headersOnly) {
Expand Down Expand Up @@ -1425,7 +1427,8 @@ public async Task WriteToAsync (FormatOptions options, Stream stream, bool heade
await filtered.FlushAsync (cancellationToken).ConfigureAwait (false);
}

await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false);
if (compliance == RfcComplianceMode.Strict || Body.Headers.HasBodySeparator)
await stream.WriteAsync (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken).ConfigureAwait (false);

if (!headersOnly) {
try {
Expand Down
2 changes: 1 addition & 1 deletion MimeKit/MimeParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1779,7 +1779,7 @@ unsafe HeaderList ParseHeaders (byte* inbuf, CancellationToken cancellationToken

state = eos ? MimeParserState.Eos : MimeParserState.Complete;

var parsed = new HeaderList (options, true);
var parsed = new HeaderList (options);
foreach (var header in headers)
parsed.Add (header);

Expand Down
16 changes: 8 additions & 8 deletions UnitTests/ExperimentalMimeParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4392,7 +4392,7 @@ public void TestMessageRfc822WithMungedFromMarkerEOF ()
Assert.That (rfc822.Message.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Headers.Count");
Assert.That (rfc822.Message.Body.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Body.Headers.Count");

AssertSerialization (message, NewLineFormat.Unix, text + "\n");
AssertSerialization (message, NewLineFormat.Unix, text);
}

text = text.Replace ("\n", "\r\n");
Expand All @@ -4414,7 +4414,7 @@ public void TestMessageRfc822WithMungedFromMarkerEOF ()
Assert.That (rfc822.Message.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Headers.Count");
Assert.That (rfc822.Message.Body.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Body.Headers.Count");

AssertSerialization (message, NewLineFormat.Dos, text + "\r\n");
AssertSerialization (message, NewLineFormat.Dos, text);
}
}

Expand Down Expand Up @@ -4461,7 +4461,7 @@ public async Task TestMessageRfc822WithMungedFromMarkerEOFAsync ()
Assert.That (rfc822.Message.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Headers.Count");
Assert.That (rfc822.Message.Body.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Body.Headers.Count");

await AssertSerializationAsync (message, NewLineFormat.Unix, text + "\n");
await AssertSerializationAsync (message, NewLineFormat.Unix, text);
}

text = text.Replace ("\n", "\r\n");
Expand All @@ -4483,7 +4483,7 @@ public async Task TestMessageRfc822WithMungedFromMarkerEOFAsync ()
Assert.That (rfc822.Message.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Headers.Count");
Assert.That (rfc822.Message.Body.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Body.Headers.Count");

await AssertSerializationAsync (message, NewLineFormat.Dos, text + "\r\n");
await AssertSerializationAsync (message, NewLineFormat.Dos, text);
}
}

Expand Down Expand Up @@ -4530,7 +4530,7 @@ public void TestMessageRfc822WithTruncatedMungedFromMarker ()
Assert.That (rfc822.Message.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Headers.Count");
Assert.That (rfc822.Message.Body.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Body.Headers.Count");

AssertSerialization (message, NewLineFormat.Unix, text + "\n");
AssertSerialization (message, NewLineFormat.Unix, text);
}

text = text.Replace ("\n", "\r\n");
Expand All @@ -4552,7 +4552,7 @@ public void TestMessageRfc822WithTruncatedMungedFromMarker ()
Assert.That (rfc822.Message.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Headers.Count");
Assert.That (rfc822.Message.Body.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Body.Headers.Count");

AssertSerialization (message, NewLineFormat.Dos, text + "\r\n");
AssertSerialization (message, NewLineFormat.Dos, text);
}
}

Expand Down Expand Up @@ -4599,7 +4599,7 @@ public async Task TestMessageRfc822WithTruncatedMungedFromMarkerAsync ()
Assert.That (rfc822.Message.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Headers.Count");
Assert.That (rfc822.Message.Body.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Body.Headers.Count");

await AssertSerializationAsync (message, NewLineFormat.Unix, text + "\n");
await AssertSerializationAsync (message, NewLineFormat.Unix, text);
}

text = text.Replace ("\n", "\r\n");
Expand All @@ -4621,7 +4621,7 @@ public async Task TestMessageRfc822WithTruncatedMungedFromMarkerAsync ()
Assert.That (rfc822.Message.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Headers.Count");
Assert.That (rfc822.Message.Body.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Body.Headers.Count");

await AssertSerializationAsync (message, NewLineFormat.Dos, text + "\r\n");
await AssertSerializationAsync (message, NewLineFormat.Dos, text);
}
}

Expand Down

0 comments on commit 7c53ed8

Please sign in to comment.