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
}
}