Skip to content

Commit 7c53ed8

Browse files
committed
Modified HeaderList.HasBodySeparator logic a bit
Also modified MimeMessage.WriteTo() to check this property to fix more ExperimentalMimeParserTests.
1 parent 893a29c commit 7c53ed8

7 files changed

+58
-28
lines changed

MimeKit/AsyncMimeParser.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ public async Task<HeaderList> ParseHeadersAsync (CancellationToken cancellationT
532532

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

535-
var parsed = new HeaderList (options, true);
535+
var parsed = new HeaderList (options);
536536
foreach (var header in headers)
537537
parsed.Add (header);
538538

MimeKit/ExperimentalMimeParser.cs

+6-2
Original file line numberDiff line numberDiff line change
@@ -856,10 +856,12 @@ public HeaderList ParseHeaders (CancellationToken cancellationToken = default)
856856
throw;
857857
}
858858

859-
var parsed = new HeaderList (Options, hasBodySeparator);
859+
var parsed = new HeaderList (Options);
860860
foreach (var header in headers)
861861
parsed.Add (header);
862862

863+
parsed.HasBodySeparator = hasBodySeparator;
864+
863865
return parsed;
864866
}
865867

@@ -891,10 +893,12 @@ public async Task<HeaderList> ParseHeadersAsync (CancellationToken cancellationT
891893
throw;
892894
}
893895

894-
var parsed = new HeaderList (Options, hasBodySeparator);
896+
var parsed = new HeaderList (Options);
895897
foreach (var header in headers)
896898
parsed.Add (header);
897899

900+
parsed.HasBodySeparator = hasBodySeparator;
901+
898902
return parsed;
899903
}
900904

MimeKit/HeaderList.cs

+28-6
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,12 @@ public sealed class HeaderList : IList<Header>
5050
// this table references the first header of each field
5151
readonly Dictionary<string, Header> table;
5252
readonly List<Header> headers;
53-
bool hasBodySeparator;
5453

55-
internal HeaderList (ParserOptions options, bool hasBodySeparator)
54+
internal HeaderList (ParserOptions options)
5655
{
5756
table = new Dictionary<string, Header> (MimeUtils.OrdinalIgnoreCase);
58-
this.hasBodySeparator = hasBodySeparator;
5957
headers = new List<Header> ();
58+
HasBodySeparator = true;
6059
Options = options;
6160
}
6261

@@ -66,10 +65,21 @@ internal HeaderList (ParserOptions options, bool hasBodySeparator)
6665
/// <remarks>
6766
/// Creates a new empty header list.
6867
/// </remarks>
69-
public HeaderList () : this (ParserOptions.Default.Clone (), true)
68+
public HeaderList () : this (ParserOptions.Default.Clone ())
7069
{
7170
}
7271

72+
/// <summary>
73+
/// Get or set whether or not the header list has a body separator.
74+
/// </summary>
75+
/// <remarks>
76+
/// Get or set whether or not the header list has a body separator.
77+
/// </remarks>
78+
/// <value><see langword="true"/> if the header list has a body separator; otherwise, <see langword="false"/>.</value>
79+
internal bool HasBodySeparator {
80+
get; set;
81+
}
82+
7383
/// <summary>
7484
/// Add a header with the specified field and value.
7585
/// </summary>
@@ -456,6 +466,8 @@ public void RemoveAll (HeaderId id)
456466
if (!table.Remove (id.ToHeaderName ()))
457467
return;
458468

469+
HasBodySeparator = true;
470+
459471
for (int i = headers.Count - 1; i >= 0; i--) {
460472
if (headers[i].Id != id)
461473
continue;
@@ -485,6 +497,8 @@ public void RemoveAll (string field)
485497
if (!table.Remove (field))
486498
return;
487499

500+
HasBodySeparator = true;
501+
488502
for (int i = headers.Count - 1; i >= 0; i--) {
489503
if (!headers[i].Field.Equals (field, StringComparison.OrdinalIgnoreCase))
490504
continue;
@@ -706,7 +720,7 @@ public void WriteTo (FormatOptions options, Stream stream, CancellationToken can
706720
filtered.Flush (cancellationToken);
707721
}
708722

709-
if (!hasBodySeparator)
723+
if (!HasBodySeparator)
710724
return;
711725

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

766-
if (!hasBodySeparator)
780+
if (!HasBodySeparator)
767781
return;
768782

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

859873
header.Changed += HeaderChanged;
860874
headers.Add (header);
875+
HasBodySeparator = true;
861876

862877
OnChanged (header, HeaderListChangedAction.Added);
863878
}
@@ -873,6 +888,7 @@ public void Clear ()
873888
foreach (var header in headers)
874889
header.Changed -= HeaderChanged;
875890

891+
HasBodySeparator = true;
876892
headers.Clear ();
877893
table.Clear ();
878894

@@ -956,6 +972,7 @@ public bool Remove (Header header)
956972
}
957973

958974
headers.RemoveAt (index);
975+
HasBodySeparator = true;
959976

960977
OnChanged (header, HeaderListChangedAction.Removed);
961978

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

1022+
HasBodySeparator = true;
1023+
10051024
OnChanged (first, HeaderListChangedAction.Removed);
10061025
OnChanged (header, HeaderListChangedAction.Added);
10071026
}
@@ -1063,6 +1082,7 @@ public void Insert (int index, Header header)
10631082

10641083
headers.Insert (index, header);
10651084
header.Changed += HeaderChanged;
1085+
HasBodySeparator = true;
10661086

10671087
OnChanged (header, HeaderListChangedAction.Added);
10681088
}
@@ -1099,6 +1119,7 @@ public void RemoveAt (int index)
10991119
}
11001120

11011121
headers.RemoveAt (index);
1122+
HasBodySeparator = true;
11021123

11031124
OnChanged (header, HeaderListChangedAction.Removed);
11041125
}
@@ -1169,6 +1190,7 @@ public Header this [int index] {
11691190
}
11701191

11711192
headers[index] = value;
1193+
HasBodySeparator = true;
11721194

11731195
if (header.Field.Equals (value.Field, StringComparison.OrdinalIgnoreCase)) {
11741196
OnChanged (value, HeaderListChangedAction.Changed);

MimeKit/MimeEntity.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ protected MimeEntity (MimeEntityConstructorArgs args)
9292
if (args is null)
9393
throw new ArgumentNullException (nameof (args));
9494

95-
Headers = new HeaderList (args.ParserOptions, args.HasBodySeparator);
95+
Headers = new HeaderList (args.ParserOptions);
9696
ContentType = args.ContentType;
9797

9898
foreach (var header in args.Headers) {
@@ -102,6 +102,7 @@ protected MimeEntity (MimeEntityConstructorArgs args)
102102
Headers.Add (header);
103103
}
104104

105+
Headers.HasBodySeparator = args.HasBodySeparator;
105106
ContentType.Changed += ContentTypeChanged;
106107
Headers.Changed += HeadersChanged;
107108
}

MimeKit/MimeMessage.cs

+12-9
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ enum LazyLoadedFields {
107107
internal MimeMessage (ParserOptions options, IEnumerable<Header> headers, RfcComplianceMode mode)
108108
{
109109
addresses = new Dictionary<HeaderId, InternetAddressList> ();
110-
Headers = new HeaderList (options, true);
110+
Headers = new HeaderList (options);
111111

112112
compliance = mode;
113113

@@ -135,7 +135,7 @@ internal MimeMessage (ParserOptions options, IEnumerable<Header> headers, RfcCom
135135
internal MimeMessage (ParserOptions options)
136136
{
137137
addresses = new Dictionary<HeaderId, InternetAddressList> ();
138-
Headers = new HeaderList (options, true);
138+
Headers = new HeaderList (options);
139139

140140
compliance = RfcComplianceMode.Strict;
141141

@@ -252,7 +252,7 @@ public MimeMessage (IEnumerable<Header> headers)
252252
if (headers is HeaderList headerList) {
253253
Headers = headerList;
254254
} else {
255-
Headers = new HeaderList (ParserOptions.Default.Clone (), true);
255+
Headers = new HeaderList (ParserOptions.Default.Clone ());
256256

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

1354-
if (stream is ICancellableStream cancellable) {
1355-
cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
1356-
} else {
1357-
cancellationToken.ThrowIfCancellationRequested ();
1358-
stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
1354+
if (compliance == RfcComplianceMode.Strict || Body.Headers.HasBodySeparator) {
1355+
if (stream is ICancellableStream cancellable) {
1356+
cancellable.Write (options.NewLineBytes, 0, options.NewLineBytes.Length, cancellationToken);
1357+
} else {
1358+
cancellationToken.ThrowIfCancellationRequested ();
1359+
stream.Write (options.NewLineBytes, 0, options.NewLineBytes.Length);
1360+
}
13591361
}
13601362

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

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

14301433
if (!headersOnly) {
14311434
try {

MimeKit/MimeParser.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1779,7 +1779,7 @@ unsafe HeaderList ParseHeaders (byte* inbuf, CancellationToken cancellationToken
17791779

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

1782-
var parsed = new HeaderList (options, true);
1782+
var parsed = new HeaderList (options);
17831783
foreach (var header in headers)
17841784
parsed.Add (header);
17851785

UnitTests/ExperimentalMimeParserTests.cs

+8-8
Original file line numberDiff line numberDiff line change
@@ -4392,7 +4392,7 @@ public void TestMessageRfc822WithMungedFromMarkerEOF ()
43924392
Assert.That (rfc822.Message.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Headers.Count");
43934393
Assert.That (rfc822.Message.Body.Headers.Count, Is.EqualTo (0), "MessagePart.Message.Body.Headers.Count");
43944394

4395-
AssertSerialization (message, NewLineFormat.Unix, text + "\n");
4395+
AssertSerialization (message, NewLineFormat.Unix, text);
43964396
}
43974397

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

4417-
AssertSerialization (message, NewLineFormat.Dos, text + "\r\n");
4417+
AssertSerialization (message, NewLineFormat.Dos, text);
44184418
}
44194419
}
44204420

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

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

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

4486-
await AssertSerializationAsync (message, NewLineFormat.Dos, text + "\r\n");
4486+
await AssertSerializationAsync (message, NewLineFormat.Dos, text);
44874487
}
44884488
}
44894489

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

4533-
AssertSerialization (message, NewLineFormat.Unix, text + "\n");
4533+
AssertSerialization (message, NewLineFormat.Unix, text);
45344534
}
45354535

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

4555-
AssertSerialization (message, NewLineFormat.Dos, text + "\r\n");
4555+
AssertSerialization (message, NewLineFormat.Dos, text);
45564556
}
45574557
}
45584558

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

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

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

4624-
await AssertSerializationAsync (message, NewLineFormat.Dos, text + "\r\n");
4624+
await AssertSerializationAsync (message, NewLineFormat.Dos, text);
46254625
}
46264626
}
46274627

0 commit comments

Comments
 (0)