Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,16 +131,31 @@ private static bool TryParse([NotNullWhen(true)] string? address, string? displa

displayNameEncoding ??= Encoding.GetEncoding(MimeBasePart.DefaultCharSet);
displayName ??= string.Empty;
bool userProvidedDisplayName = !string.IsNullOrEmpty(displayName);

// Check for bounding quotes
if (!string.IsNullOrEmpty(displayName))
if (userProvidedDisplayName)
{
if (!MailAddressParser.TryNormalizeOrThrow(displayName, out displayName, throwExceptionIfFail))
{
parsedData = default;
return false;
}

// Reject CR/LF in caller-supplied display names. Such characters would corrupt
// the encoded mail header when the address is serialized into a message
// (e.g. From/To headers written by SmtpClient).
if (MailBnfHelper.HasCROrLF(displayName))
{
if (throwExceptionIfFail)
{
throw new FormatException(SR.MailAddressInvalidFormat);
}

parsedData = default;
return false;
}

if (displayName.Length >= 2 && displayName.StartsWith('\"') && displayName.EndsWith('\"'))
{
// Peel bounding quotes, they'll get re-added later.
Expand All @@ -155,16 +170,37 @@ private static bool TryParse([NotNullWhen(true)] string? address, string? displa
}

// If we were not given a display name, use the one parsed from 'address'.
if (string.IsNullOrEmpty(displayName))
if (!userProvidedDisplayName)
{
displayName = info.DisplayName;

// The display name parsed out of 'address' may contain folding white space
// (CRLF inside a quoted string, RFC 5322 §3.2.4). Unfold by stripping any
// CR/LF characters so re-encoding into a header does not corrupt the output.
if (!string.IsNullOrEmpty(displayName) && MailBnfHelper.HasCROrLF(displayName))
{
displayName = StripCRLF(displayName);
}
}

parsedData = (displayName, info.User, info.Host, displayNameEncoding);

return true;
}

private static string StripCRLF(string value)
{
ValueStringBuilder sb = new ValueStringBuilder(stackalloc char[256]);
foreach (char c in value)
{
if (c != '\r' && c != '\n')
{
sb.Append(c);
}
}
return sb.ToString();
}

public string DisplayName
{
get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,21 @@ public void MailAddress_Ctor_FullString_Succeeds(string address, string displayN
Assert.Equal(expectedDisplayName ?? displayName, mailAddress.DisplayName);
Assert.Equal(expectedToString, mailAddress.ToString());
}

[Theory]
[InlineData("Display\rName")]
[InlineData("Display\nName")]
[InlineData("Display\r\nName")]
[InlineData("DisplayName\r")]
[InlineData("DisplayName\n")]
[InlineData("DisplayName\r\n")]
[InlineData("\rDisplayName")]
[InlineData("\nDisplayName")]
[InlineData("\"Display\r\nName\"")]
public void MailAddress_Ctor_DisplayNameContainsCRLF_Throws(string displayName)
{
Assert.Throws<FormatException>(() => new MailAddress(Address, displayName));
Assert.False(MailAddress.TryCreate(Address, displayName, out _));
}
}
}
Loading