using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Xml; using CodeScales.Http; using CodeScales.Http.Entity; using CodeScales.Http.Entity.Mime; using CodeScales.Http.Methods; namespace SendGridMail.Transport { public class Web : ITransport { #region Properties public const String Endpoint = "http://sendgrid.com/api/mail.send"; public const String JsonFormat = "json"; public const String XmlFormat = "xml"; private readonly NetworkCredential _credentials; private readonly String _restEndpoint; private readonly String _format; #endregion /// /// Factory method for Web transport of sendgrid messages /// /// SendGrid credentials for sending mail messages /// The uri of the Web endpoint /// New instance of the transport mechanism public static Web GetInstance(NetworkCredential credentials, String url = Endpoint) { return new Web(credentials, url); } /// /// Creates a new Web interface for sending mail. Preference is using the Factory method. /// /// SendGrid user parameters /// The uri of the Web endpoint internal Web(NetworkCredential credentials, String url = Endpoint) { _credentials = credentials; _format = XmlFormat; _restEndpoint = url + "." + _format; } /// /// Delivers a message over SendGrid's Web interface /// /// public void Deliver(ISendGrid message) { MultipartEntity multipartEntity; HttpPost postMethod; var client = InitializeTransport(out multipartEntity, out postMethod); AttachFormParams(message, multipartEntity); AttachFiles(message, multipartEntity); var response = client.Execute(postMethod); CheckForErrors(response); } #region Support Methods internal HttpClient InitializeTransport(out MultipartEntity multipartEntity, out HttpPost postMethod) { var client = new HttpClient(); postMethod = new HttpPost(new Uri(_restEndpoint)); multipartEntity = new MultipartEntity(); postMethod.Entity = multipartEntity; return client; } private void AttachFormParams(ISendGrid message, MultipartEntity multipartEntity) { var formParams = FetchFormParams(message); formParams.ForEach(kvp => multipartEntity.AddBody(new StringBody(Encoding.UTF8, kvp.Key, kvp.Value))); } private void AttachFiles(ISendGrid message, MultipartEntity multipartEntity) { var files = FetchFileBodies(message); files.ForEach(kvp => multipartEntity.AddBody(new FileBody("files[" + Path.GetFileName(kvp.Key) + "]", Path.GetFileName(kvp.Key), kvp.Value))); } private void CheckForErrors(CodeScales.Http.Methods.HttpResponse response) { var status = EntityUtils.ToString(response.Entity); var stream = new MemoryStream(Encoding.UTF8.GetBytes(status)); using (var reader = XmlReader.Create(stream)) { while (reader.Read()) { if (reader.IsStartElement()) { switch (reader.Name) { case "result": break; case "message": // success return; case "error": // failure throw new ProtocolViolationException(status); default: throw new ArgumentException("Unknown element: " + reader.Name); } } } } } internal List> FetchFormParams(ISendGrid message) { var result = new List>() { new KeyValuePair("api_user", _credentials.UserName), new KeyValuePair("api_key", _credentials.Password), new KeyValuePair("headers", message.Headers.Count == 0 ? null : Utils.SerializeDictionary(message.Headers)), new KeyValuePair("replyto", message.ReplyTo.Length == 0 ? null : message.ReplyTo.ToList().First().Address), new KeyValuePair("from", message.From.Address), new KeyValuePair("fromname", message.From.DisplayName), new KeyValuePair("subject", message.Subject), new KeyValuePair("text", message.Text), new KeyValuePair("html", message.Html), new KeyValuePair("x-smtpapi", message.Header.AsJson()) }; if(message.To != null) { result = result.Concat(message.To.ToList().Select(a => new KeyValuePair("to[]", a.Address))) .Concat(message.To.ToList().Select(a => new KeyValuePair("toname[]", a.DisplayName))) .ToList(); } if(message.Bcc != null) { result = result.Concat(message.Bcc.ToList().Select(a => new KeyValuePair("bcc[]", a.Address))) .ToList(); } if(message.Cc != null) { result = result.Concat(message.Cc.ToList().Select(a => new KeyValuePair("cc[]", a.Address))) .ToList(); } return result.Where(r => !String.IsNullOrEmpty(r.Value)).ToList(); } internal List> FetchFileBodies(ISendGrid message) { if(message.Attachments == null) return new List>(); return message.Attachments.Select(name => new KeyValuePair(name, new FileInfo(name))).ToList(); } #endregion } }