Skip to content

Commit 5e80668

Browse files
committed
detect bare linefeeds
1 parent 6bf39ed commit 5e80668

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
@@ -1530,7 +1530,7 @@ void StepHeaderField (int headerFieldLength)
15301530
inputIndex += headerIndex;
15311531
}
15321532

1533-
unsafe bool StepHeaderValue (byte* inbuf, ref bool midline)
1533+
unsafe bool StepHeaderValue (byte* inbuf, ref bool midline, ref bool detectedBareLinefeed)
15341534
{
15351535
byte* inptr = inbuf + inputIndex;
15361536
byte* inend = inbuf + inputEnd;
@@ -1595,6 +1595,9 @@ unsafe bool StepHeaderValue (byte* inbuf, ref bool midline)
15951595
break;
15961596
}
15971597

1598+
if (index > 0 && *(inptr - 1) != (byte) '\r')
1599+
detectedBareLinefeed = true;
1600+
15981601
// Consume the newline and update our line number state.
15991602
inptr++;
16001603

@@ -1619,12 +1622,13 @@ unsafe bool StepHeaderValue (byte* inbuf, ref bool midline)
16191622
return inptr < inend;
16201623
}
16211624

1622-
bool IsEndOfHeaderBlock (int left)
1625+
bool IsEndOfHeaderBlock (int left, ref bool detectedBareLinefeed)
16231626
{
16241627
if (input[inputIndex] == (byte) '\n') {
16251628
state = MimeParserState.Content;
16261629
inputIndex++;
16271630
IncrementLineNumber (inputIndex);
1631+
detectedBareLinefeed = true;
16281632
return true;
16291633
}
16301634

@@ -1724,6 +1728,7 @@ Header CreateHeader (long beginOffset, int beginLineNumber, int fieldNameLength,
17241728
unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken)
17251729
{
17261730
int headersBeginLineNumber = lineNumber;
1731+
bool detectedBareLinefeed = false;
17271732
var eof = false;
17281733

17291734
headerBlockBegin = GetOffset (inputIndex);
@@ -1770,7 +1775,7 @@ unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken)
17701775
}
17711776

17721777
// Check for an empty line denoting the end of the header block.
1773-
if (IsEndOfHeaderBlock (left)) {
1778+
if (IsEndOfHeaderBlock (left, ref detectedBareLinefeed)) {
17741779
state = MimeParserState.Content;
17751780
break;
17761781
}
@@ -1843,8 +1848,8 @@ unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken)
18431848
bool midline = true;
18441849

18451850
// Consume the header value.
1846-
while (!StepHeaderValue (inbuf, ref midline)) {
1847-
if (ReadAhead (1, 0, cancellationToken) == 0) {
1851+
while (!StepHeaderValue (inbuf, ref midline, ref detectedBareLinefeed)) {
1852+
if (ReadAhead (1, 1, cancellationToken) == 0) {
18481853
if (midline)
18491854
OnComplianceIssueEncountered (MimeComplianceStatus.IncompleteHeader, beginOffset, beginLineNumber);
18501855
else
@@ -1871,6 +1876,9 @@ unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken)
18711876
Debugger.Break ();
18721877
#endif
18731878

1879+
if (detectedBareLinefeed)
1880+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInHeader, headerBlockBegin, headersBeginLineNumber);
1881+
18741882
headerBlockEnd = GetOffset (inputIndex);
18751883

18761884
OnHeadersEnd (headerBlockBegin, headersBeginLineNumber, headerBlockEnd, lineNumber, cancellationToken);
@@ -1889,6 +1897,9 @@ unsafe bool InnerSkipLine (byte* inbuf, bool consumeNewLine)
18891897
if (inptr < inend) {
18901898
inputIndex = (int) (inptr - inbuf);
18911899

1900+
if (input[inputIndex - 1] != (byte) '\r')
1901+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, GetOffset (inputIndex), lineNumber);
1902+
18921903
if (consumeNewLine) {
18931904
inputIndex++;
18941905
IncrementLineNumber (inputIndex);
@@ -2236,6 +2247,9 @@ unsafe int ConstructMimePart (byte* inbuf, CancellationToken cancellationToken)
22362247
var result = ScanContent (ScanContentType.MimeContent, inbuf, beginOffset, beginLineNumber, true, cancellationToken);
22372248
OnMimePartContentEnd (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, result.Format, cancellationToken);
22382249

2250+
if (result.Format != NewLineFormat.Dos)
2251+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
2252+
22392253
return result.Lines;
22402254
}
22412255

@@ -2326,6 +2340,9 @@ unsafe void MultipartScanPreamble (byte* inbuf, CancellationToken cancellationTo
23262340
OnMultipartPreambleBegin (beginOffset, beginLineNumber, cancellationToken);
23272341
var result = ScanContent (ScanContentType.MultipartPreamble, inbuf, beginOffset, beginLineNumber, false, cancellationToken);
23282342
OnMultipartPreambleEnd (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, cancellationToken);
2343+
2344+
if (result.Format != NewLineFormat.Dos)
2345+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
23292346
}
23302347

23312348
unsafe void MultipartScanEpilogue (byte* inbuf, CancellationToken cancellationToken)
@@ -2336,6 +2353,9 @@ unsafe void MultipartScanEpilogue (byte* inbuf, CancellationToken cancellationTo
23362353
OnMultipartEpilogueBegin (beginOffset, beginLineNumber, cancellationToken);
23372354
var result = ScanContent (ScanContentType.MultipartEpilogue, inbuf, beginOffset, beginLineNumber, true, cancellationToken);
23382355
OnMultipartEpilogueEnd (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, cancellationToken);
2356+
2357+
if (result.Format != NewLineFormat.Dos)
2358+
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
23392359
}
23402360

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

0 commit comments

Comments
 (0)