/// <summary> /// Sends an array of email messages using a session state object. /// Credentials, Enable SSL and Client Hostname are NOT taken from the state object but /// rather from the properties of this class. /// </summary> /// <param name="sessionStates">The session states.</param> /// <param name="sessionId">The session identifier.</param> /// <param name="ct">The cancellation token.</param> /// <returns> /// A task that represents the asynchronous of send email operation. /// </returns> /// <exception cref="ArgumentNullException">sessionStates.</exception> /// <exception cref="SecurityException">Could not upgrade the channel to SSL.</exception> /// <exception cref="SmtpException">Defines an SMTP Exceptions class.</exception> public async Task SendMailAsync(IEnumerable <SmtpSessionState> sessionStates, string sessionId = null, CancellationToken ct = default) { if (sessionStates == null) { throw new ArgumentNullException(nameof(sessionStates)); } using (var tcpClient = new TcpClient()) { await tcpClient.ConnectAsync(Host, Port); using (var connection = new Connection(tcpClient, Encoding.UTF8, "\r\n", true, 1000)) { var sender = new SmtpSender(sessionId); try { // Read the greeting message sender.ReplyText = await connection.ReadLineAsync(ct); // EHLO 1 sender.RequestText = $"{SmtpCommandNames.EHLO} {ClientHostname}"; await connection.WriteLineAsync(sender.RequestText, ct); do { sender.ReplyText = await connection.ReadLineAsync(ct); } while (!sender.IsReplyOk); sender.ValidateReply(); // STARTTLS if (EnableSsl) { sender.RequestText = $"{SmtpCommandNames.STARTTLS}"; await connection.WriteLineAsync(sender.RequestText, ct); sender.ReplyText = await connection.ReadLineAsync(ct); sender.ValidateReply(); if (await connection.UpgradeToSecureAsClientAsync() == false) { throw new SecurityException("Could not upgrade the channel to SSL."); } } { // EHLO 2 sender.RequestText = $"{SmtpCommandNames.EHLO} {ClientHostname}"; await connection.WriteLineAsync(sender.RequestText, ct); do { sender.ReplyText = await connection.ReadLineAsync(ct); } while (!sender.IsReplyOk); sender.ValidateReply(); } // AUTH if (Credentials != null) { var auth = new ConnectionAuth(connection, sender, Credentials); await auth.AuthenticateAsync(ct); } foreach (var sessionState in sessionStates) { { // MAIL FROM sender.RequestText = $"{SmtpCommandNames.MAIL} FROM:<{sessionState.SenderAddress}>"; await connection.WriteLineAsync(sender.RequestText, ct); sender.ReplyText = await connection.ReadLineAsync(ct); sender.ValidateReply(); } // RCPT TO foreach (var recipient in sessionState.Recipients) { sender.RequestText = $"{SmtpCommandNames.RCPT} TO:<{recipient}>"; await connection.WriteLineAsync(sender.RequestText, ct); sender.ReplyText = await connection.ReadLineAsync(ct); sender.ValidateReply(); } { // DATA sender.RequestText = $"{SmtpCommandNames.DATA}"; await connection.WriteLineAsync(sender.RequestText, ct); sender.ReplyText = await connection.ReadLineAsync(ct); sender.ValidateReply(); } { // CONTENT var dataTerminator = sessionState.DataBuffer.Skip(sessionState.DataBuffer.Count - 5).ToArray().ToText(); sender.RequestText = $"Buffer ({sessionState.DataBuffer.Count} bytes)"; await connection.WriteDataAsync(sessionState.DataBuffer.ToArray(), true, ct); if (dataTerminator.EndsWith(SmtpDefinitions.SmtpDataCommandTerminator) == false) { await connection.WriteTextAsync(SmtpDefinitions.SmtpDataCommandTerminator, ct); } sender.ReplyText = await connection.ReadLineAsync(ct); sender.ValidateReply(); } } { // QUIT sender.RequestText = $"{SmtpCommandNames.QUIT}"; await connection.WriteLineAsync(sender.RequestText, ct); sender.ReplyText = await connection.ReadLineAsync(ct); sender.ValidateReply(); } } catch (Exception ex) { var errorMessage = $"Could not send email. {ex.Message}\r\n Last Request: {sender.RequestText}\r\n Last Reply: {sender.ReplyText}"; errorMessage.Error(typeof(SmtpClient).FullName, sessionId); throw new SmtpException(errorMessage); } } } }
/// <summary> /// Sends an array of email messages using a session state object. /// Credentials, Enable SSL and Client Hostname are NOT taken from the state object but /// rather from the properties of this class. /// </summary> /// <param name="sessionStates">The session states.</param> /// <param name="sessionId">The session identifier.</param> /// <param name="callback">The callback.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns> /// A task that represents the asynchronous of send email operation. /// </returns> /// <exception cref="ArgumentNullException">sessionStates.</exception> /// <exception cref="SecurityException">Could not upgrade the channel to SSL.</exception> /// <exception cref="SmtpException">Defines an SMTP Exceptions class.</exception> public async Task SendMailAsync( IEnumerable <SmtpSessionState> sessionStates, string?sessionId = null, RemoteCertificateValidationCallback?callback = null, CancellationToken cancellationToken = default) { if (sessionStates == null) { throw new ArgumentNullException(nameof(sessionStates)); } using var tcpClient = new TcpClient(); await tcpClient.ConnectAsync(Host, Port, cancellationToken).ConfigureAwait(false); using var connection = new Connection(tcpClient, Encoding.UTF8, "\r\n", true, 1000); var sender = new SmtpSender(sessionId); try { // Read the greeting message sender.ReplyText = await connection.ReadLineAsync(cancellationToken).ConfigureAwait(false); // EHLO 1 await SendEhlo(sender, connection, cancellationToken).ConfigureAwait(false); // STARTTLS if (EnableSsl) { sender.RequestText = $"{SmtpCommandNames.STARTTLS}"; await connection.WriteLineAsync(sender.RequestText, cancellationToken).ConfigureAwait(false); sender.ReplyText = await connection.ReadLineAsync(cancellationToken).ConfigureAwait(false); sender.ValidateReply(); if (await connection.UpgradeToSecureAsClientAsync(callback: callback).ConfigureAwait(false) == false) { throw new SecurityException("Could not upgrade the channel to SSL."); } } // EHLO 2 await SendEhlo(sender, connection, cancellationToken).ConfigureAwait(false); // AUTH if (Credentials != null) { var auth = new ConnectionAuth(connection, sender, Credentials); await auth.AuthenticateAsync(cancellationToken).ConfigureAwait(false); } foreach (var sessionState in sessionStates) { { // MAIL FROM sender.RequestText = $"{SmtpCommandNames.MAIL} FROM:<{sessionState.SenderAddress}>"; await connection.WriteLineAsync(sender.RequestText, cancellationToken).ConfigureAwait(false); sender.ReplyText = await connection.ReadLineAsync(cancellationToken).ConfigureAwait(false); sender.ValidateReply(); } // RCPT TO foreach (var recipient in sessionState.Recipients) { sender.RequestText = $"{SmtpCommandNames.RCPT} TO:<{recipient}>"; await connection.WriteLineAsync(sender.RequestText, cancellationToken).ConfigureAwait(false); sender.ReplyText = await connection.ReadLineAsync(cancellationToken).ConfigureAwait(false); sender.ValidateReply(); } { // DATA sender.RequestText = $"{SmtpCommandNames.DATA}"; await connection.WriteLineAsync(sender.RequestText, cancellationToken).ConfigureAwait(false); sender.ReplyText = await connection.ReadLineAsync(cancellationToken).ConfigureAwait(false); sender.ValidateReply(); } { // CONTENT var dataTerminator = sessionState.DataBuffer .Skip(sessionState.DataBuffer.Count - 5) .ToText(); sender.RequestText = $"Buffer ({sessionState.DataBuffer.Count} bytes)"; await connection.WriteDataAsync(sessionState.DataBuffer.ToArray(), true, cancellationToken).ConfigureAwait(false); if (!dataTerminator.EndsWith(SmtpDefinitions.SmtpDataCommandTerminator, StringComparison.OrdinalIgnoreCase)) { await connection.WriteTextAsync(SmtpDefinitions.SmtpDataCommandTerminator, cancellationToken).ConfigureAwait(false); } sender.ReplyText = await connection.ReadLineAsync(cancellationToken).ConfigureAwait(false); sender.ValidateReply(); } } { // QUIT sender.RequestText = $"{SmtpCommandNames.QUIT}"; await connection.WriteLineAsync(sender.RequestText, cancellationToken).ConfigureAwait(false); sender.ReplyText = await connection.ReadLineAsync(cancellationToken).ConfigureAwait(false); sender.ValidateReply(); } } catch (Exception ex) { throw new SmtpException($"Could not send email - Session ID {sessionId}. {ex.Message}\r\n Last Request: {sender.RequestText}\r\n Last Reply: {sender.ReplyText}"); } }