Skip to content

Commit

Permalink
detect bare linefeeds
Browse files Browse the repository at this point in the history
  • Loading branch information
jstedfast committed Dec 30, 2024
1 parent 6bf39ed commit 5e80668
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 30 deletions.
19 changes: 16 additions & 3 deletions MimeKit/AsyncMimeReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ async Task StepMboxMarkerAsync (CancellationToken cancellationToken)
async Task StepHeadersAsync (CancellationToken cancellationToken)
{
int headersBeginLineNumber = lineNumber;
bool detectedBareLinefeed = false;
var eof = false;

headerBlockBegin = GetOffset (inputIndex);
Expand Down Expand Up @@ -175,7 +176,7 @@ async Task StepHeadersAsync (CancellationToken cancellationToken)
}

// Check for an empty line denoting the end of the header block.
if (IsEndOfHeaderBlock (left)) {
if (IsEndOfHeaderBlock (left, ref detectedBareLinefeed)) {
state = MimeParserState.Content;
break;
}
Expand Down Expand Up @@ -272,12 +273,12 @@ async Task StepHeadersAsync (CancellationToken cancellationToken)
do {
unsafe {
fixed (byte* inbuf = input) {
if (StepHeaderValue (inbuf, ref midline))
if (StepHeaderValue (inbuf, ref midline, ref detectedBareLinefeed))
break;
}
}

if (await ReadAheadAsync (1, 0, cancellationToken).ConfigureAwait (false) == 0) {
if (await ReadAheadAsync (1, 1, cancellationToken).ConfigureAwait (false) == 0) {
if (midline)
OnComplianceIssueEncountered (MimeComplianceStatus.IncompleteHeader, beginOffset, beginLineNumber);
else
Expand All @@ -304,6 +305,9 @@ async Task StepHeadersAsync (CancellationToken cancellationToken)
Debugger.Break ();
#endif

if (detectedBareLinefeed)
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInHeader, headerBlockBegin, headersBeginLineNumber);

headerBlockEnd = GetOffset (inputIndex);

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

if (result.Format != NewLineFormat.Dos)
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);

return result.Lines;
}

Expand Down Expand Up @@ -510,6 +517,9 @@ async Task MultipartScanPreambleAsync (CancellationToken cancellationToken)
await OnMultipartPreambleBeginAsync (beginOffset, beginLineNumber, cancellationToken).ConfigureAwait (false);
var result = await ScanContentAsync (ScanContentType.MultipartPreamble, beginOffset, beginLineNumber, false, cancellationToken).ConfigureAwait (false);
await OnMultipartPreambleEndAsync (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, cancellationToken).ConfigureAwait (false);

if (result.Format != NewLineFormat.Dos)
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
}

async Task MultipartScanEpilogueAsync (CancellationToken cancellationToken)
Expand All @@ -520,6 +530,9 @@ async Task MultipartScanEpilogueAsync (CancellationToken cancellationToken)
await OnMultipartEpilogueBeginAsync (beginOffset, beginLineNumber, cancellationToken).ConfigureAwait (false);
var result = await ScanContentAsync (ScanContentType.MultipartEpilogue, beginOffset, beginLineNumber, true, cancellationToken).ConfigureAwait (false);
await OnMultipartEpilogueEndAsync (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, cancellationToken).ConfigureAwait (false);

if (result.Format != NewLineFormat.Dos)
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
}

async Task MultipartScanSubpartsAsync (ContentType multipartContentType, int depth, CancellationToken cancellationToken)
Expand Down
44 changes: 22 additions & 22 deletions MimeKit/MimeComplianceStatus.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,82 +41,82 @@ public enum MimeComplianceStatus {
/// </summary>
Compliant = 0,

/// <summary>
/// A line was found in a header that was linefeed terminated instead of carriage return &amp; linefeed terminated.
/// </summary>
BareLinefeedInHeader = 1 << 0,

/// <summary>
/// A line was found in a MIME part body content that was linefeed terminated instead of carriage return &amp; linefeed terminated.
/// </summary>
BareLinefeedInBody = 1 << 1,

/// <summary>
/// The header was not of the correct form.
/// </summary>
InvalidHeader = 1 << 0,
InvalidHeader = 1 << 2,

/// <summary>
/// The header ended prematurely at the end of the stream.
/// </summary>
IncompleteHeader = 1 << 1,
IncompleteHeader = 1 << 3,

/// <summary>
/// The Content-Transfer-Encoding header value was not valid.
/// </summary>
InvalidContentTransferEncoding = 1 << 2,
InvalidContentTransferEncoding = 1 << 4,

/// <summary>
/// The Content-Type header value was not valid.
/// </summary>
InvalidContentType = 1 << 3,
InvalidContentType = 1 << 5,

/// <summary>
/// The MIME-Version header value was not valid.
/// </summary>
InvalidMimeVersion = 1 << 4,
InvalidMimeVersion = 1 << 6,

/// <summary>
/// A line was found that was longer than the SMTP limit of 1000 characters.
/// </summary>
InvalidWrapping = 1 << 5,
InvalidWrapping = 1 << 7,

/// <summary>
/// An empty line separating the headers from the body was missing.
/// </summary>
MissingBodySeparator = 1 << 6,
MissingBodySeparator = 1 << 8,

/// <summary>
/// The MIME-Version header is missing.
/// </summary>
MissingMimeVersion = 1 << 7,
MissingMimeVersion = 1 << 9,

/// <summary>
/// The boundary parameter is missing from a multipart Content-Type header.
/// </summary>
MissingMultipartBoundaryParameter = 1 << 8,
MissingMultipartBoundaryParameter = 1 << 10,

/// <summary>
/// A multipart boundary was missing.
/// </summary>
MissingMultipartBoundary = 1 << 9,
MissingMultipartBoundary = 1 << 11,

/// <summary>
/// A MIME part contained multiple Content-Transfer-Encoding headers.
/// </summary>
DuplicateContentTransferEncoding = 1 << 10,
DuplicateContentTransferEncoding = 1 << 12,

/// <summary>
/// A MIME part contained multiple Content-Type headers.
/// </summary>
DuplicateContentType = 1 << 11,
DuplicateContentType = 1 << 13,

#if false
/// <summary>
/// A line was found in a MIME part body content that was linefeed terminated instead of carriage return &amp; linefeed terminated.
/// </summary>
BareLinefeedInBody,

/// <summary>
/// An external body was specified with invalid syntax.
/// </summary>
InvalidExternalBody,

/// <summary>
/// A line was found in a MIME part header that was linefeed terminated instead of carriage return &amp; linefeed terminated.
/// </summary>
BareLinefeedInHeader,

/// <summary>
/// Unexpected binary content was found in MIME part body content.
/// </summary>
Expand Down
30 changes: 25 additions & 5 deletions MimeKit/MimeReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1530,7 +1530,7 @@ void StepHeaderField (int headerFieldLength)
inputIndex += headerIndex;
}

unsafe bool StepHeaderValue (byte* inbuf, ref bool midline)
unsafe bool StepHeaderValue (byte* inbuf, ref bool midline, ref bool detectedBareLinefeed)
{
byte* inptr = inbuf + inputIndex;
byte* inend = inbuf + inputEnd;
Expand Down Expand Up @@ -1595,6 +1595,9 @@ unsafe bool StepHeaderValue (byte* inbuf, ref bool midline)
break;
}

if (index > 0 && *(inptr - 1) != (byte) '\r')
detectedBareLinefeed = true;

// Consume the newline and update our line number state.
inptr++;

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

bool IsEndOfHeaderBlock (int left)
bool IsEndOfHeaderBlock (int left, ref bool detectedBareLinefeed)
{
if (input[inputIndex] == (byte) '\n') {
state = MimeParserState.Content;
inputIndex++;
IncrementLineNumber (inputIndex);
detectedBareLinefeed = true;
return true;
}

Expand Down Expand Up @@ -1724,6 +1728,7 @@ Header CreateHeader (long beginOffset, int beginLineNumber, int fieldNameLength,
unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken)
{
int headersBeginLineNumber = lineNumber;
bool detectedBareLinefeed = false;
var eof = false;

headerBlockBegin = GetOffset (inputIndex);
Expand Down Expand Up @@ -1770,7 +1775,7 @@ unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken)
}

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

// Consume the header value.
while (!StepHeaderValue (inbuf, ref midline)) {
if (ReadAhead (1, 0, cancellationToken) == 0) {
while (!StepHeaderValue (inbuf, ref midline, ref detectedBareLinefeed)) {
if (ReadAhead (1, 1, cancellationToken) == 0) {
if (midline)
OnComplianceIssueEncountered (MimeComplianceStatus.IncompleteHeader, beginOffset, beginLineNumber);
else
Expand All @@ -1871,6 +1876,9 @@ unsafe void StepHeaders (byte* inbuf, CancellationToken cancellationToken)
Debugger.Break ();
#endif

if (detectedBareLinefeed)
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInHeader, headerBlockBegin, headersBeginLineNumber);

headerBlockEnd = GetOffset (inputIndex);

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

if (input[inputIndex - 1] != (byte) '\r')
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, GetOffset (inputIndex), lineNumber);

if (consumeNewLine) {
inputIndex++;
IncrementLineNumber (inputIndex);
Expand Down Expand Up @@ -2236,6 +2247,9 @@ unsafe int ConstructMimePart (byte* inbuf, CancellationToken cancellationToken)
var result = ScanContent (ScanContentType.MimeContent, inbuf, beginOffset, beginLineNumber, true, cancellationToken);
OnMimePartContentEnd (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, result.Format, cancellationToken);

if (result.Format != NewLineFormat.Dos)
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);

return result.Lines;
}

Expand Down Expand Up @@ -2326,6 +2340,9 @@ unsafe void MultipartScanPreamble (byte* inbuf, CancellationToken cancellationTo
OnMultipartPreambleBegin (beginOffset, beginLineNumber, cancellationToken);
var result = ScanContent (ScanContentType.MultipartPreamble, inbuf, beginOffset, beginLineNumber, false, cancellationToken);
OnMultipartPreambleEnd (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, cancellationToken);

if (result.Format != NewLineFormat.Dos)
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
}

unsafe void MultipartScanEpilogue (byte* inbuf, CancellationToken cancellationToken)
Expand All @@ -2336,6 +2353,9 @@ unsafe void MultipartScanEpilogue (byte* inbuf, CancellationToken cancellationTo
OnMultipartEpilogueBegin (beginOffset, beginLineNumber, cancellationToken);
var result = ScanContent (ScanContentType.MultipartEpilogue, inbuf, beginOffset, beginLineNumber, true, cancellationToken);
OnMultipartEpilogueEnd (beginOffset, beginLineNumber, beginOffset + result.ContentLength, result.Lines, cancellationToken);

if (result.Format != NewLineFormat.Dos)
OnComplianceIssueEncountered (MimeComplianceStatus.BareLinefeedInBody, beginOffset, beginLineNumber);
}

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

0 comments on commit 5e80668

Please sign in to comment.