diff --git a/src/libraries/System.Net.Mail/ref/System.Net.Mail.cs b/src/libraries/System.Net.Mail/ref/System.Net.Mail.cs index d1b280aec9489..b3c1f32996dca 100644 --- a/src/libraries/System.Net.Mail/ref/System.Net.Mail.cs +++ b/src/libraries/System.Net.Mail/ref/System.Net.Mail.cs @@ -186,6 +186,8 @@ public void SendAsync(string from, string recipients, string subject, string bod public void SendAsyncCancel() { } public System.Threading.Tasks.Task SendMailAsync(System.Net.Mail.MailMessage message) { throw null; } public System.Threading.Tasks.Task SendMailAsync(string from, string recipients, string subject, string body) { throw null; } + public System.Threading.Tasks.Task SendMailAsync(System.Net.Mail.MailMessage message, System.Threading.CancellationToken cancellationToken) { throw null; } + public System.Threading.Tasks.Task SendMailAsync(string from, string recipients, string subject, string body, System.Threading.CancellationToken cancellationToken) { throw null; } } public enum SmtpDeliveryFormat { diff --git a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs index 01bb0a44e7355..077bfeb57175f 100644 --- a/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs +++ b/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpClient.cs @@ -782,17 +782,59 @@ public void SendAsyncCancel() public Task SendMailAsync(string from, string recipients, string subject, string body) { var message = new MailMessage(from, recipients, subject, body); - return SendMailAsync(message); + return SendMailAsync(message, cancellationToken: default); } public Task SendMailAsync(MailMessage message) { + return SendMailAsync(message, cancellationToken: default); + } + + public Task SendMailAsync(string from, string recipients, string subject, string body, CancellationToken cancellationToken) + { + var message = new MailMessage(from, recipients, subject, body); + return SendMailAsync(message, cancellationToken); + } + + public Task SendMailAsync(MailMessage message, CancellationToken cancellationToken) + { + if (cancellationToken.IsCancellationRequested) + { + return Task.FromCanceled(cancellationToken); + } + // Create a TaskCompletionSource to represent the operation var tcs = new TaskCompletionSource(); + CancellationTokenRegistration ctr = default; + + // Indicates whether the CTR has been set - captured in handler + int state = 0; + // Register a handler that will transfer completion results to the TCS Task SendCompletedEventHandler handler = null; - handler = (sender, e) => HandleCompletion(tcs, e, handler); + handler = (sender, e) => + { + if (e.UserState == tcs) + { + try + { + ((SmtpClient)sender).SendCompleted -= handler; + if (Interlocked.Exchange(ref state, 1) != 0) + { + // A CTR has been set, we have to wait until it completes before completing the task + ctr.Dispose(); + } + } + catch (ObjectDisposedException) { } // SendAsyncCancel will throw if SmtpClient was disposed + finally + { + if (e.Error != null) tcs.TrySetException(e.Error); + else if (e.Cancelled) tcs.TrySetCanceled(); + else tcs.TrySetResult(null); + } + } + }; SendCompleted += handler; // Start the async operation. @@ -806,22 +848,19 @@ public Task SendMailAsync(MailMessage message) throw; } - // Return the task to represent the asynchronous operation - return tcs.Task; - } + ctr = cancellationToken.Register(s => + { + ((SmtpClient)s).SendAsyncCancel(); + }, this); - private void HandleCompletion(TaskCompletionSource tcs, AsyncCompletedEventArgs e, SendCompletedEventHandler handler) - { - if (e.UserState == tcs) + if (Interlocked.Exchange(ref state, 1) != 0) { - try { SendCompleted -= handler; } - finally - { - if (e.Error != null) tcs.TrySetException(e.Error); - else if (e.Cancelled) tcs.TrySetCanceled(); - else tcs.TrySetResult(null); - } + // SendCompleted was already invoked, ensure the CTR completes before returning the task + ctr.Dispose(); } + + // Return the task to represent the asynchronous operation + return tcs.Task; } diff --git a/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs b/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs new file mode 100644 index 0000000000000..8db9e8c5e108a --- /dev/null +++ b/src/libraries/System.Net.Mail/tests/Functional/LoopbackSmtpServer.cs @@ -0,0 +1,265 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Net; +using System.Net.Mail; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Systen.Net.Mail.Tests +{ + public class LoopbackSmtpServer : IDisposable + { + private static readonly ReadOnlyMemory s_messageTerminator = new byte[] { (byte)'\r', (byte)'\n' }; + private static readonly ReadOnlyMemory s_bodyTerminator = new byte[] { (byte)'\r', (byte)'\n', (byte)'.', (byte)'\r', (byte)'\n' }; + + public bool ReceiveMultipleConnections = false; + public bool SupportSmtpUTF8 = false; + public bool AdvertiseNtlmAuthSupport = false; + + private bool _disposed = false; + private readonly Socket _listenSocket; + private readonly ConcurrentBag _socketsToDispose; + private long _messageCounter = new Random().Next(1000, 2000); + + public readonly int Port; + public SmtpClient CreateClient() => new SmtpClient("localhost", Port); + + public Action OnConnected; + public Action OnHelloReceived; + public Action OnCommandReceived; + public Action OnUnknownCommand; + public Action OnQuitReceived; + + public string ClientDomain { get; private set; } + public string MailFrom { get; private set; } + public string MailTo { get; private set; } + public string UsernamePassword { get; private set; } + public string Username { get; private set; } + public string Password { get; private set; } + public string AuthMethodUsed { get; private set; } + public ParsedMailMessage Message { get; private set; } + + public int ConnectionCount { get; private set; } + public int MessagesReceived { get; private set; } + + public LoopbackSmtpServer() + { + _socketsToDispose = new ConcurrentBag(); + _listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + _socketsToDispose.Add(_listenSocket); + + _listenSocket.Bind(new IPEndPoint(IPAddress.Any, 0)); + Port = ((IPEndPoint)_listenSocket.LocalEndPoint).Port; + _listenSocket.Listen(1); + + _ = Task.Run(async () => + { + do + { + var socket = await _listenSocket.AcceptAsync(); + _socketsToDispose.Add(socket); + ConnectionCount++; + _ = Task.Run(async () => await HandleConnectionAsync(socket)); + } + while (ReceiveMultipleConnections); + }); + } + + private async Task HandleConnectionAsync(Socket socket) + { + var buffer = new byte[1024].AsMemory(); + + async ValueTask ReceiveMessageAsync(bool isBody = false) + { + var terminator = isBody ? s_bodyTerminator : s_messageTerminator; + int suffix = terminator.Length; + + int received = 0; + do + { + int read = await socket.ReceiveAsync(buffer.Slice(received), SocketFlags.None); + if (read == 0) return null; + received += read; + } + while (received < suffix || !buffer.Slice(received - suffix, suffix).Span.SequenceEqual(terminator.Span)); + + MessagesReceived++; + return Encoding.UTF8.GetString(buffer.Span.Slice(0, received - suffix)); + } + async ValueTask SendMessageAsync(string text) + { + var bytes = buffer.Slice(0, Encoding.UTF8.GetBytes(text, buffer.Span) + 2); + bytes.Span[^2] = (byte)'\r'; + bytes.Span[^1] = (byte)'\n'; + await socket.SendAsync(bytes, SocketFlags.None); + } + + try + { + OnConnected?.Invoke(socket); + await SendMessageAsync("220 localhost"); + + string message = await ReceiveMessageAsync(); + Debug.Assert(message.ToLower().StartsWith("helo ") || message.ToLower().StartsWith("ehlo ")); + ClientDomain = message.Substring(5).ToLower(); + OnCommandReceived?.Invoke(message.Substring(0, 4), ClientDomain); + OnHelloReceived?.Invoke(ClientDomain); + + await SendMessageAsync("250-localhost, mock server here"); + if (SupportSmtpUTF8) await SendMessageAsync("250-SMTPUTF8"); + await SendMessageAsync("250 AUTH PLAIN LOGIN" + (AdvertiseNtlmAuthSupport ? " NTLM" : "")); + + while ((message = await ReceiveMessageAsync()) != null) + { + int colonIndex = message.IndexOf(':'); + string command = colonIndex == -1 ? message : message.Substring(0, colonIndex); + string argument = command.Length == message.Length ? string.Empty : message.Substring(colonIndex + 1).Trim(); + + OnCommandReceived?.Invoke(command, argument); + + if (command.StartsWith("AUTH", StringComparison.OrdinalIgnoreCase)) + { + var parts = command.Split(' '); + Debug.Assert(parts.Length > 1, "Expected an actual auth request"); + + AuthMethodUsed = parts[1]; + + if (parts[1].Equals("LOGIN", StringComparison.OrdinalIgnoreCase)) + { + if (parts.Length == 2) + { + await SendMessageAsync("334 VXNlcm5hbWU6"); + Username = Encoding.UTF8.GetString(Convert.FromBase64String(await ReceiveMessageAsync())); + } + else + { + Username = Encoding.UTF8.GetString(Convert.FromBase64String(parts[2])); + } + await SendMessageAsync("334 UGFzc3dvcmQ6"); + Password = Encoding.UTF8.GetString(Convert.FromBase64String(await ReceiveMessageAsync())); + UsernamePassword = Username + Password; + await SendMessageAsync("235 Authentication successful"); + } + else if (parts[1].Equals("NTLM", StringComparison.OrdinalIgnoreCase)) + { + await SendMessageAsync("12345 I lied, I can't speak NTLM - here's an invalid response"); + } + else await SendMessageAsync("504 scheme not supported"); + continue; + } + + switch (command.ToUpper()) + { + case "MAIL FROM": + MailFrom = argument; + await SendMessageAsync("250 Ok"); + break; + + case "RCPT TO": + MailTo = argument; + await SendMessageAsync("250 Ok"); + break; + + case "DATA": + await SendMessageAsync("354 Start mail input; end with ."); + string data = await ReceiveMessageAsync(true); + Message = ParsedMailMessage.Parse(data); + await SendMessageAsync("250 Ok: queued as " + Interlocked.Increment(ref _messageCounter)); + break; + + case "QUIT": + OnQuitReceived?.Invoke(socket); + await SendMessageAsync("221 Bye"); + return; + + default: + OnUnknownCommand?.Invoke(message); + await SendMessageAsync("500 Idk that command"); + break; + } + } + } + catch { } + finally + { + try + { + socket.Shutdown(SocketShutdown.Both); + } + finally + { + socket?.Close(); + } + } + } + + public void Dispose() + { + if (!_disposed) + { + _disposed = true; + foreach (var socket in _socketsToDispose) + { + try + { + socket.Close(); + } + catch { } + } + _socketsToDispose.Clear(); + } + } + + + public class ParsedMailMessage + { + public readonly IReadOnlyDictionary Headers; + public readonly string Body; + + private string GetHeader(string name) => Headers.TryGetValue(name, out string value) ? value : "NOT-PRESENT"; + public string From => GetHeader("From"); + public string To => GetHeader("To"); + public string Subject => GetHeader("Subject"); + + private ParsedMailMessage(Dictionary headers, string body) + { + Headers = headers; + Body = body; + } + + public static ParsedMailMessage Parse(string data) + { + Dictionary headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + + ReadOnlySpan dataSpan = data; + string body = null; + + while (!dataSpan.IsEmpty) + { + int endOfLine = dataSpan.IndexOf('\n'); + Debug.Assert(endOfLine != -1, "Expected valid \r\n terminated lines"); + var line = dataSpan.Slice(0, endOfLine).TrimEnd('\r'); + + if (line.IsEmpty) + { + body = dataSpan.Slice(endOfLine + 1).TrimEnd(stackalloc char[] { '\r', '\n' }).ToString(); + break; + } + else + { + int colon = line.IndexOf(':'); + Debug.Assert(colon != -1, "Expected a valid header"); + headers.Add(line.Slice(0, colon).Trim().ToString(), line.Slice(colon + 1).Trim().ToString()); + dataSpan = dataSpan.Slice(endOfLine + 1); + } + } + + return new ParsedMailMessage(headers, body); + } + } + } +} diff --git a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs index 2c8bfdfe73dc8..01558afbfac0d 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs +++ b/src/libraries/System.Net.Mail/tests/Functional/SmtpClientTest.cs @@ -14,6 +14,7 @@ using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; +using Systen.Net.Mail.Tests; using Xunit; namespace System.Net.Mail.Tests @@ -290,29 +291,21 @@ public async Task SendAsync_ServerDoesntExist_Throws() [Fact] public void TestMailDelivery() { - SmtpServer server = new SmtpServer(); - SmtpClient client = new SmtpClient("localhost", server.EndPoint.Port); - client.Credentials = new NetworkCredential("user", "password"); + using var server = new LoopbackSmtpServer(); + using SmtpClient client = server.CreateClient(); + client.Credentials = new NetworkCredential("Foo", "Bar"); MailMessage msg = new MailMessage("foo@example.com", "bar@example.com", "hello", "howdydoo"); - string clientDomain = IPGlobalProperties.GetIPGlobalProperties().HostName.Trim().ToLower(); - try - { - Thread t = new Thread(server.Run); - t.Start(); - client.Send(msg); - t.Join(); + client.Send(msg); - Assert.Equal("", server.MailFrom); - Assert.Equal("", server.MailTo); - Assert.Equal("hello", server.Subject); - Assert.Equal("howdydoo", server.Body); - Assert.Equal(clientDomain, server.ClientDomain); - } - finally - { - server.Stop(); - } + Assert.Equal("", server.MailFrom); + Assert.Equal("", server.MailTo); + Assert.Equal("hello", server.Message.Subject); + Assert.Equal("howdydoo", server.Message.Body); + Assert.Equal(GetClientDomain(), server.ClientDomain); + Assert.Equal("Foo", server.Username); + Assert.Equal("Bar", server.Password); + Assert.Equal("LOGIN", server.AuthMethodUsed, StringComparer.OrdinalIgnoreCase); } [Fact] @@ -349,60 +342,37 @@ public void TestZeroTimeout() [InlineData(null)] public async Task TestMailDeliveryAsync(string body) { - SmtpServer server = new SmtpServer(); - SmtpClient client = new SmtpClient("localhost", server.EndPoint.Port); + using var server = new LoopbackSmtpServer(); + using SmtpClient client = server.CreateClient(); MailMessage msg = new MailMessage("foo@example.com", "bar@example.com", "hello", body); - string clientDomain = IPGlobalProperties.GetIPGlobalProperties().HostName.Trim().ToLower(); - try - { - Thread t = new Thread(server.Run); - t.Start(); - await client.SendMailAsync(msg).TimeoutAfter((int)TimeSpan.FromSeconds(30).TotalMilliseconds); - t.Join(); - - Assert.Equal("", server.MailFrom); - Assert.Equal("", server.MailTo); - Assert.Equal("hello", server.Subject); - Assert.Equal(body ?? "", server.Body); - Assert.Equal(clientDomain, server.ClientDomain); - } - finally - { - server.Stop(); - } + await client.SendMailAsync(msg).TimeoutAfter((int)TimeSpan.FromSeconds(30).TotalMilliseconds); + + Assert.Equal("", server.MailFrom); + Assert.Equal("", server.MailTo); + Assert.Equal("hello", server.Message.Subject); + Assert.Equal(body ?? "", server.Message.Body); + Assert.Equal(GetClientDomain(), server.ClientDomain); } [Fact] + [PlatformSpecific(TestPlatforms.Windows)] // NTLM support required, see https://github.com/dotnet/corefx/issues/28961 public async Task TestCredentialsCopyInAsyncContext() { - SmtpServer server = new SmtpServer(); - SmtpClient client = new SmtpClient("localhost", server.EndPoint.Port); + using var server = new LoopbackSmtpServer(); + using SmtpClient client = server.CreateClient(); MailMessage msg = new MailMessage("foo@example.com", "bar@example.com", "hello", "howdydoo"); - string clientDomain = IPGlobalProperties.GetIPGlobalProperties().HostName.Trim().ToLower(); CredentialCache cache = new CredentialCache(); - cache.Add("localhost", server.EndPoint.Port, "NTLM", CredentialCache.DefaultNetworkCredentials); + cache.Add("localhost", server.Port, "NTLM", CredentialCache.DefaultNetworkCredentials); client.Credentials = cache; - try - { - Thread t = new Thread(server.Run); - t.Start(); - await client.SendMailAsync(msg); - t.Join(); - - Assert.Equal("", server.MailFrom); - Assert.Equal("", server.MailTo); - Assert.Equal("hello", server.Subject); - Assert.Equal("howdydoo", server.Body); - Assert.Equal(clientDomain, server.ClientDomain); - } - finally - { - server.Stop(); - } + // The mock server doesn't actually understand NTLM, but still advertises support for it + server.AdvertiseNtlmAuthSupport = true; + await Assert.ThrowsAsync(async () => await client.SendMailAsync(msg)); + + Assert.Equal("NTLM", server.AuthMethodUsed, StringComparer.OrdinalIgnoreCase); } @@ -423,13 +393,12 @@ public void SendMail_DeliveryFormat_SubjectEncoded(bool useAsyncSend, bool useSe // If the server does not support `SMTPUTF8` or use `SmtpDeliveryFormat.SevenBit`, the server should received this subject. const string subjectBase64 = "=?utf-8?B?VGVzdCDmtYvor5UgQ29udGFpbiDljIXlkKsgVVRGOA==?="; - SmtpServer server = new SmtpServer(); + using var server = new LoopbackSmtpServer(); + using SmtpClient client = server.CreateClient(); // Setting up Server Support for `SMTPUTF8`. server.SupportSmtpUTF8 = useSmtpUTF8; - SmtpClient client = new SmtpClient("localhost", server.EndPoint.Port); - if (useSevenBit) { // Subject will be encoded by Base64. @@ -444,33 +413,76 @@ public void SendMail_DeliveryFormat_SubjectEncoded(bool useAsyncSend, bool useSe MailMessage msg = new MailMessage("foo@example.com", "bar@example.com", subjectText, "hello \u9ad8\u575a\u679c"); msg.HeadersEncoding = msg.BodyEncoding = msg.SubjectEncoding = System.Text.Encoding.UTF8; - try + if (useAsyncSend) { - Thread t = new Thread(server.Run); - t.Start(); - - if (useAsyncSend) - { - client.SendMailAsync(msg).Wait(); - } - else - { - client.Send(msg); - } + client.SendMailAsync(msg).Wait(); + } + else + { + client.Send(msg); + } - if (useSevenBit || !useSmtpUTF8) - { - Assert.Equal(subjectBase64, server.Subject); - } - else - { - Assert.Equal(subjectText, server.Subject); - } + if (useSevenBit || !useSmtpUTF8) + { + Assert.Equal(subjectBase64, server.Message.Subject); } - finally + else { - server.Stop(); + Assert.Equal(subjectText, server.Message.Subject); } } + + [Fact] + public void SendMailAsync_CanBeCanceled_CancellationToken_SetAlready() + { + using var server = new LoopbackSmtpServer(); + using SmtpClient client = server.CreateClient(); + + CancellationTokenSource cts = new CancellationTokenSource(); + cts.Cancel(); + + var message = new MailMessage("foo@internet.com", "bar@internet.com", "Foo", "Bar"); + + Task sendTask = client.SendMailAsync(message, cts.Token); + + // Tests an implementation detail - if a CT is already set a canceled task will be returned + Assert.True(sendTask.IsCanceled); + } + + [Fact] + public async Task SendMailAsync_CanBeCanceled_CancellationToken() + { + using var server = new LoopbackSmtpServer(); + using SmtpClient client = server.CreateClient(); + + server.ReceiveMultipleConnections = true; + + // The server will introduce some fake latency so that the operation can be canceled before the request completes + ManualResetEvent serverMre = new ManualResetEvent(false); + server.OnConnected += _ => serverMre.WaitOne(); + + CancellationTokenSource cts = new CancellationTokenSource(); + + var message = new MailMessage("foo@internet.com", "bar@internet.com", "Foo", "Bar"); + + Task sendTask = client.SendMailAsync(message, cts.Token); + + cts.Cancel(); + await Task.Delay(500); + serverMre.Set(); + + await Assert.ThrowsAsync(async () => await sendTask); + + // We should still be able to send mail on the SmtpClient instance + await client.SendMailAsync(message); + + Assert.Equal("", server.MailFrom); + Assert.Equal("", server.MailTo); + Assert.Equal("Foo", server.Message.Subject); + Assert.Equal("Bar", server.Message.Body); + Assert.Equal(GetClientDomain(), server.ClientDomain); + } + + private static string GetClientDomain() => IPGlobalProperties.GetIPGlobalProperties().HostName.Trim().ToLower(); } } diff --git a/src/libraries/System.Net.Mail/tests/Functional/SmtpServer.cs b/src/libraries/System.Net.Mail/tests/Functional/SmtpServer.cs deleted file mode 100644 index 403f7c5c03a1d..0000000000000 --- a/src/libraries/System.Net.Mail/tests/Functional/SmtpServer.cs +++ /dev/null @@ -1,150 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// See the LICENSE file in the project root for more information. -// -// SmtpServer.cs - Dummy SMTP server used to test SmtpClient -// -// Author: -// Raja R Harinath -// - -using System.Diagnostics; -using System.IO; -using System.Net.Sockets; -using System.Text; - -namespace System.Net.Mail.Tests -{ - public class SmtpServer - { - private string _mailfrom, _mailto, _subject, _body, _clientdomain; - - public string MailFrom => _mailfrom; - public string MailTo => _mailto; - public string Subject => _subject; - public string Body => _body; - public string ClientDomain => _clientdomain; - - private readonly TcpListener _server; - - public IPEndPoint EndPoint - { - get { return (IPEndPoint)_server.LocalEndpoint; } - } - - public bool SupportSmtpUTF8 { get; set; } - - public SmtpServer() - { - IPAddress address = IPAddress.Loopback; - _server = new TcpListener(address, 0); - _server.Start(1); - } - - private static void WriteNS(NetworkStream ns, string s) - { - Trace("response", s); - byte[] bytes = Encoding.ASCII.GetBytes(s); - ns.Write(bytes, 0, bytes.Length); - } - - public void Stop() - { - _server.Stop(); - } - - public void Run() - { - try - { - string s; - using (TcpClient client = _server.AcceptTcpClient()) - { - Trace("connection", EndPoint.Port); - using (NetworkStream ns = client.GetStream()) - { - WriteNS(ns, "220 localhost\r\n"); - using (StreamReader r = new StreamReader(ns, Encoding.UTF8)) - { - while ((s = r.ReadLine()) != null && Dispatch(ns, r, s)) - ; - } - } - } - } - catch (SocketException e) - { - // The _server might have been stopped. - if (e.SocketErrorCode != SocketError.Interrupted) - throw; - } - } - - // return false == terminate - private bool Dispatch(NetworkStream ns, StreamReader r, string s) - { - Trace("command", s); - if (s.Length < 4) - { - WriteNS(ns, "502 Unrecognized\r\n"); - return false; - } - - bool retval = true; - switch (s.Substring(0, 4)) - { - case "HELO": - _clientdomain = s.Substring(5).Trim().ToLower(); - break; - case "EHLO": - _clientdomain = s.Substring(5).Trim().ToLower(); - WriteNS(ns, "250-localhost Hello" + s.Substring(5, s.Length - 5) + "\r\n"); - WriteNS(ns, "250-AUTH PLAIN\r\n"); - if (SupportSmtpUTF8) - { - WriteNS(ns, "250-SMTPUTF8\r\n"); - } - break; - case "QUIT": - WriteNS(ns, "221 Quit\r\n"); - return false; - case "MAIL": - _mailfrom = s.Substring(10); - break; - case "RCPT": - _mailto = s.Substring(8); - break; - case "DATA": - WriteNS(ns, "354 Continue\r\n"); - while ((s = r.ReadLine()) != null) - { - if (s == ".") - break; - - if (s.StartsWith("Subject:")) - { - _subject = s.Substring(9, s.Length - 9); - } - else if (s == "" && _body == null) - { - _body = r.ReadLine(); - } - } - Trace("end of data", s); - retval = (s != null); - break; - default: - WriteNS(ns, "502 Unrecognized\r\n"); - return true; - } - - WriteNS(ns, "250 OK\r\n"); - return retval; - } - - [Conditional("TEST")] - private static void Trace(string key, object value) - { - Console.Error.WriteLine("{0}: {1}", key, value); - } - } -} diff --git a/src/libraries/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj b/src/libraries/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj index 9bdd31a46ace3..f91fe3a0abbb5 100644 --- a/src/libraries/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj +++ b/src/libraries/System.Net.Mail/tests/Functional/System.Net.Mail.Functional.Tests.csproj @@ -20,7 +20,7 @@ - + Common\System\Diagnostics\Tracing\TestEventListener.cs