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); }
/// <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) }); }