| File: System\Net\Mail\SmtpTransport.cs | Web Access |
| Project: src\runtime\src\libraries\System.Net.Mail\src\System.Net.Mail.csproj (System.Net.Mail) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; using System.IO; using System.Net.Mime; using System.Runtime.ExceptionServices; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; namespace System.Net.Mail { internal sealed class SmtpTransport { internal const int DefaultPort = 25; private readonly ISmtpAuthenticationModule[] _authenticationModules; private SmtpConnection? _connection; private readonly SmtpClient _client; private ICredentialsByHost? _credentials; private bool _shouldAbort; private bool _enableSsl; internal SmtpTransport(SmtpClient client) : this(client, SmtpAuthenticationManager.GetModules()) { } internal SmtpTransport(SmtpClient client, ISmtpAuthenticationModule[] authenticationModules) { ArgumentNullException.ThrowIfNull(authenticationModules); _client = client; _authenticationModules = authenticationModules; } internal ICredentialsByHost? Credentials { get { return _credentials; } set { _credentials = value; } } internal bool IsConnected { get { return _connection != null && _connection.IsConnected; } } internal bool EnableSsl { get { return _enableSsl; } set { _enableSsl = value; } } internal X509CertificateCollection ClientCertificates => field ??= new X509CertificateCollection(); internal bool ServerSupportsEai { get { return _connection != null && _connection.ServerSupportsEai; } } internal void GetConnection(string host, int port) { GetConnectionAsync<SyncReadWriteAdapter>(host, port).GetAwaiter().GetResult(); } internal async Task GetConnectionAsync(string host, int port, CancellationToken cancellationToken = default) { await GetConnectionAsync<AsyncReadWriteAdapter>(host, port, cancellationToken).ConfigureAwait(false); } internal Task GetConnectionAsync<TIOAdapter>(string host, int port, CancellationToken cancellationToken = default) where TIOAdapter : IReadWriteAdapter { lock (this) { _connection = new SmtpConnection(this, _client, _credentials, _authenticationModules); if (_shouldAbort) { _connection.Abort(); } _shouldAbort = false; } if (NetEventSource.Log.IsEnabled()) NetEventSource.Associate(this, _connection); if (EnableSsl) { _connection.EnableSsl = true; _connection.ClientCertificates = ClientCertificates; } return _connection.GetConnectionAsync<TIOAdapter>(host, port, cancellationToken); } internal async Task<(MailWriter, List<SmtpFailedRecipientException>?)> SendMailAsync<TIOAdapter>(MailAddress sender, MailAddressCollection recipients, string deliveryNotify, bool allowUnicode, CancellationToken cancellationToken = default) where TIOAdapter : IReadWriteAdapter { ArgumentNullException.ThrowIfNull(sender); ArgumentNullException.ThrowIfNull(recipients); await MailCommand.SendAsync<TIOAdapter>(_connection!, SmtpCommands.Mail, sender, allowUnicode, cancellationToken).ConfigureAwait(false); List<SmtpFailedRecipientException>? failedRecipientExceptions = null; foreach (MailAddress address in recipients) { string smtpAddress = address.GetSmtpAddress(allowUnicode); string to = smtpAddress + (_connection!.DSNEnabled ? deliveryNotify : string.Empty); (bool success, string? response) = await RecipientCommand.SendAsync<TIOAdapter>(_connection, to, cancellationToken).ConfigureAwait(false); if (!success) { (failedRecipientExceptions ??= new()).Add( new SmtpFailedRecipientException(_connection.Reader!.StatusCode, smtpAddress, response)); } } if (failedRecipientExceptions?.Count > 0 && failedRecipientExceptions.Count == recipients.Count) { var exception = failedRecipientExceptions.Count == 1 ? failedRecipientExceptions[0] : new SmtpFailedRecipientsException(failedRecipientExceptions, true); exception.fatal = true; throw exception; } await DataCommand.SendAsync<TIOAdapter>(_connection!, cancellationToken).ConfigureAwait(false); return (new MailWriter(_connection!.GetClosableStream(), encodeForTransport: true), failedRecipientExceptions); } internal void ReleaseConnection() { _connection?.ReleaseConnection(); } internal void Abort() { lock (this) { if (_connection != null) { _connection.Abort(); } else { _shouldAbort = true; } } } } }