public SmtpClientSession(ILog log, SmtpClientSessionConfiguration configuration, MessageData messageData) { _log = log; _configuration = configuration; _messageData = messageData; }
public async Task<List<DeliveryResult>> DeliverAsync(Message message, List<Recipient> recipients) { if (message == null) throw new ArgumentNullException(nameof(message)); if (message.Recipients == null) throw new ArgumentNullException(nameof(message.Recipients)); var commaSeparatedRecipientList = string.Join(", ", recipients.Select(item => item.Address)); _log.LogInfo(new LogEvent() { EventType = LogEventType.Application, LogLevel = LogLevel.Info, Message = $"Delivering message from {message.From} to {commaSeparatedRecipientList}", Protocol = "SMTPD", }); var recipientsByDomain = recipients.GroupBy(recipient => EmailAddressParser.GetDomain(recipient.Address)).Distinct().ToList(); var result = new List<DeliveryResult>(); foreach (var domainWithRecipients in recipientsByDomain) { var ipAddresses = await _dnsClient.ResolveMxIpAddressesAsync(domainWithRecipients.Key); var remainingRecipientsOnDomain = domainWithRecipients.Select(item => item.Address).ToList(); foreach (var ipAddress in ipAddresses) { var client = new TcpClient(); await client.ConnectAsync(ipAddress, 25); var connection = new Connection(client, CancellationToken.None); using (var messageStream = _messageRepository.GetMessageData(message)) { var messageData = new MessageData() { From = message.From, Recipients = remainingRecipientsOnDomain, Data = messageStream }; var clientSession = new SmtpClientSession(_log, new SmtpClientSessionConfiguration(), messageData); await clientSession.HandleConnection(connection); foreach (var deliveryResult in clientSession.DeliveryResult) { var matchingRecipient = recipients.Single(recipient => string.Equals(recipient.Address, deliveryResult.Recipient, StringComparison.InvariantCultureIgnoreCase)); switch (deliveryResult.ReplyCodeSeverity) { case ReplyCodeSeverity.Positive: // Delete the recipient right away, so that if there is a crash we don't end up sending to this recipient again. await _messageRepository.DeleteRecipientAsync(matchingRecipient); result.Add(deliveryResult); remainingRecipientsOnDomain.Remove(deliveryResult.Recipient); _log.LogInfo(new LogEvent() { EventType = LogEventType.Application, LogLevel = LogLevel.Info, Message = $"Message delivery from {message.From} to {deliveryResult.Recipient} completed", Protocol = "SMTPD", }); break; case ReplyCodeSeverity.PermanentNegative: // Let this recipient be deleted after we've submitted bounce message. This is delayed so that we don't // lose the information if there's a crash. result.Add(deliveryResult); remainingRecipientsOnDomain.Remove(deliveryResult.Recipient); _log.LogInfo(new LogEvent() { EventType = LogEventType.Application, LogLevel = LogLevel.Info, Message = $"Message delivery from {message.From} to {deliveryResult.Recipient} failed permanently: {deliveryResult.ResultMessage}", Protocol = "SMTPD", }); break; case ReplyCodeSeverity.TransientNegative: _log.LogInfo(new LogEvent() { EventType = LogEventType.Application, LogLevel = LogLevel.Info, Message = $"Message delivery from {message.From} to {deliveryResult.Recipient} failed temporarily: {deliveryResult.ResultMessage}", Protocol = "SMTPD", }); break; } } } if (!remainingRecipientsOnDomain.Any()) { // No more recipients remaining. break; } } } return result; }