private string GetAccountMessageDirectory(Account account) { var mailbox = EmailAddressParser.GetMailbox(account.Address); var domain = EmailAddressParser.GetDomain(account.Address); return(Path.Combine(_dataDirectory, domain, mailbox)); }
public void TestParser() { EmailAddressParser emailaddressparser = new EmailAddressParser(); String name1 = "Test Tester"; String email1 = "*****@*****.**"; EmailAddress emailaddress = emailaddressparser.ParseRawEmailAddress(String.Format("{0} <{1}>", name1, email1)); Assert.IsNull(emailaddressparser.LastError); Assert.IsNotNull(emailaddress, emailaddressparser.LastError); Assert.AreEqual(name1, emailaddress.Name); Assert.AreEqual(email1, emailaddress.Email); emailaddress = emailaddressparser.ParseRawEmailAddress(email1); Assert.IsNull(emailaddressparser.LastError); Assert.IsNotNull(emailaddress, emailaddressparser.LastError); Assert.AreEqual("", emailaddress.Name); Assert.AreEqual(email1, emailaddress.Email); emailaddress = emailaddressparser.ParseRawEmailAddress(String.Format("\"{0}\" <{1}>", name1, email1)); Assert.IsNotNull(emailaddress, emailaddressparser.LastError); Assert.IsNull(emailaddressparser.LastError, emailaddressparser.LastError); Assert.AreEqual("\"" + name1 + "\"", emailaddress.Name); Assert.AreEqual(email1, emailaddress.Email); emailaddress = emailaddressparser.ParseRawEmailAddress(String.Format("{0} <{1}>", name1, "test@localhost")); Assert.IsNotNull(emailaddress, emailaddressparser.LastError); Assert.IsNull(emailaddressparser.LastError, emailaddressparser.LastError); Assert.AreEqual(name1, emailaddress.Name); Assert.AreEqual("test@localhost", emailaddress.Email); }
public void ParseValidEmail(string email) { var e = EmailAddressParser.ParseEmailAddress($"\"First, middle, lastname\" <{email}>"); e.Name.Should().Be("First, middle, lastname"); e.Email.Should().Be(email); e = EmailAddressParser.ParseEmailAddress($"\"First\" <{email}>"); e.Name.Should().Be("First"); e.Email.Should().Be(email); e = EmailAddressParser.ParseEmailAddress(email); e.Name.Should().Be(""); e.Email.Should().Be(email); }
public void ParseMultipleValidEmails(string email) { void verify(EmailAddress[] addr, params string[] expectedNames) { addr.Should().HaveCount(2); for (int i = 0; i < addr.Length; i++) { addr[i].Email.Should().Be(email); addr[i].Name.Should().Be(expectedNames[i]); } } verify(EmailAddressParser.ParseEmailAddresses($"\"First, middle, lastname\" <{email}>, \"Second\" <{email}>"), "First, middle, lastname", "Second"); verify(EmailAddressParser.ParseEmailAddresses($"\"First, middle, lastname\" <{email}>, \"First\" <{email}>"), "First, middle, lastname", "First"); verify(EmailAddressParser.ParseEmailAddresses($"\"First, middle, lastname\" <{email}>, {email}"), "First, middle, lastname", ""); }
private async Task HandleMailFrom(string data) { var mailFromParseResult = CommandParser.ParseMailFrom(data); // BREAKING: Option Allow Mail From Null removed. if (!EmailAddressParser.IsValidEmailAddress(mailFromParseResult.Address)) { await SendCommandResult(new SmtpCommandReply(550, "The address is not valid")); return; } var commandResult = await _commandHandler.HandleMailFrom(mailFromParseResult.Address); await SendCommandResult(commandResult); if (commandResult.IsPositive()) { _state.HasMailFrom = true; } }
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); }
private bool IsMailerDaemonAddress(string address) { var mailbox = EmailAddressParser.GetMailbox(address); return(mailbox.Equals("MAILER-DAEMON", StringComparison.InvariantCultureIgnoreCase)); }
/// <summary> /// Parses a raw multipart form from the stream as a sendgrid email according to https://sendgrid.com/docs/for-developers/parsing-email/setting-up-the-inbound-parse-webhook/#default-parameters /// Adapted from https://github.com/KoditkarVedant/sendgrid-inbound /// </summary> public Email Parse(MemoryStream body) { var parser = MultipartFormDataParser.Parse(body, Encoding.UTF8); var charsets = JObject.Parse(parser.GetParameterValue("charsets", "{}")) .Properties() .ToDictionary(p => p.Name, p => Encoding.GetEncoding(p.Value.ToString())); var rawHeaders = parser .GetParameterValue("headers", "") .Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries); var headers = rawHeaders .Select(header => { var splitHeader = header.Split(new[] { ": " }, StringSplitOptions.RemoveEmptyEntries); var key = splitHeader[0]; var value = splitHeader.Length > 1 ? splitHeader[1] : null; return(new KeyValuePair <string, string>(key, value)); }).ToArray(); // Create a dictionary of parsers, one parser for each desired encoding. // This is necessary because MultipartFormDataParser can only handle one // encoding and SendGrid can use different encodings for parameters such // as "from", "to", "text" and "html". var encodedParsers = charsets .Where(c => c.Value != Encoding.UTF8) .Select(c => c.Value) .Distinct() .Select(encoding => { body.Position = 0; return(new { Encoding = encoding, Parser = MultipartFormDataParser.Parse(body, encoding) }); }) .Union(new[] { new { Encoding = Encoding.UTF8, Parser = parser } }) .ToDictionary(ep => ep.Encoding, ep => ep.Parser); // convert the raw formats so we can apply the correct encoders. the pre-parsed Sendgrid values (in Envelope) are forced to UTF-8 var rawFrom = GetEncodedValue("from", charsets, encodedParsers, string.Empty); var from = EmailAddressParser.ParseEmailAddress(rawFrom); var rawTo = GetEncodedValue("to", charsets, encodedParsers, string.Empty); var to = EmailAddressParser.ParseEmailAddresses(rawTo); var rawCc = GetEncodedValue("cc", charsets, encodedParsers, string.Empty); var cc = EmailAddressParser.ParseEmailAddresses(rawCc); // will have attachment1...attachmentX properties depending on attachment count // conver to array var attachmentInfoAsJObject = JObject.Parse(parser.GetParameterValue("attachment-info", "{}")); var attachments = attachmentInfoAsJObject .Properties() .Select(prop => { var attachment = prop.Value.ToObject <EmailAttachment>(); attachment.Id = prop.Name; var file = parser.Files.FirstOrDefault(f => f.Name == prop.Name); if (file != null) { attachment.Base64Data = file.Data.ConvertToBase64(); if (string.IsNullOrEmpty(attachment.ContentType)) { attachment.ContentType = file.ContentType; } if (string.IsNullOrEmpty(attachment.FileName)) { attachment.FileName = file.FileName; } } return(attachment); }).ToArray(); return(new Email { // serializer friendly format Charsets = charsets.ToDictionary(p => p.Key, p => p.Value.WebName), Headers = headers, From = from, To = to, Cc = cc, Subject = GetEncodedValue("subject", charsets, encodedParsers, null), Html = GetEncodedValue("html", charsets, encodedParsers, null), Text = GetEncodedValue("text", charsets, encodedParsers, null), Attachments = attachments, SenderIp = GetEncodedValue("sender_ip", charsets, encodedParsers, null), SpamReport = GetEncodedValue("spam_report", charsets, encodedParsers, null), SpamScore = GetEncodedValue("spam_score", charsets, encodedParsers, null), Dkim = GetEncodedValue("dkim", charsets, encodedParsers, null), Spf = GetEncodedValue("SPF", charsets, encodedParsers, null) }); }