Пример #1
0
        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", "");
        }
Пример #5
0
        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;
            }
        }
Пример #6
0
        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);
        }
Пример #7
0
        private bool IsMailerDaemonAddress(string address)
        {
            var mailbox = EmailAddressParser.GetMailbox(address);

            return(mailbox.Equals("MAILER-DAEMON", StringComparison.InvariantCultureIgnoreCase));
        }
Пример #8
0
        /// <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)
            });
        }