Skip to content

Commit 7d87555

Browse files
committed
detect bare linefeeds
1 parent ba19975 commit 7d87555

File tree

3 files changed

+63
-30
lines changed

3 files changed

+63
-30
lines changed

MimeKit/AsyncMimeReader.cs

+16-3
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ async Task StepMboxMarkerAsync (CancellationToken cancellationToken)
129129
async Task StepHeadersAsync (CancellationToken cancellationToken)
130130
{
131131
int headersBeginLineNumber = lineNumber;
132+
bool detectedBareLinefeed = false;
132133
var eof = false;
133134

134135
headerBlockBegin = GetOffset (inputIndex);
@@ -175,7 +176,7 @@ async Task StepHeadersAsync (CancellationToken cancellationToken)
175176
}
176177

177178
// Check for an empty line denoting the end of the header block.
178-
if (IsEndOfHeaderBlock (left)) {
179+
if (IsEndOfHeaderBlock (left, ref detectedBareLinefeed)) {
179180
state = MimeParserState.Content;
180181
break;
181182
}
@@ -272,12 +273,12 @@ async Task StepHeadersAsync (CancellationToken cancellationToken)
272273
do {
273274
unsafe {
274275
fixed (byte* inbuf = input) {
275-
if (StepHeaderValue (inbuf, ref midline))
276+
if (StepHeaderValue (inbuf, ref midline, ref detectedBareLinefeed))
276277
break;
277278
}
278279
}
279280

280-
if (await ReadAheadAsync (1, 0, cancellationToken).ConfigureAwait (false) == 0) {
281+
if (await ReadAheadAsync (1, 1, cancellationToken).ConfigureAwait (false) == 0) {
281282
if (midline)
282283
OnComplianceIssueEncountered (MimeComplianceStatus.IncompleteHeader, beginOffset, beginLineNumber);
283284
else
@@ -304,6 +305,9 @@ async Task StepHeadersAsync (CancellationToken cancellationToken)
304305
Debugger.Break ();
305306
#endif
306307

308+
if (detectedBareLinefeed)
309+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInHeader, headerBlockBegin, headersBeginLineNumber);
310+
307311
headerBlockEnd = GetOffset (inputIndex);
308312

309313
await OnHeadersEndAsync (headerBlockBegin, headersBeginLineNumber, headerBlockEnd, lineNumber, cancellationToken).ConfigureAwait (false);
@@ -416,6 +420,9 @@ async Task<int> ConstructMimePartAsync (CancellationToken cancellationToken)
416420
var result = await ScanContentAsync (ScanContentType.MimeContent, beginOffset, beginLineNumber, true, cancellationToken).ConfigureAwait (false);
417421
await OnMimePartContentEndAsync (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, result.Format, cancellationToken).ConfigureAwait (false);
418422

423+
if (result.Format != NewLineFormat.Dos)
424+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
425+
419426
return result.Lines;
420427
}
421428

@@ -510,6 +517,9 @@ async Task MultipartScanPreambleAsync (CancellationToken cancellationToken)
510517
await OnMultipartPreambleBeginAsync (beginOffset, beginLineNumber, cancellationToken).ConfigureAwait (false);
511518
var result = await ScanContentAsync (ScanContentType.MultipartPreamble, beginOffset, beginLineNumber, false, cancellationToken).ConfigureAwait (false);
512519
await OnMultipartPreambleEndAsync (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, cancellationToken).ConfigureAwait (false);
520+
521+
if (result.Format != NewLineFormat.Dos)
522+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
513523
}
514524

515525
async Task MultipartScanEpilogueAsync (CancellationToken cancellationToken)
@@ -520,6 +530,9 @@ async Task MultipartScanEpilogueAsync (CancellationToken cancellationToken)
520530
await OnMultipartEpilogueBeginAsync (beginOffset, beginLineNumber, cancellationToken).ConfigureAwait (false);
521531
var result = await ScanContentAsync (ScanContentType.MultipartEpilogue, beginOffset, beginLineNumber, true, cancellationToken).ConfigureAwait (false);
522532
await OnMultipartEpilogueEndAsync (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, cancellationToken).ConfigureAwait (false);
533+
534+
if (result.Format != NewLineFormat.Dos)
535+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
523536
}
524537

525538
async Task MultipartScanSubpartsAsync (ContentType multipartContentType, int depth, CancellationToken cancellationToken)

MimeKit/MimeComplianceStatus.cs

+22-22
Original file line numberDiff line numberDiff line change
@@ -41,82 +41,82 @@ public enum MimeComplianceStatus {
4141
/// </summary>
4242
Compliant = 0,
4343

44+
/// <summary>
45+
/// A line was found in a header that was linefeed terminated instead of carriage return &amp; linefeed terminated.
46+
/// </summary>
47+
BareLinefeedInHeader = 1 << 0,
48+
49+
/// <summary>
50+
/// A line was found in a MIME part body content that was linefeed terminated instead of carriage return &amp; linefeed terminated.
51+
/// </summary>
52+
BareLinefeedInBody = 1 << 1,
53+
4454
/// <summary>
4555
/// The header was not of the correct form.
4656
/// </summary>
47-
InvalidHeader = 1 << 0,
57+
InvalidHeader = 1 << 2,
4858

4959
/// <summary>
5060
/// The header ended prematurely at the end of the stream.
5161
/// </summary>
52-
IncompleteHeader = 1 << 1,
62+
IncompleteHeader = 1 << 3,
5363

5464
/// <summary>
5565
/// The Content-Transfer-Encoding header value was not valid.
5666
/// </summary>
57-
InvalidContentTransferEncoding = 1 << 2,
67+
InvalidContentTransferEncoding = 1 << 4,
5868

5969
/// <summary>
6070
/// The Content-Type header value was not valid.
6171
/// </summary>
62-
InvalidContentType = 1 << 3,
72+
InvalidContentType = 1 << 5,
6373

6474
/// <summary>
6575
/// The MIME-Version header value was not valid.
6676
/// </summary>
67-
InvalidMimeVersion = 1 << 4,
77+
InvalidMimeVersion = 1 << 6,
6878

6979
/// <summary>
7080
/// A line was found that was longer than the SMTP limit of 1000 characters.
7181
/// </summary>
72-
InvalidWrapping = 1 << 5,
82+
InvalidWrapping = 1 << 7,
7383

7484
/// <summary>
7585
/// An empty line separating the headers from the body was missing.
7686
/// </summary>
77-
MissingBodySeparator = 1 << 6,
87+
MissingBodySeparator = 1 << 8,
7888

7989
/// <summary>
8090
/// The MIME-Version header is missing.
8191
/// </summary>
82-
MissingMimeVersion = 1 << 7,
92+
MissingMimeVersion = 1 << 9,
8393

8494
/// <summary>
8595
/// The boundary parameter is missing from a multipart Content-Type header.
8696
/// </summary>
87-
MissingMultipartBoundaryParameter = 1 << 8,
97+
MissingMultipartBoundaryParameter = 1 << 10,
8898

8999
/// <summary>
90100
/// A multipart boundary was missing.
91101
/// </summary>
92-
MissingMultipartBoundary = 1 << 9,
102+
MissingMultipartBoundary = 1 << 11,
93103

94104
/// <summary>
95105
/// A MIME part contained multiple Content-Transfer-Encoding headers.
96106
/// </summary>
97-
DuplicateContentTransferEncoding = 1 << 10,
107+
DuplicateContentTransferEncoding = 1 << 12,
98108

99109
/// <summary>
100110
/// A MIME part contained multiple Content-Type headers.
101111
/// </summary>
102-
DuplicateContentType = 1 << 11,
112+
DuplicateContentType = 1 << 13,
103113

104114
#if false
105-
/// <summary>
106-
/// A line was found in a MIME part body content that was linefeed terminated instead of carriage return &amp; linefeed terminated.
107-
/// </summary>
108-
BareLinefeedInBody,
109-
110115
/// <summary>
111116
/// An external body was specified with invalid syntax.
112117
/// </summary>
113118
InvalidExternalBody,
114119

115-
/// <summary>
116-
/// A line was found in a MIME part header that was linefeed terminated instead of carriage return &amp; linefeed terminated.
117-
/// </summary>
118-
BareLinefeedInHeader,
119-
120120
/// <summary>
121121
/// Unexpected binary content was found in MIME part body content.
122122
/// </summary>

MimeKit/MimeReader.cs

+25-5
Original file line numberDiff line numberDiff line change
@@ -1525,7 +1525,7 @@ void StepHeaderField (int headerFieldLength)
15251525
inputIndex += headerIndex;
15261526
}
15271527

1528-
unsafe bool StepHeaderValue (byte* inbuf, ref bool midline)
1528+
unsafe bool StepHeaderValue (byte* inbuf, ref bool midline, ref bool detectedBareLinefeed)
15291529
{
15301530
byte* inptr = inbuf + inputIndex;
15311531
byte* inend = inbuf + inputEnd;
@@ -1545,6 +1545,9 @@ unsafe bool StepHeaderValue (byte* inbuf, ref bool midline)
15451545
break;
15461546
}
15471547

1548+
if (index > 0 && *(inptr - 1) != (byte) '\r')
1549+
detectedBareLinefeed = true;
1550+
15481551
// Consume the newline and update our line number state.
15491552
inptr++;
15501553

@@ -1569,12 +1572,13 @@ unsafe bool StepHeaderValue (byte* inbuf, ref bool midline)
15691572
return inptr < inend;
15701573
}
15711574

1572-
bool IsEndOfHeaderBlock (int left)
1575+
bool IsEndOfHeaderBlock (int left, ref bool detectedBareLinefeed)
15731576
{
15741577
if (input[inputIndex] == (byte) '\n') {
15751578
state = MimeParserState.Content;
15761579
inputIndex++;
15771580
IncrementLineNumber (inputIndex);
1581+
detectedBareLinefeed = true;
15781582
return true;
15791583
}
15801584

@@ -1675,6 +1679,7 @@ Header CreateHeader (long beginOffset, int beginLineNumber, int fieldNameLength,
16751679
unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken)
16761680
{
16771681
int headersBeginLineNumber = lineNumber;
1682+
bool detectedBareLinefeed = false;
16781683
var eof = false;
16791684

16801685
headerBlockBegin = GetOffset (inputIndex);
@@ -1721,7 +1726,7 @@ unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken)
17211726
}
17221727

17231728
// Check for an empty line denoting the end of the header block.
1724-
if (IsEndOfHeaderBlock (left)) {
1729+
if (IsEndOfHeaderBlock (left, ref detectedBareLinefeed)) {
17251730
state = MimeParserState.Content;
17261731
break;
17271732
}
@@ -1794,8 +1799,8 @@ unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken)
17941799
bool midline = true;
17951800

17961801
// Consume the header value.
1797-
while (!StepHeaderValue (inbuf, ref midline)) {
1798-
if (ReadAhead (1, 0, cancellationToken) == 0) {
1802+
while (!StepHeaderValue (inbuf, ref midline, ref detectedBareLinefeed)) {
1803+
if (ReadAhead (1, 1, cancellationToken) == 0) {
17991804
if (midline)
18001805
OnComplianceIssueEncountered (MimeComplianceStatus.IncompleteHeader, beginOffset, beginLineNumber);
18011806
else
@@ -1822,6 +1827,9 @@ unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken)
18221827
Debugger.Break ();
18231828
#endif
18241829

1830+
if (detectedBareLinefeed)
1831+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInHeader, headerBlockBegin, headersBeginLineNumber);
1832+
18251833
headerBlockEnd = GetOffset (inputIndex);
18261834

18271835
OnHeadersEnd (headerBlockBegin, headersBeginLineNumber, headerBlockEnd, lineNumber, cancellationToken);
@@ -1841,6 +1849,9 @@ unsafe bool InnerSkipLine (byte* inbuf, bool consumeNewLine)
18411849
if (inptr < inend) {
18421850
inputIndex = (int) (inptr - inbuf);
18431851

1852+
if (input[inputIndex - 1] != (byte) '\r')
1853+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, GetOffset (inputIndex), lineNumber);
1854+
18441855
if (consumeNewLine) {
18451856
inputIndex++;
18461857
IncrementLineNumber (inputIndex);
@@ -2189,6 +2200,9 @@ unsafe int ConstructMimePart (byte* inbuf, CancellationToken cancellationToken)
21892200
var result = ScanContent (ScanContentType.MimeContent, inbuf, beginOffset, beginLineNumber, true, cancellationToken);
21902201
OnMimePartContentEnd (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, result.Format, cancellationToken);
21912202

2203+
if (result.Format != NewLineFormat.Dos)
2204+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
2205+
21922206
return result.Lines;
21932207
}
21942208

@@ -2280,6 +2294,9 @@ unsafe void MultipartScanPreamble (byte* inbuf, CancellationToken cancellationTo
22802294
OnMultipartPreambleBegin (beginOffset, beginLineNumber, cancellationToken);
22812295
var result = ScanContent (ScanContentType.MultipartPreamble, inbuf, beginOffset, beginLineNumber, false, cancellationToken);
22822296
OnMultipartPreambleEnd (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, cancellationToken);
2297+
2298+
if (result.Format != NewLineFormat.Dos)
2299+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
22832300
}
22842301

22852302
unsafe void MultipartScanEpilogue (byte* inbuf, CancellationToken cancellationToken)
@@ -2290,6 +2307,9 @@ unsafe void MultipartScanEpilogue (byte* inbuf, CancellationToken cancellationTo
22902307
OnMultipartEpilogueBegin (beginOffset, beginLineNumber, cancellationToken);
22912308
var result = ScanContent (ScanContentType.MultipartEpilogue, inbuf, beginOffset, beginLineNumber, true, cancellationToken);
22922309
OnMultipartEpilogueEnd (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, cancellationToken);
2310+
2311+
if (result.Format != NewLineFormat.Dos)
2312+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
22932313
}
22942314

22952315
unsafe void MultipartScanSubparts (ContentType multipartContentType, byte* inbuf, int depth, CancellationToken cancellationToken)

0 commit comments

Comments
 (0)