-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Conversation
I'm a little confused. I thought we were putting System.Net.Mail.* into the System.Net.Mail contract and not the Mime contract? And I also thought we were merging System.Net.Mime contract back into System.Net.Mail? So, should we just assume, for now, that this entire contract implementation of System.Net.Mime (which now will include System.Net.Mail.* types) will be renamed to System.Net.Mail contract? |
Yes, the contract/assembly should be renamed to System.Net.Mail. @Priya91, can you do that as part of this change? |
// Parse a comma separated list of MailAddress's | ||
// | ||
// Throws a FormatException if any MailAddress is invalid. | ||
internal static IList<MailAddress> ParseMultipleAddresses(string data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I realize the desktop code returns IList<MailAddress>
, but it would be more efficient for callers to enumerate the result if this was changed to List<MailAddress>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
@@ -283,7 +302,7 @@ private static string ParseDisplayName(string data, ref int index, bool expectMu | |||
|
|||
// Do not include the Comma (if any), and because there were no bounding quotes, | |||
// trim extra whitespace. | |||
displayName = data.SubstringTrim(index + 1, startingIndex - index); | |||
displayName = data.Substring(index + 1, startingIndex - index).Trim(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was there a reason this changed? SubstringTrim
will only allocate one string instead of two strings for the separate Substring
and Trim
operations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
@@ -4,6 +4,7 @@ | |||
|
|||
using System.Diagnostics; | |||
using System.Net.Mime; | |||
using System.Collections.Generic; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: "Remove and Sort" usings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
protected DelegatedStream(Stream stream) | ||
{ | ||
if (stream == null) | ||
throw new ArgumentNullException("stream"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: nameof(stream)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
{ | ||
if (disposed) | ||
{ | ||
throw new ObjectDisposedException(this.GetType().FullName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: the this.
isn't necessary and can be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
{ | ||
if (_disposed) | ||
{ | ||
throw new ObjectDisposedException(this.GetType().FullName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: the this.
is unnecessary and can be removed. Applies throughout (I'll stop commenting on it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
|
||
if (item == null) | ||
{ | ||
throw new ArgumentNullException("item"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: nameof(item)
. Applies throughout (I'll stop commenting on it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
return; | ||
} | ||
|
||
foreach (AlternateView view in this) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessarily for this PR, but a follow-up improvement would be to explicitly pass a new List<AlternateView>
to the base Collection<T>
constructor from this type's constructor , and then cast Items
to List<AlternateView>
here to enumerate the collection more efficiently (avoiding the enumerator allocation and overhead).
internal static string ShortNameFromFile(string fileName) | ||
{ | ||
string name; | ||
int start = fileName.LastIndexOfAny(new char[] { '\\', ':' }, fileName.Length - 1, fileName.Length); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Cache the char[]
in a readonly static
field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
} | ||
else | ||
{ | ||
if (value.IndexOfAny(new char[] { '<', '>' }) != -1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Cache the char[]
in a static readonly
field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
{ | ||
get | ||
{ | ||
return String.Format(CultureInfo.InvariantCulture, "{0}@{1}", _userName, _host); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: return _userName + "@" + _host;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any specific reason?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Concatenating 3 strings is more efficient than going through string.Format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
private string GetAddress(bool allowUnicode) | ||
{ | ||
return String.Format(CultureInfo.InvariantCulture, "{0}@{1}", | ||
GetUser(allowUnicode), GetHost(allowUnicode)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: return GetUser(allowUnicode) + "@" + GetHost(allowUnicode);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
{ | ||
get | ||
{ | ||
return String.Format(CultureInfo.InvariantCulture, "<{0}>", Address); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: return "<" + Address + ">";
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
|
||
internal string GetSmtpAddress(bool allowUnicode) | ||
{ | ||
return String.Format(CultureInfo.InvariantCulture, "<{0}>", GetAddress(allowUnicode)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: return "<" + GetAddress(allowUnicode) + ">";
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
/// <returns></returns> | ||
public override string ToString() | ||
{ | ||
if (String.IsNullOrEmpty(DisplayName)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: String
-> string
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
{ | ||
if (String.IsNullOrEmpty(DisplayName)) | ||
{ | ||
return this.Address; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this.
isn't necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
} | ||
else | ||
{ | ||
return String.Format("\"{0}\" {1}", DisplayName, SmtpAddress); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: return "\"" + DisplayName + "\" " + SmtpAddress;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
{ | ||
return false; | ||
} | ||
return ToString().Equals(value.ToString(), StringComparison.InvariantCultureIgnoreCase); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OrdinalIgnoreCase
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return ToString().GetHashCode(); | ||
} | ||
|
||
private static EncodedStreamFactory s_encoderFactory = new EncodedStreamFactory(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: readonly
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
//be appended. | ||
if (MimeBasePart.IsAscii(_displayName, false) || allowUnicode) | ||
{ | ||
encodedAddress = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", _displayName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: `encodedAddress = """ + _displayName + """;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would probably be better to be smarter about quoting the _displayName
string so as to avoid situations where the _displayName string contains a double quote character as well as properly escaping backslash characters.
See https://github.com/jstedfast/MimeKit/blob/master/MimeKit/Utils/MimeUtils.cs#L562 for how this should be done to comply with the specifications.
first = false; | ||
} | ||
|
||
return builder.ToString(); ; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: extra semicolon
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
{ | ||
for (int i = 0; i < Headers.Count; i++) | ||
{ | ||
if (string.Compare(Headers.GetKey(i), headerName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: string.Equals
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
for (int i = 0; i < Headers.Count; i++) | ||
{ | ||
if (string.Compare(Headers.GetKey(i), headerName, | ||
StringComparison.InvariantCultureIgnoreCase) == 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OrdinalIgnoreCase
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as before.
{ | ||
internal static class SmtpAuthenticationManager | ||
{ | ||
private static ArrayList s_modules = new ArrayList(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
List<ISmtpAuthenticationModule>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
using System; | ||
using System.IO; | ||
using System.Collections.Specialized; | ||
using System.Net.Mime; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: "Remove and Sort". Applies throughout (I'll stop commenting on it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolved.
@Priya91 Regarding the changes from Desktop implementation especially the un-implemented functionality (authentication) and missing tests, please open up separate issues to track this work (which might be done by others). |
Test OuterLoop CentOS7.1 Debug Build and Test |
Yes will do. I am currently working on tests, and will have another PR for it. |
@@ -199,6 +199,24 @@ public static void AssertFormat(string messageFormat, params object[] data) | |||
} | |||
} | |||
|
|||
public static void AssertFormat(bool condition, string messageFormat, params object[] data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method should be renamed to just Assert
. It's ok and expected to have multiple overloads of a method where only the number/type of parameters vary. I.e. Console.WriteLine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went along with the naming of already existing methods in this file. The Assert methods on this file didn't have formatting syntax, and there was another method AssertFormat that took in params array to allow formatting, hence added overload to already existing AssertFormat, to include a condition.
namespace System.Net | ||
{ | ||
[EventSource(Name = "Microsoft-System-Net-Mail", LocalizationResources = "FxResources.System.Net.Mail.SR")] | ||
internal sealed class WebEventSource : EventSource |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is the class named WebEventSource
? Smtp isn't doing "web" things like HTTP. Wouldn't it be better to be called something like MailEventSource
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SmtpClient uses a TraceSource named as Web on Desktop, kept the same naming scheme from Desktop code here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rename as "EmailEventSource". Also, please add a GUID and update the documentation at https://github.com/dotnet/corefx/blob/master/Documentation/debugging/windows-instructions.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding proper ETW tracing! 👍
{ | ||
base.Close(); | ||
|
||
if (_sslStream != null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: pls use braces around the content of the if
as per CoreFx coding guidlines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will address the nit in a separate PR, as triggering outerloop CI again for this change does not add value.
<None Include="project.json" /> | ||
</ItemGroup> | ||
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" /> | ||
</Project> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: missing newline at end of file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will address the nit in a separate PR, as triggering outerloop CI again for this change does not add value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file doesn't exist, System.Net.Mail.csproj is the current.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a few comments. Otherwise, LGTM.
Thanks for the review @davidsh |
Centos run failed with
|
@dotnet-bot test OuterLoop CentOS7.1 Debug |
filed issues #12526 #12529 #12535 #12536 #12537 #12538 on pending work items for .NET Core. |
Centos build failed with, not related to this change
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Priya91 PTAL
|
||
if (GlobalLog.IsEnabled && recipients.Count > 0) | ||
{ | ||
GlobalLog.Assert("SmtpTransport::BeginSendMail()|recepients.Count <= 0"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/cc @stephentoub
Assert used to crash the process before. This is only logging silently.
Here any everywhere GlobalLog.Assert* is used, please follow the pattern used in other System.Net.* contracts and add Debug.Fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@CIPop Can you file an issue on corefx with details, i don't follow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Priya91 The code as written will not assert (crash the process when DEBUG is used).
#12674
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in #12749
namespace System.Net | ||
{ | ||
[EventSource(Name = "Microsoft-System-Net-Mail", LocalizationResources = "FxResources.System.Net.Mail.SR")] | ||
internal sealed class WebEventSource : EventSource |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please rename as "EmailEventSource". Also, please add a GUID and update the documentation at https://github.com/dotnet/corefx/blob/master/Documentation/debugging/windows-instructions.md
namespace System.Net | ||
{ | ||
[EventSource(Name = "Microsoft-System-Net-Mail", LocalizationResources = "FxResources.System.Net.Mail.SR")] | ||
internal sealed class WebEventSource : EventSource |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adding proper ETW tracing! 👍
@@ -9,6 +9,7 @@ | |||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard1.7_Release|AnyCPU'" /> | |||
<ItemGroup> | |||
<Compile Include="System.Net.Mime.cs" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is uncommon: we usually have a single .cs file. I actually prefer this but didn't know it's an option.
@ericstj, @weshaggard would it be safer to merge (do you foresee any tooling issues)?
@@ -79,10 +81,8 @@ internal void Append(string value, int offset, int count) | |||
for (int i = 0; i < count; i++) | |||
{ | |||
char c = value[offset + i]; | |||
if (c > 0xFF) | |||
{ | |||
if ((ushort)c > 0xFF) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add the braces back.
|
||
namespace System.Net.Mail | ||
{ | ||
[Serializable] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@karelz @weshaggard
/cc @davidsh
Are we adding [Serializable] back in netstandard? If yes, we need to track changes for other already-ported types.
Same question about CAS.
{ | ||
} | ||
|
||
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this isn't necessary anymore. If it is, please change the Justification text at System.dll
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it is
How do i verify that ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The build should fail if removed. Either way, the Justification
string is incorrect.
GetObjectData(serializationInfo, streamingContext); | ||
} | ||
|
||
[SuppressMessage("Microsoft.Security", "CA2123:OverrideLinkDemandsShouldBeIdenticalToBase", Justification = "System.dll is still using pre-v4 security model and needs this demand")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above.
InvokeCallback(); | ||
return false; | ||
} | ||
else if ((int)info.StatusCode != 334) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: add 334 to SmtpStatusCode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SmtpStatusCode is a public enum, adding to it is a new API change. Out of scope of this PR and netstandard2.0
try | ||
{ | ||
LineInfo info = AuthCommand.EndSend(result); | ||
if ((int)info.StatusCode == 235) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Add 235 to SmtpStatusCode.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SmtpStatusCode is a public enum, adding to it is a new API change. Out of scope of this PR and netstandard2.0
Hi Regards |
Please do not comment on long-closed issues or PRs. We are not monitoring them. |
Initial commit to System.Net.Mail. Commit migrated from dotnet/corefx@abc007c
Changes from Desktop implementation:
dependency on internal NTAuthentication type. Authentication currently happens uses plain text login, SSL connections need to be used.
web.config
files is not supported, as netstandard2.0 doesn't expose theSystem.Net.Configuration
apis.NotSupportedException
. Desktop uses COM types for this API, investigation on partially supporting this feature on Windows is pending.Notes:
SmtpConnection.cs
andTlsStream.cs
files for correctness, they have changed functionally from Desktop code.Connect()
,BeginConnect()
andEndConnect()
in .NET Core contract yet, once that's available, the SendAsync should use the async connect methods. - Update: Added nowDid a test locally using the
SmtpClient.Send()
andSmtpClient.SendAsync()
, was able to successfully send emails to google smpt server with tls authentication.cc @stephentoub @ericeil @CIPop @davidsh @geoffkizer @tijoytom @karelz