summaryrefslogtreecommitdiffstats
path: root/SendGrid/SendGridMail
diff options
context:
space:
mode:
authorRobin J <me@rbin.co>2014-02-17 16:11:42 +0000
committerRobin J <me@rbin.co>2014-02-17 16:11:42 +0000
commit849ece5e6b93b0fb965f6d52a78958ef1a014f9a (patch)
tree08652475fbd7e8adeb26f816a3bc80259c4156bc /SendGrid/SendGridMail
parent66094a4e8321e834229a70084a6235360e52155c (diff)
parent9ab26dc5e0057ba3c9017175196b1b96a0a53deb (diff)
downloadsendgrid-csharp-849ece5e6b93b0fb965f6d52a78958ef1a014f9a.zip
sendgrid-csharp-849ece5e6b93b0fb965f6d52a78958ef1a014f9a.tar.gz
sendgrid-csharp-849ece5e6b93b0fb965f6d52a78958ef1a014f9a.tar.bz2
Merge pull request #44 from Xerax/master
Code clean-up and tweaks
Diffstat (limited to 'SendGrid/SendGridMail')
-rw-r--r--SendGrid/SendGridMail/ISendGrid.cs558
-rw-r--r--SendGrid/SendGridMail/Mail.csproj3
-rw-r--r--SendGrid/SendGridMail/Properties/AssemblyInfo.cs21
-rw-r--r--SendGrid/SendGridMail/SendGrid.cs1064
-rw-r--r--SendGrid/SendGridMail/Transport/ITransport.cs24
-rw-r--r--SendGrid/SendGridMail/Transport/Web.cs410
-rw-r--r--SendGrid/SendGridMail/packages.config11
7 files changed, 1043 insertions, 1048 deletions
diff --git a/SendGrid/SendGridMail/ISendGrid.cs b/SendGrid/SendGridMail/ISendGrid.cs
index ad868eb..1e61610 100644
--- a/SendGrid/SendGridMail/ISendGrid.cs
+++ b/SendGrid/SendGridMail/ISendGrid.cs
@@ -1,276 +1,298 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Net;
using System.Net.Mail;
using Smtpapi;
namespace SendGridMail
{
- /// <summary>
- /// Represents the basic set of functions that will be called by the user
- /// includes basic message data manipulation and filter settings
- /// </summary>
- public interface ISendGrid
- {
- #region Properties
- MailAddress From { get; set; }
- MailAddress[] To { get; set; }
- MailAddress[] Cc { get; }
- MailAddress[] Bcc { get; }
- MailAddress[] ReplyTo { get; set; }
- Dictionary<String, MemoryStream> StreamedAttachments { get; set; }
- String[] Attachments { get; set; }
- String Subject { get; set; }
- Dictionary<String, String> Headers { get; set; }
- IHeader Header { get; set; }
- String Html { get; set; }
- String Text { get; set; }
- #endregion
-
- #region Interface for ITransport
- /// <summary>
- /// Used by the Transport object to create a MIME for SMTP
- /// </summary>
- /// <returns>MIME to be sent</returns>
- MailMessage CreateMimeMessage();
- #endregion
-
- #region Methods for setting data
- /// <summary>
- /// Add to the 'To' address.
- /// </summary>
- /// <param name="address">single string eg. 'you@company.com'</param>
- void AddTo(String address);
-
- /// <summary>
- /// Add to the 'To' address.
- /// </summary>
- /// <param name="addresses">list of email addresses as strings</param>
- void AddTo(IEnumerable<String> addresses);
-
- /// <summary>
- /// Add to the 'To' address.
- /// </summary>
- /// <param name="addresssInfo"> the dictionary keys are the email addresses, which points to a dictionary of
- /// key substitutionValues pairs mapping to other address codes, such as { foo@bar.com => { 'DisplayName' => 'Mr Foo' } } </param>
- void AddTo(IDictionary<String, IDictionary<String, String>> addresssInfo);
-
- /// <summary>
- /// Add to the 'CC' address.
- /// </summary>
- /// <param name="address">a single email address eg "you@company.com"</param>
- void AddCc(String address);
-
- /// <summary>
- /// Add to the 'CC' address.
- /// </summary>
- /// <param name="addresses">a list of email addresses as strings</param>
- void AddCc(IEnumerable<String> addresses);
-
- /// <summary>
- /// Add to the 'CC' address.
- /// </summary>
- /// <param name="addresssInfo">the dictionary keys are the email addresses, which points to a dictionary of
- /// key substitutionValues pairs mapping to other address codes, such as { foo@bar.com => { 'DisplayName' => 'Mr Foo' } } </param>
- void AddCc(IDictionary<String, IDictionary<String, String>> addresssInfo);
-
- /// <summary>
- /// Add to the 'Bcc' address.
- /// </summary>
- /// <param name="address">a single email as the input eg "you@company.com"</param>
- void AddBcc(String address);
-
- /// <summary>
- /// Add to the 'Bcc' address.
- /// </summary>
- /// <param name="addresses">a list of emails as an array of strings.</param>
- void AddBcc(IEnumerable<String> addresses);
-
- /// <summary>
- /// Add to the 'Bcc' address.
- /// </summary>
- /// <param name="addresssInfo">the dictionary keys are the email addresses, which points to a dictionary of
- /// key substitutionValues pairs mapping to other address codes, such as { foo@bar.com => { 'DisplayName' => 'Mr Foo' } }</param>
- void AddBcc(IDictionary<String, IDictionary<String, String>> addresssInfo);
-
- /// <summary>
- /// Defines a mapping between a replacement string in the text of the message to a list of
- /// substitution values to be used, one per each recipient, in the same order as the recipients were added.
- /// </summary>
- /// <param name="replacementTag">the string in the email that you'll replace eg. '-name-'</param>
- /// <param name="substitutionValues">a list of values that will be substituted in for the replacementTag, one for each recipient</param>
- void AddSubstitution(String replacementTag, List<String> substitutionValues);
-
- /// <summary>
- /// This adds parameters and values that will be bassed back through SendGrid's
- /// Event API if an event notification is triggered by this email.
- /// </summary>
- /// <param name="identifiers">parameter substitutionValues pairs to be passed back on event notification</param>
- void AddUniqueArgs(IDictionary<String, String> identifiers);
-
- /// <summary>
- /// This sets the category for this email. Statistics are stored on a per category
- /// basis, so this can be useful for tracking on a per group basis.
- /// </summary>
- /// <param name="category">categories applied to the message</param>
- void SetCategory(String category);
-
- /// <summary>
- /// This sets the categories for this email. Statistics are stored on a per category
- /// basis, so this can be useful for tracking on a per group basis.
- /// </summary>
- /// <param name="categories">categories applied to the message</param>
- void SetCategories(IEnumerable<String> categories);
-
- /// <summary>
- /// Add an attachment to the message.
- /// </summary>
- /// <param name="filePath">a fully qualified file path as a string</param>
- void AddAttachment(String filePath);
-
- /// <summary>
- /// Add a stream as an attachment to the message
- /// </summary>
- /// <param name="stream">Stream of file to be attached</param>
- /// <param name="name">Name of file to be attached</param>
- void AddAttachment(Stream stream, String name);
-
- /// <summary>
- /// GetRecipients returns a list of all the recepients by retrieving the to, cc, and bcc lists.
- /// </summary>
- /// <returns></returns>
- IEnumerable<String> GetRecipients();
-
- /// <summary>
- /// Add custom headers to the message
- /// </summary>
- /// <param name="headers">key substitutionValues pairs</param>
- void AddHeaders(IDictionary<String, String> headers);
- #endregion
-
- #region SMTP API Functions
- /// <summary>
- /// Disable the gravatar app
- /// </summary>
- void DisableGravatar();
-
- /// <summary>
- /// Disable the open tracking app
- /// </summary>
- void DisableOpenTracking();
-
- /// <summary>
- /// Disable the click tracking app
- /// </summary>
- void DisableClickTracking();
-
- /// <summary>
- /// Disable the spam check
- /// </summary>
- void DisableSpamCheck();
-
- /// <summary>
- /// Disable the unsubscribe app
- /// </summary>
- void DisableUnsubscribe();
-
- /// <summary>
- /// Disable the footer app
- /// </summary>
- void DisableFooter();
-
- /// <summary>
- /// Disable the Google Analytics app
- /// </summary>
- void DisableGoogleAnalytics();
-
- /// <summary>
- /// Disable the templates app
- /// </summary>
- void DisableTemplate();
-
- /// <summary>
- /// Disable Bcc app
- /// </summary>
- void DisableBcc();
-
- /// <summary>
- /// Disable the Bypass List Management app
- /// </summary>
- void DisableBypassListManagement();
-
- /// <summary>
- /// Inserts the gravatar image of the sender to the bottom of the message
- /// </summary>
- void EnableGravatar();
-
- /// <summary>
- /// Adds an invisible image to the end of the email which can track e-mail opens.
- /// </summary>
- void EnableOpenTracking();
-
- /// <summary>
- /// Causes all links to be overwritten, shortened, and pointed to SendGrid's servers so clicks will be tracked.
- /// </summary>
- /// <param name="includePlainText">true if links found in plain text portions of the message are to be overwritten</param>
- void EnableClickTracking(bool includePlainText = false);
-
- /// <summary>
- /// Provides notification when emails are deteched that exceed a predefined spam threshold.
- /// </summary>
- /// <param name="score">Emails with a SpamAssassin score over this substitutionValues will be considered spam and not be delivered.</param>
- /// <param name="url">SendGrid will send an HTTP POST request to this url when a message is detected as spam</param>
- void EnableSpamCheck(int score = 5, String url = null);
-
- /// <summary>
- /// Allow's SendGrid to manage unsubscribes and ensure these users don't get future emails from the sender
- /// </summary>
- /// <param name="text">String for the plain text email body showing what you want the message to look like.</param>
- /// <param name="html">String for the HTML email body showing what you want the message to look like.</param>
- void EnableUnsubscribe(String text, String html);
-
- /// <summary>
- /// Allow's SendGrid to manage unsubscribes and ensure these users don't get future emails from the sender
- /// </summary>
- /// <param name="replace">Tag in the message body to be replaced with the unsubscribe link and message</param>
- void EnableUnsubscribe(String replace);
-
- /// <summary>
- /// Attaches a message at the footer of the email
- /// </summary>
- /// <param name="text">Message for the plain text body of the email</param>
- /// <param name="html">Message for the HTML body of the email</param>
- void EnableFooter(String text = null, String html = null);
-
- /// <summary>
- /// Re-writes links to integrate with Google Analytics
- /// </summary>
- /// <param name="source">Name of the referrer source (e.g. Google, SomeDomain.com, NewsletterA)</param>
- /// <param name="medium">Name of the marketing medium (e.g. Email)</param>
- /// <param name="term">Identify paid keywords</param>
- /// <param name="content">Use to differentiate ads</param>
- /// <param name="campaign">Name of the campaign</param>
- void EnableGoogleAnalytics(String source, String medium, String term, String content = null, String campaign = null);
-
- /// <summary>
- /// Wraps an HTML template around your email content.
- /// </summary>
- /// <param name="html">HTML that your emails will be wrapped in, containing a body replacementTag.</param>
- void EnableTemplate(String html = null);
-
- /// <summary>
- /// Automatically sends a blind carbon copy to an address for every e-mail sent, without
- /// adding that address to the header.
- /// </summary>
- /// <param name="email">A single email recipient</param>
- void EnableBcc(String email = null);
-
- /// <summary>
- /// Enabing this app will bypass the normal unsubscribe / bounce / spam report checks
- /// and queue the e-mail for delivery.
- /// </summary>
- void EnableBypassListManagement();
- #endregion
- }
-}
+ /// <summary>
+ /// Represents the basic set of functions that will be called by the user
+ /// includes basic message data manipulation and filter settings
+ /// </summary>
+ public interface ISendGrid
+ {
+ #region Properties
+
+ MailAddress From { get; set; }
+ MailAddress[] To { get; set; }
+ MailAddress[] Cc { get; }
+ MailAddress[] Bcc { get; }
+ MailAddress[] ReplyTo { get; set; }
+ Dictionary<String, MemoryStream> StreamedAttachments { get; set; }
+ String[] Attachments { get; set; }
+ String Subject { get; set; }
+ Dictionary<String, String> Headers { get; set; }
+ IHeader Header { get; set; }
+ String Html { get; set; }
+ String Text { get; set; }
+
+ #endregion
+
+ #region Interface for ITransport
+
+ /// <summary>
+ /// Used by the Transport object to create a MIME for SMTP
+ /// </summary>
+ /// <returns>MIME to be sent</returns>
+ MailMessage CreateMimeMessage();
+
+ #endregion
+
+ #region Methods for setting data
+
+ /// <summary>
+ /// Add to the 'To' address.
+ /// </summary>
+ /// <param name="address">single string eg. 'you@company.com'</param>
+ void AddTo(String address);
+
+ /// <summary>
+ /// Add to the 'To' address.
+ /// </summary>
+ /// <param name="addresses">list of email addresses as strings</param>
+ void AddTo(IEnumerable<String> addresses);
+
+ /// <summary>
+ /// Add to the 'To' address.
+ /// </summary>
+ /// <param name="addresssInfo">
+ /// the dictionary keys are the email addresses, which points to a dictionary of
+ /// key substitutionValues pairs mapping to other address codes, such as { foo@bar.com => { 'DisplayName' => 'Mr Foo' }
+ /// }
+ /// </param>
+ void AddTo(IDictionary<String, IDictionary<String, String>> addresssInfo);
+
+ /// <summary>
+ /// Add to the 'CC' address.
+ /// </summary>
+ /// <param name="address">a single email address eg "you@company.com"</param>
+ void AddCc(String address);
+
+ /// <summary>
+ /// Add to the 'CC' address.
+ /// </summary>
+ /// <param name="addresses">a list of email addresses as strings</param>
+ void AddCc(IEnumerable<String> addresses);
+
+ /// <summary>
+ /// Add to the 'CC' address.
+ /// </summary>
+ /// <param name="addresssInfo">
+ /// the dictionary keys are the email addresses, which points to a dictionary of
+ /// key substitutionValues pairs mapping to other address codes, such as { foo@bar.com => { 'DisplayName' => 'Mr Foo' }
+ /// }
+ /// </param>
+ void AddCc(IDictionary<String, IDictionary<String, String>> addresssInfo);
+
+ /// <summary>
+ /// Add to the 'Bcc' address.
+ /// </summary>
+ /// <param name="address">a single email as the input eg "you@company.com"</param>
+ void AddBcc(String address);
+
+ /// <summary>
+ /// Add to the 'Bcc' address.
+ /// </summary>
+ /// <param name="addresses">a list of emails as an array of strings.</param>
+ void AddBcc(IEnumerable<String> addresses);
+
+ /// <summary>
+ /// Add to the 'Bcc' address.
+ /// </summary>
+ /// <param name="addresssInfo">
+ /// the dictionary keys are the email addresses, which points to a dictionary of
+ /// key substitutionValues pairs mapping to other address codes, such as { foo@bar.com => { 'DisplayName' => 'Mr Foo' }
+ /// }
+ /// </param>
+ void AddBcc(IDictionary<String, IDictionary<String, String>> addresssInfo);
+
+ /// <summary>
+ /// Defines a mapping between a replacement string in the text of the message to a list of
+ /// substitution values to be used, one per each recipient, in the same order as the recipients were added.
+ /// </summary>
+ /// <param name="replacementTag">the string in the email that you'll replace eg. '-name-'</param>
+ /// <param name="substitutionValues">
+ /// a list of values that will be substituted in for the replacementTag, one for each
+ /// recipient
+ /// </param>
+ void AddSubstitution(String replacementTag, List<String> substitutionValues);
+
+ /// <summary>
+ /// This adds parameters and values that will be bassed back through SendGrid's
+ /// Event API if an event notification is triggered by this email.
+ /// </summary>
+ /// <param name="identifiers">parameter substitutionValues pairs to be passed back on event notification</param>
+ void AddUniqueArgs(IDictionary<String, String> identifiers);
+
+ /// <summary>
+ /// This sets the category for this email. Statistics are stored on a per category
+ /// basis, so this can be useful for tracking on a per group basis.
+ /// </summary>
+ /// <param name="category">categories applied to the message</param>
+ void SetCategory(String category);
+
+ /// <summary>
+ /// This sets the categories for this email. Statistics are stored on a per category
+ /// basis, so this can be useful for tracking on a per group basis.
+ /// </summary>
+ /// <param name="categories">categories applied to the message</param>
+ void SetCategories(IEnumerable<String> categories);
+
+ /// <summary>
+ /// Add an attachment to the message.
+ /// </summary>
+ /// <param name="filePath">a fully qualified file path as a string</param>
+ void AddAttachment(String filePath);
+
+ /// <summary>
+ /// Add a stream as an attachment to the message
+ /// </summary>
+ /// <param name="stream">Stream of file to be attached</param>
+ /// <param name="name">Name of file to be attached</param>
+ void AddAttachment(Stream stream, String name);
+
+ /// <summary>
+ /// GetRecipients returns a list of all the recepients by retrieving the to, cc, and bcc lists.
+ /// </summary>
+ /// <returns></returns>
+ IEnumerable<String> GetRecipients();
+
+ /// <summary>
+ /// Add custom headers to the message
+ /// </summary>
+ /// <param name="headers">key substitutionValues pairs</param>
+ void AddHeaders(IDictionary<String, String> headers);
+
+ #endregion
+
+ #region SMTP API Functions
+
+ /// <summary>
+ /// Disable the gravatar app
+ /// </summary>
+ void DisableGravatar();
+
+ /// <summary>
+ /// Disable the open tracking app
+ /// </summary>
+ void DisableOpenTracking();
+
+ /// <summary>
+ /// Disable the click tracking app
+ /// </summary>
+ void DisableClickTracking();
+
+ /// <summary>
+ /// Disable the spam check
+ /// </summary>
+ void DisableSpamCheck();
+
+ /// <summary>
+ /// Disable the unsubscribe app
+ /// </summary>
+ void DisableUnsubscribe();
+
+ /// <summary>
+ /// Disable the footer app
+ /// </summary>
+ void DisableFooter();
+
+ /// <summary>
+ /// Disable the Google Analytics app
+ /// </summary>
+ void DisableGoogleAnalytics();
+
+ /// <summary>
+ /// Disable the templates app
+ /// </summary>
+ void DisableTemplate();
+
+ /// <summary>
+ /// Disable Bcc app
+ /// </summary>
+ void DisableBcc();
+
+ /// <summary>
+ /// Disable the Bypass List Management app
+ /// </summary>
+ void DisableBypassListManagement();
+
+ /// <summary>
+ /// Inserts the gravatar image of the sender to the bottom of the message
+ /// </summary>
+ void EnableGravatar();
+
+ /// <summary>
+ /// Adds an invisible image to the end of the email which can track e-mail opens.
+ /// </summary>
+ void EnableOpenTracking();
+
+ /// <summary>
+ /// Causes all links to be overwritten, shortened, and pointed to SendGrid's servers so clicks will be tracked.
+ /// </summary>
+ /// <param name="includePlainText">true if links found in plain text portions of the message are to be overwritten</param>
+ void EnableClickTracking(bool includePlainText = false);
+
+ /// <summary>
+ /// Provides notification when emails are deteched that exceed a predefined spam threshold.
+ /// </summary>
+ /// <param name="score">
+ /// Emails with a SpamAssassin score over this substitutionValues will be considered spam and not be
+ /// delivered.
+ /// </param>
+ /// <param name="url">SendGrid will send an HTTP POST request to this url when a message is detected as spam</param>
+ void EnableSpamCheck(int score = 5, String url = null);
+
+ /// <summary>
+ /// Allow's SendGrid to manage unsubscribes and ensure these users don't get future emails from the sender
+ /// </summary>
+ /// <param name="text">String for the plain text email body showing what you want the message to look like.</param>
+ /// <param name="html">String for the HTML email body showing what you want the message to look like.</param>
+ void EnableUnsubscribe(String text, String html);
+
+ /// <summary>
+ /// Allow's SendGrid to manage unsubscribes and ensure these users don't get future emails from the sender
+ /// </summary>
+ /// <param name="replace">Tag in the message body to be replaced with the unsubscribe link and message</param>
+ void EnableUnsubscribe(String replace);
+
+ /// <summary>
+ /// Attaches a message at the footer of the email
+ /// </summary>
+ /// <param name="text">Message for the plain text body of the email</param>
+ /// <param name="html">Message for the HTML body of the email</param>
+ void EnableFooter(String text = null, String html = null);
+
+ /// <summary>
+ /// Re-writes links to integrate with Google Analytics
+ /// </summary>
+ /// <param name="source">Name of the referrer source (e.g. Google, SomeDomain.com, NewsletterA)</param>
+ /// <param name="medium">Name of the marketing medium (e.g. Email)</param>
+ /// <param name="term">Identify paid keywords</param>
+ /// <param name="content">Use to differentiate ads</param>
+ /// <param name="campaign">Name of the campaign</param>
+ void EnableGoogleAnalytics(String source, String medium, String term, String content = null, String campaign = null);
+
+ /// <summary>
+ /// Wraps an HTML template around your email content.
+ /// </summary>
+ /// <param name="html">HTML that your emails will be wrapped in, containing a body replacementTag.</param>
+ void EnableTemplate(String html = null);
+
+ /// <summary>
+ /// Automatically sends a blind carbon copy to an address for every e-mail sent, without
+ /// adding that address to the header.
+ /// </summary>
+ /// <param name="email">A single email recipient</param>
+ void EnableBcc(String email = null);
+
+ /// <summary>
+ /// Enabing this app will bypass the normal unsubscribe / bounce / spam report checks
+ /// and queue the e-mail for delivery.
+ /// </summary>
+ void EnableBypassListManagement();
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/SendGrid/SendGridMail/Mail.csproj b/SendGrid/SendGridMail/Mail.csproj
index a41ecbe..9d3c35f 100644
--- a/SendGrid/SendGridMail/Mail.csproj
+++ b/SendGrid/SendGridMail/Mail.csproj
@@ -13,6 +13,8 @@
<FileAlignment>512</FileAlignment>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile />
+ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
+ <RestorePackages>true</RestorePackages>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
@@ -82,6 +84,7 @@
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.10\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
</Target>
+ <Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
diff --git a/SendGrid/SendGridMail/Properties/AssemblyInfo.cs b/SendGrid/SendGridMail/Properties/AssemblyInfo.cs
index 5ae1b77..f47a2ad 100644
--- a/SendGrid/SendGridMail/Properties/AssemblyInfo.cs
+++ b/SendGrid/SendGridMail/Properties/AssemblyInfo.cs
@@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
+
[assembly: AssemblyTitle("SendGridMail")]
[assembly: AssemblyDescription("A client library for interfacing with the SendGrid API")]
[assembly: AssemblyConfiguration("")]
@@ -17,19 +18,20 @@ using System.Runtime.InteropServices;
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
+
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("193fa200-8430-4206-aacd-2d2bb2dfa6cf")]
+[assembly: Guid("193fa200-8430-4206-aacd-2d2bb2dfa6cf")]
[assembly: InternalsVisibleTo("Tests")]
-[assembly:InternalsVisibleTo("DynamicProxyGenAssembly2," +
-"1310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b" +
-"PublicKey=002400000480000094000000060200000024000052534" +
-"3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d926665" +
-"4753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb" +
-"4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c486" +
-"1eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2," +
+ "1310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b" +
+ "PublicKey=002400000480000094000000060200000024000052534" +
+ "3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d926665" +
+ "4753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb" +
+ "4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c486" +
+ "1eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
// Version information for an assembly consists of the following four values:
//
@@ -41,5 +43,6 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
+
[assembly: AssemblyVersion("2.1.1")]
-[assembly: AssemblyFileVersion("2.1.1")]
+[assembly: AssemblyFileVersion("2.1.1")] \ No newline at end of file
diff --git a/SendGrid/SendGridMail/SendGrid.cs b/SendGrid/SendGridMail/SendGrid.cs
index f551328..cc87ffd 100644
--- a/SendGrid/SendGridMail/SendGrid.cs
+++ b/SendGrid/SendGridMail/SendGrid.cs
@@ -3,559 +3,523 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
-using System.Net;
using System.Net.Mail;
using System.Net.Mime;
+using System.Text.RegularExpressions;
using Smtpapi;
namespace SendGridMail
{
- public class SendGrid : ISendGrid
- {
- #region constants/vars
- //private/constant vars:
- private static readonly Dictionary<String, String> Filters = InitializeFilters();
- private MailMessage message;
-
- // TODO find appropriate types for these
- const string encoding = "quoted-printable";
- const string charset = "utf-8";
-
- //apps list and settings
- private const String ReText = @"<\%\s*\%>";
- private const String ReHtml = @"<\%\s*[^\s]+\s*\%>";
- #endregion
-
- #region Initialization and Constructors
- /// <summary>
- /// Creates an instance of SendGrid's custom message object
- /// </summary>
- /// <returns></returns>
- public static SendGrid GetInstance()
- {
- var header = new Header();
- return new SendGrid(header);
- }
-
- /// <summary>
- /// Creates an instance of SendGrid's custom message object with mail parameters
- /// </summary>
- /// <param name="from">The email address of the sender</param>
- /// <param name="to">An array of the recipients</param>
- /// <param name="cc">Supported over SMTP, with future plans for support in the Web transport</param>
- /// <param name="bcc">Blind recipients</param>
- /// <param name="subject">The subject of the message</param>
- /// <param name="html">the html content for the message</param>
- /// <param name="text">the plain text part of the message</param>
- /// <param name="transport">Transport class to use for sending the message</param>
- /// <returns></returns>
- public static SendGrid GetInstance(MailAddress from, MailAddress[] to, MailAddress[] cc, MailAddress[] bcc,
- String subject, String html, String text)
- {
- var header = new Header();
- return new SendGrid(from, to, cc, bcc, subject, html, text, header);
- }
-
- internal SendGrid(MailAddress from, MailAddress[] to, MailAddress[] cc, MailAddress[] bcc,
- String subject, String html, String text, IHeader header = null ) : this(header)
- {
- From = from;
- To = to;
- Cc = cc;
- Bcc = bcc;
-
- message.Subject = subject;
-
- Text = text;
- Html = html;
- }
-
- internal SendGrid(IHeader header)
- {
- message = new MailMessage();
- Header = header;
- Headers = new Dictionary<string, string>();
- }
-
- private static Dictionary<string, string> InitializeFilters()
- {
- return
- new Dictionary<string, string>
- {
- {"Gravatar", "gravatar"},
- {"OpenTracking", "opentrack"},
- {"ClickTracking", "clicktrack"},
- {"SpamCheck", "spamcheck"},
- {"Unsubscribe", "subscriptiontrack"},
- {"Footer", "footer"},
- {"GoogleAnalytics", "ganalytics"},
- {"Template", "template"},
- {"Bcc", "bcc"},
- {"BypassListManagement", "bypass_list_management"}
- };
- }
- #endregion
-
- #region Properties
- public MailAddress From
- {
- get
- {
- return message.From;
- }
- set
- {
- if (value != null) message.From = value;
- }
- }
-
- public MailAddress[] ReplyTo
- {
- get
- {
- return message.ReplyToList.ToArray();
- }
- set
- {
- message.ReplyToList.Clear();
- foreach (var replyTo in value)
- {
- message.ReplyToList.Add(replyTo);
- }
- }
- }
-
- public MailAddress[] To
- {
- get
- {
- return message.To.ToArray();
- }
- set
- {
- message.To.Clear();
- foreach (var mailAddress in value)
- {
- message.To.Add(mailAddress);
- }
- }
- }
-
- public MailAddress[] Cc
- {
- get
- {
- return message.CC.ToArray();
- }
- set
- {
- message.CC.Clear();
- foreach (var mailAddress in value)
- {
- message.CC.Add(mailAddress);
- }
- }
- }
-
- public MailAddress[] Bcc
- {
- get
- {
- return message.Bcc.ToArray();
- }
- set
- {
- message.Bcc.Clear();
- foreach (var mailAddress in value)
- {
- message.Bcc.Add(mailAddress);
- }
- }
- }
-
- public String Subject
- {
- get
- {
- return message.Subject;
- }
- set
- {
- if (value != null) message.Subject = value;
- }
- }
-
- public Dictionary<String, String> Headers { get; set; }
- public IHeader Header { get; set; }
- public String Html { get; set; }
- public String Text { get; set; }
- #endregion
-
- #region Methods for setting data
- public void AddTo(String address)
- {
- var mailAddress = new MailAddress(address);
- message.To.Add(mailAddress);
- }
-
- public void AddTo(IEnumerable<String> addresses)
- {
- if (addresses != null)
- {
- foreach (var address in addresses)
- {
- if (address != null) AddTo(address);
- }
- }
- }
-
- public void AddTo(IDictionary<String, IDictionary<String, String>> addresssInfo)
- {
- foreach (var address in addresssInfo.Keys)
- {
- var table = addresssInfo[address];
- //DisplayName is the only option that this implementation of MailAddress implements.
- var mailAddress = new MailAddress(address, table.ContainsKey("DisplayName") ? table["DisplayName"] : null);
- message.To.Add(mailAddress);
- }
- }
-
- public void AddCc(String address)
- {
- var mailAddress = new MailAddress(address);
- message.CC.Add(mailAddress);
- }
-
- public void AddCc(IEnumerable<String> addresses)
- {
- if (addresses != null)
- {
- foreach (var address in addresses)
- {
- if (address != null) AddCc(address);
- }
- }
- }
-
- public void AddCc(IDictionary<String, IDictionary<String, String>> addresssInfo)
- {
- foreach (var address in addresssInfo.Keys)
- {
- var table = addresssInfo[address];
- //DisplayName is the only option that this implementation of MailAddress implements.
- var mailAddress = new MailAddress(address, table.ContainsKey("DisplayName") ? table["DisplayName"] : null);
- message.CC.Add(mailAddress);
- }
- }
-
- public void AddBcc(String address)
- {
- var mailAddress = new MailAddress(address);
- message.Bcc.Add(mailAddress);
- }
-
- public void AddBcc(IEnumerable<String> addresses)
- {
- if (addresses != null)
- {
- foreach (var address in addresses)
- {
- if (address != null) AddBcc(address);
- }
- }
- }
-
- public void AddBcc(IDictionary<String, IDictionary<String, String>> addresssInfo)
- {
- foreach (var address in addresssInfo.Keys)
- {
- var table = addresssInfo[address];
- //DisplayName is the only option that this implementation of MailAddress implements.
- var mailAddress = new MailAddress(address, table.ContainsKey("DisplayName") ? table["DisplayName"] : null);
- message.Bcc.Add(mailAddress);
- }
- }
-
- private Dictionary<String, MemoryStream> _streamedAttachments = new Dictionary<string, MemoryStream>();
- public Dictionary<String, MemoryStream> StreamedAttachments
- {
- get { return _streamedAttachments; }
- set { _streamedAttachments = value; }
- }
-
- private List<String> _attachments = new List<String>();
- public String[] Attachments
- {
- get { return _attachments.ToArray(); }
- set { _attachments = value.ToList(); }
- }
-
- public void AddSubstitution(String replacementTag, List<String> substitutionValues)
- {
- //let the system complain if they do something bad, since the function returns null
- Header.AddSubstitution(replacementTag, substitutionValues);
- }
-
- public void AddUniqueArgs(IDictionary<String, String> identifiers)
- {
- Header.AddUniqueArgs(identifiers);
- }
-
- public void SetCategory(String category)
- {
- Header.SetCategory(category);
- }
-
- public void SetCategories(IEnumerable<string> categories)
- {
- Header.SetCategories(categories);
- }
-
- public void AddAttachment(Stream stream, String name)
- {
- MemoryStream ms = new MemoryStream();
- stream.CopyTo(ms);
- ms.Seek(0,SeekOrigin.Begin);
- StreamedAttachments[name] = ms;
- }
-
- public void AddAttachment(String filePath)
- {
- _attachments.Add(filePath);
- }
-
- public IEnumerable<String> GetRecipients()
- {
- List<MailAddress> tos = message.To.ToList();
- List<MailAddress> ccs = message.CC.ToList();
- List<MailAddress> bccs = message.Bcc.ToList();
-
- var rcpts = tos.Union(ccs.Union(bccs)).Select(address => address.Address);
- return rcpts;
- }
-
- public void AddHeaders(IDictionary<string, string> headers)
- {
- headers.Keys.ToList().ForEach(key => Headers[key] = headers[key]);
- }
- #endregion
-
- #region SMTP API Functions
- public void DisableGravatar()
- {
- Header.DisableFilter(Filters["Gravatar"]);
- }
-
- public void DisableOpenTracking()
- {
- Header.DisableFilter(Filters["OpenTracking"]);
- }
-
- public void DisableClickTracking()
- {
- Header.DisableFilter(Filters["ClickTracking"]);
- }
-
- public void DisableSpamCheck()
- {
- Header.DisableFilter(Filters["SpamCheck"]);
- }
-
- public void DisableUnsubscribe()
- {
- Header.DisableFilter(Filters["Unsubscribe"]);
- }
-
- public void DisableFooter()
- {
- Header.DisableFilter(Filters["Footer"]);
- }
-
- public void DisableGoogleAnalytics()
- {
- Header.DisableFilter(Filters["GoogleAnalytics"]);
- }
-
- public void DisableTemplate()
- {
- Header.DisableFilter(Filters["Template"]);
- }
-
- public void DisableBcc()
- {
- Header.DisableFilter(Filters["Bcc"]);
- }
-
- public void DisableBypassListManagement()
- {
- Header.DisableFilter(Filters["BypassListManagement"]);
- }
-
- public void EnableGravatar()
- {
- Header.EnableFilter(Filters["Gravatar"]);
- }
-
- public void EnableOpenTracking()
- {
- Header.EnableFilter(Filters["OpenTracking"]);
- }
-
- public void EnableClickTracking(bool includePlainText = false)
- {
- var filter = Filters["ClickTracking"];
-
- Header.EnableFilter(filter);
- if (includePlainText)
- {
- Header.AddFilterSetting(filter, new List<string> { "enable_text" }, "1");
- }
- }
-
- public void EnableSpamCheck(int score = 5, string url = null)
- {
- var filter = Filters["SpamCheck"];
-
- Header.EnableFilter(filter);
- Header.AddFilterSetting(filter, new List<string> { "maxscore" }, score.ToString(CultureInfo.InvariantCulture));
- Header.AddFilterSetting(filter, new List<string> { "url" }, url);
- }
-
- public void EnableUnsubscribe(string text, string html)
- {
- var filter = Filters["Unsubscribe"];
-
- if(!System.Text.RegularExpressions.Regex.IsMatch(text, ReText))
- {
- throw new Exception("Missing substitution replacementTag in text");
- }
-
- if(!System.Text.RegularExpressions.Regex.IsMatch(html, ReHtml))
- {
- throw new Exception("Missing substitution replacementTag in html");
- }
-
- Header.EnableFilter(filter);
- Header.AddFilterSetting(filter, new List<string> { "text/plain" }, text);
- Header.AddFilterSetting(filter, new List<string> {"text/html"}, html);
- }
-
- public void EnableUnsubscribe(string replace)
- {
- var filter = Filters["Unsubscribe"];
-
- Header.EnableFilter(filter);
- Header.AddFilterSetting(filter, new List<string> { "replace" }, replace);
- }
-
- public void EnableFooter(string text = null, string html = null)
- {
- var filter = Filters["Footer"];
-
- Header.EnableFilter(filter);
- Header.AddFilterSetting(filter, new List<string> { "text/plain" }, text);
- Header.AddFilterSetting(filter, new List<string> { "text/html" }, html);
- }
-
- public void EnableGoogleAnalytics(string source, string medium, string term, string content = null, string campaign = null)
- {
- var filter = Filters["GoogleAnalytics"];
-
- Header.EnableFilter(filter);
- Header.AddFilterSetting(filter, new List<string> { "utm_source" }, source);
- Header.AddFilterSetting(filter, new List<string> { "utm_medium" }, medium);
- Header.AddFilterSetting(filter, new List<string> { "utm_term" }, term);
- Header.AddFilterSetting(filter, new List<string> { "utm_content" }, content);
- Header.AddFilterSetting(filter, new List<string> { "utm_campaign" }, campaign);
- }
-
- public void EnableTemplate(string html)
- {
- var filter = Filters["Template"];
-
- if (!System.Text.RegularExpressions.Regex.IsMatch(html, ReHtml))
- {
- throw new Exception("Missing substitution replacementTag in html");
- }
-
- Header.EnableFilter(filter);
- Header.AddFilterSetting(filter, new List<string> { "text/html" }, html);
- }
-
- public void EnableBcc(string email)
- {
- var filter = Filters["Bcc"];
-
- Header.EnableFilter(filter);
- Header.AddFilterSetting(filter, new List<string> { "email" }, email);
- }
-
- public void EnableBypassListManagement()
- {
- Header.EnableFilter(Filters["BypassListManagement"]);
- }
- #endregion
-
- public MailMessage CreateMimeMessage()
- {
- String smtpapi = Header.JsonString();
-
- if (!String.IsNullOrEmpty(smtpapi))
- message.Headers.Add("X-Smtpapi", smtpapi);
-
- Headers.Keys.ToList().ForEach(k => message.Headers.Add(k, Headers[k]));
-
- message.Attachments.Clear();
- message.AlternateViews.Clear();
-
- if(Attachments != null)
- {
- foreach (var attachment in Attachments)
- {
- message.Attachments.Add(new Attachment(attachment, MediaTypeNames.Application.Octet));
- }
- }
-
- if(StreamedAttachments != null)
- {
- foreach (var attachment in StreamedAttachments)
- {
- attachment.Value.Position = 0;
- message.Attachments.Add(new Attachment(attachment.Value, attachment.Key));
- }
- }
-
- if (Text != null)
- {
- AlternateView plainView = AlternateView.CreateAlternateViewFromString(Text, null, "text/plain");
- message.AlternateViews.Add(plainView);
- }
-
- if (Html != null)
- {
- AlternateView htmlView = AlternateView.CreateAlternateViewFromString(Html, null, "text/html");
- message.AlternateViews.Add(htmlView);
- }
-
- //message.SubjectEncoding = Encoding.GetEncoding(charset);
- //message.BodyEncoding = Encoding.GetEncoding(charset);
-
- return message;
- }
-
- /// <summary>
- /// Helper function lets us look at the mime before it is sent
- /// </summary>
- /// <param name="directory">directory in which we store this mime message</param>
- internal void SaveMessage(String directory)
- {
- var client = new SmtpClient("localhost")
- {
- DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory,
- PickupDirectoryLocation = @"C:\temp"
- };
- var msg = CreateMimeMessage();
- client.Send(msg);
- }
- }
-}
+ public class SendGrid : ISendGrid
+ {
+ #region constants/vars
+
+ //apps list and settings
+ private const String ReText = @"<\%\s*\%>";
+ private const String ReHtml = @"<\%\s*[^\s]+\s*\%>";
+ private static readonly Dictionary<String, String> Filters = InitializeFilters();
+ private readonly MailMessage _message;
+
+ #endregion
+
+ #region Initialization and Constructors
+
+ internal SendGrid(MailAddress from, MailAddress[] to, MailAddress[] cc, MailAddress[] bcc,
+ String subject, String html, String text, IHeader header = null) : this(header)
+ {
+ From = from;
+ To = to;
+ Cc = cc;
+ Bcc = bcc;
+
+ _message.Subject = subject;
+
+ Text = text;
+ Html = html;
+ }
+
+ internal SendGrid(IHeader header)
+ {
+ _message = new MailMessage();
+ Header = header;
+ Headers = new Dictionary<string, string>();
+ }
+
+ /// <summary>
+ /// Creates an instance of SendGrid's custom message object
+ /// </summary>
+ /// <returns></returns>
+ public static SendGrid GetInstance()
+ {
+ var header = new Header();
+ return new SendGrid(header);
+ }
+
+ /// <summary>
+ /// Creates an instance of SendGrid's custom message object with mail parameters
+ /// </summary>
+ /// <param name="from">The email address of the sender</param>
+ /// <param name="to">An array of the recipients</param>
+ /// <param name="cc">Supported over SMTP, with future plans for support in the Web transport</param>
+ /// <param name="bcc">Blind recipients</param>
+ /// <param name="subject">The subject of the message</param>
+ /// <param name="html">the html content for the message</param>
+ /// <param name="text">the plain text part of the message</param>
+ /// <returns></returns>
+ public static SendGrid GetInstance(MailAddress from, MailAddress[] to, MailAddress[] cc, MailAddress[] bcc,
+ String subject, String html, String text)
+ {
+ var header = new Header();
+ return new SendGrid(from, to, cc, bcc, subject, html, text, header);
+ }
+
+ private static Dictionary<string, string> InitializeFilters()
+ {
+ return
+ new Dictionary<string, string>
+ {
+ {"Gravatar", "gravatar"},
+ {"OpenTracking", "opentrack"},
+ {"ClickTracking", "clicktrack"},
+ {"SpamCheck", "spamcheck"},
+ {"Unsubscribe", "subscriptiontrack"},
+ {"Footer", "footer"},
+ {"GoogleAnalytics", "ganalytics"},
+ {"Template", "template"},
+ {"Bcc", "bcc"},
+ {"BypassListManagement", "bypass_list_management"}
+ };
+ }
+
+ #endregion
+
+ #region Properties
+
+ public MailAddress From
+ {
+ get { return _message.From; }
+ set { if (value != null) _message.From = value; }
+ }
+
+ public MailAddress[] ReplyTo
+ {
+ get { return _message.ReplyToList.ToArray(); }
+ set
+ {
+ _message.ReplyToList.Clear();
+ foreach (var replyTo in value)
+ {
+ _message.ReplyToList.Add(replyTo);
+ }
+ }
+ }
+
+ public MailAddress[] To
+ {
+ get { return _message.To.ToArray(); }
+ set
+ {
+ _message.To.Clear();
+ foreach (var mailAddress in value)
+ {
+ _message.To.Add(mailAddress);
+ }
+ }
+ }
+
+ public MailAddress[] Cc
+ {
+ get { return _message.CC.ToArray(); }
+ set
+ {
+ _message.CC.Clear();
+ foreach (var mailAddress in value)
+ {
+ _message.CC.Add(mailAddress);
+ }
+ }
+ }
+
+ public MailAddress[] Bcc
+ {
+ get { return _message.Bcc.ToArray(); }
+ set
+ {
+ _message.Bcc.Clear();
+ foreach (var mailAddress in value)
+ {
+ _message.Bcc.Add(mailAddress);
+ }
+ }
+ }
+
+ public String Subject
+ {
+ get { return _message.Subject; }
+ set { if (value != null) _message.Subject = value; }
+ }
+
+ public Dictionary<String, String> Headers { get; set; }
+ public IHeader Header { get; set; }
+ public String Html { get; set; }
+ public String Text { get; set; }
+
+ #endregion
+
+ #region Methods for setting data
+
+ private List<String> _attachments = new List<String>();
+ private Dictionary<String, MemoryStream> _streamedAttachments = new Dictionary<string, MemoryStream>();
+
+ public void AddTo(String address)
+ {
+ var mailAddress = new MailAddress(address);
+ _message.To.Add(mailAddress);
+ }
+
+ public void AddTo(IEnumerable<String> addresses)
+ {
+ if (addresses == null) return;
+
+ foreach (var address in addresses.Where(address => address != null))
+ AddTo(address);
+ }
+
+ public void AddTo(IDictionary<String, IDictionary<String, String>> addresssInfo)
+ {
+ foreach (var mailAddress in from address in addresssInfo.Keys let table = addresssInfo[address] select new MailAddress(address, table.ContainsKey("DisplayName") ? table["DisplayName"] : null))
+ {
+ _message.To.Add(mailAddress);
+ }
+ }
+
+ public void AddCc(String address)
+ {
+ var mailAddress = new MailAddress(address);
+ _message.CC.Add(mailAddress);
+ }
+
+ public void AddCc(IEnumerable<String> addresses)
+ {
+ if (addresses == null) return;
+ foreach (var address in addresses.Where(address => address != null))
+ {
+ AddCc(address);
+ }
+ }
+
+ public void AddCc(IDictionary<String, IDictionary<String, String>> addresssInfo)
+ {
+ foreach (var mailAddress in from address in addresssInfo.Keys let table = addresssInfo[address] select new MailAddress(address, table.ContainsKey("DisplayName") ? table["DisplayName"] : null))
+ {
+ _message.CC.Add(mailAddress);
+ }
+ }
+
+ public void AddBcc(String address)
+ {
+ var mailAddress = new MailAddress(address);
+ _message.Bcc.Add(mailAddress);
+ }
+
+ public void AddBcc(IEnumerable<String> addresses)
+ {
+ if (addresses == null) return;
+ foreach (var address in addresses.Where(address => address != null))
+ {
+ AddBcc(address);
+ }
+ }
+
+ public void AddBcc(IDictionary<String, IDictionary<String, String>> addresssInfo)
+ {
+ foreach (var mailAddress in from address in addresssInfo.Keys let table = addresssInfo[address] select new MailAddress(address, table.ContainsKey("DisplayName") ? table["DisplayName"] : null))
+ {
+ _message.Bcc.Add(mailAddress);
+ }
+ }
+
+ public Dictionary<String, MemoryStream> StreamedAttachments
+ {
+ get { return _streamedAttachments; }
+ set { _streamedAttachments = value; }
+ }
+
+ public String[] Attachments
+ {
+ get { return _attachments.ToArray(); }
+ set { _attachments = value.ToList(); }
+ }
+
+ public void AddSubstitution(String replacementTag, List<String> substitutionValues)
+ {
+ //let the system complain if they do something bad, since the function returns null
+ Header.AddSubstitution(replacementTag, substitutionValues);
+ }
+
+ public void AddUniqueArgs(IDictionary<String, String> identifiers)
+ {
+ Header.AddUniqueArgs(identifiers);
+ }
+
+ public void SetCategory(String category)
+ {
+ Header.SetCategory(category);
+ }
+
+ public void SetCategories(IEnumerable<string> categories)
+ {
+ Header.SetCategories(categories);
+ }
+
+ public void AddAttachment(Stream stream, String name)
+ {
+ var ms = new MemoryStream();
+ stream.CopyTo(ms);
+ ms.Seek(0, SeekOrigin.Begin);
+ StreamedAttachments[name] = ms;
+ }
+
+ public void AddAttachment(String filePath)
+ {
+ _attachments.Add(filePath);
+ }
+
+ public IEnumerable<String> GetRecipients()
+ {
+ var tos = _message.To.ToList();
+ var ccs = _message.CC.ToList();
+ var bccs = _message.Bcc.ToList();
+
+ var rcpts = tos.Union(ccs.Union(bccs)).Select(address => address.Address);
+ return rcpts;
+ }
+
+ public void AddHeaders(IDictionary<string, string> headers)
+ {
+ headers.Keys.ToList().ForEach(key => Headers[key] = headers[key]);
+ }
+
+ #endregion
+
+ #region SMTP API Functions
+
+ public void DisableGravatar()
+ {
+ Header.DisableFilter(Filters["Gravatar"]);
+ }
+
+ public void DisableOpenTracking()
+ {
+ Header.DisableFilter(Filters["OpenTracking"]);
+ }
+
+ public void DisableClickTracking()
+ {
+ Header.DisableFilter(Filters["ClickTracking"]);
+ }
+
+ public void DisableSpamCheck()
+ {
+ Header.DisableFilter(Filters["SpamCheck"]);
+ }
+
+ public void DisableUnsubscribe()
+ {
+ Header.DisableFilter(Filters["Unsubscribe"]);
+ }
+
+ public void DisableFooter()
+ {
+ Header.DisableFilter(Filters["Footer"]);
+ }
+
+ public void DisableGoogleAnalytics()
+ {
+ Header.DisableFilter(Filters["GoogleAnalytics"]);
+ }
+
+ public void DisableTemplate()
+ {
+ Header.DisableFilter(Filters["Template"]);
+ }
+
+ public void DisableBcc()
+ {
+ Header.DisableFilter(Filters["Bcc"]);
+ }
+
+ public void DisableBypassListManagement()
+ {
+ Header.DisableFilter(Filters["BypassListManagement"]);
+ }
+
+ public void EnableGravatar()
+ {
+ Header.EnableFilter(Filters["Gravatar"]);
+ }
+
+ public void EnableOpenTracking()
+ {
+ Header.EnableFilter(Filters["OpenTracking"]);
+ }
+
+ public void EnableClickTracking(bool includePlainText = false)
+ {
+ var filter = Filters["ClickTracking"];
+
+ Header.EnableFilter(filter);
+ if (includePlainText)
+ {
+ Header.AddFilterSetting(filter, new List<string> {"enable_text"}, "1");
+ }
+ }
+
+ public void EnableSpamCheck(int score = 5, string url = null)
+ {
+ var filter = Filters["SpamCheck"];
+
+ Header.EnableFilter(filter);
+ Header.AddFilterSetting(filter, new List<string> {"maxscore"}, score.ToString(CultureInfo.InvariantCulture));
+ Header.AddFilterSetting(filter, new List<string> {"url"}, url);
+ }
+
+ public void EnableUnsubscribe(string text, string html)
+ {
+ var filter = Filters["Unsubscribe"];
+
+ if (!Regex.IsMatch(text, ReText))
+ {
+ throw new Exception("Missing substitution replacementTag in text");
+ }
+
+ if (!Regex.IsMatch(html, ReHtml))
+ {
+ throw new Exception("Missing substitution replacementTag in html");
+ }
+
+ Header.EnableFilter(filter);
+ Header.AddFilterSetting(filter, new List<string> {"text/plain"}, text);
+ Header.AddFilterSetting(filter, new List<string> {"text/html"}, html);
+ }
+
+ public void EnableUnsubscribe(string replace)
+ {
+ var filter = Filters["Unsubscribe"];
+
+ Header.EnableFilter(filter);
+ Header.AddFilterSetting(filter, new List<string> {"replace"}, replace);
+ }
+
+ public void EnableFooter(string text = null, string html = null)
+ {
+ var filter = Filters["Footer"];
+
+ Header.EnableFilter(filter);
+ Header.AddFilterSetting(filter, new List<string> {"text/plain"}, text);
+ Header.AddFilterSetting(filter, new List<string> {"text/html"}, html);
+ }
+
+ public void EnableGoogleAnalytics(string source, string medium, string term, string content = null,
+ string campaign = null)
+ {
+ var filter = Filters["GoogleAnalytics"];
+
+ Header.EnableFilter(filter);
+ Header.AddFilterSetting(filter, new List<string> {"utm_source"}, source);
+ Header.AddFilterSetting(filter, new List<string> {"utm_medium"}, medium);
+ Header.AddFilterSetting(filter, new List<string> {"utm_term"}, term);
+ Header.AddFilterSetting(filter, new List<string> {"utm_content"}, content);
+ Header.AddFilterSetting(filter, new List<string> {"utm_campaign"}, campaign);
+ }
+
+ public void EnableTemplate(string html)
+ {
+ var filter = Filters["Template"];
+
+ if (!Regex.IsMatch(html, ReHtml))
+ {
+ throw new Exception("Missing substitution replacementTag in html");
+ }
+
+ Header.EnableFilter(filter);
+ Header.AddFilterSetting(filter, new List<string> {"text/html"}, html);
+ }
+
+ public void EnableBcc(string email)
+ {
+ var filter = Filters["Bcc"];
+
+ Header.EnableFilter(filter);
+ Header.AddFilterSetting(filter, new List<string> {"email"}, email);
+ }
+
+ public void EnableBypassListManagement()
+ {
+ Header.EnableFilter(Filters["BypassListManagement"]);
+ }
+
+ #endregion
+
+ public MailMessage CreateMimeMessage()
+ {
+ var smtpapi = Header.JsonString();
+
+ if (!String.IsNullOrEmpty(smtpapi))
+ _message.Headers.Add("X-Smtpapi", smtpapi);
+
+ Headers.Keys.ToList().ForEach(k => _message.Headers.Add(k, Headers[k]));
+
+ _message.Attachments.Clear();
+ _message.AlternateViews.Clear();
+
+ if (Attachments != null)
+ {
+ foreach (var attachment in Attachments)
+ {
+ _message.Attachments.Add(new Attachment(attachment, MediaTypeNames.Application.Octet));
+ }
+ }
+
+ if (StreamedAttachments != null)
+ {
+ foreach (var attachment in StreamedAttachments)
+ {
+ attachment.Value.Position = 0;
+ _message.Attachments.Add(new Attachment(attachment.Value, attachment.Key));
+ }
+ }
+
+ if (Text != null)
+ {
+ var plainView = AlternateView.CreateAlternateViewFromString(Text, null, "text/plain");
+ _message.AlternateViews.Add(plainView);
+ }
+
+ if (Html == null) return _message;
+
+ var htmlView = AlternateView.CreateAlternateViewFromString(Html, null, "text/html");
+ _message.AlternateViews.Add(htmlView);
+
+ //message.SubjectEncoding = Encoding.GetEncoding(charset);
+ //message.BodyEncoding = Encoding.GetEncoding(charset);
+
+ return _message;
+ }
+
+ /// <summary>
+ /// Helper function lets us look at the mime before it is sent
+ /// </summary>
+ /// <param name="directory">directory in which we store this mime message</param>
+ internal void SaveMessage(String directory)
+ {
+ var client = new SmtpClient("localhost")
+ {
+ DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory,
+ PickupDirectoryLocation = @"C:\temp"
+ };
+ var msg = CreateMimeMessage();
+ client.Send(msg);
+ }
+ }
+} \ No newline at end of file
diff --git a/SendGrid/SendGridMail/Transport/ITransport.cs b/SendGrid/SendGridMail/Transport/ITransport.cs
index 0394cec..99b620c 100644
--- a/SendGrid/SendGridMail/Transport/ITransport.cs
+++ b/SendGrid/SendGridMail/Transport/ITransport.cs
@@ -1,15 +1,15 @@
namespace SendGridMail
{
- /// <summary>
- /// Encapsulates the transport mechanism so that it can be used in a generic way,
- /// regardless of the transport type
- /// </summary>
- public interface ITransport
- {
- /// <summary>
- /// Delivers a message using the protocol of the derived class
- /// </summary>
- /// <param name="message">the message to be delivered</param>
- void Deliver(ISendGrid message);
- }
+ /// <summary>
+ /// Encapsulates the transport mechanism so that it can be used in a generic way,
+ /// regardless of the transport type
+ /// </summary>
+ public interface ITransport
+ {
+ /// <summary>
+ /// Delivers a message using the protocol of the derived class
+ /// </summary>
+ /// <param name="message">the message to be delivered</param>
+ void Deliver(ISendGrid message);
+ }
} \ No newline at end of file
diff --git a/SendGrid/SendGridMail/Transport/Web.cs b/SendGrid/SendGridMail/Transport/Web.cs
index c99dcf7..f053575 100644
--- a/SendGrid/SendGridMail/Transport/Web.cs
+++ b/SendGrid/SendGridMail/Transport/Web.cs
@@ -3,226 +3,228 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
-using System.Net.Http.Headers;
-using System.Xml;
using System.Net.Http;
+using System.Net.Http.Headers;
using System.Threading.Tasks;
+using System.Xml;
using Smtpapi;
+// ReSharper disable MemberCanBePrivate.Global
namespace SendGridMail
{
- public class Web : ITransport
- {
- #region Properties
+ public class Web : ITransport
+ {
+ #region Properties
+
//TODO: Make this configurable
- public const String BaseUrl = "api.sendgrid.com";
- public const String Endpoint = "/api/mail.send";
- public const String JsonFormat = "json";
- public const String XmlFormat = "xml";
-
- private readonly NetworkCredential _credentials;
- #endregion
-
- /// <summary>
- /// Factory method for Web transport of sendgrid messages
- /// </summary>
- /// <param name="credentials">SendGrid credentials for sending mail messages</param>
- /// <param name="https">Use https?</param>
- /// <returns>New instance of the transport mechanism</returns>
- public static Web GetInstance(NetworkCredential credentials)
- {
- return new Web(credentials);
- }
+ public const String BaseUrl = "api.sendgrid.com";
+ public const String Endpoint = "/api/mail.send";
+
+ private readonly NetworkCredential _credentials;
+
+ #endregion
+
+ /// <summary>
+ /// Creates a new Web interface for sending mail. Preference is using the Factory method.
+ /// </summary>
+ /// <param name="credentials">SendGrid user parameters</param>
+ /// <param name="https">Use https?</param>
+ internal Web(NetworkCredential credentials)
+ {
+ _credentials = credentials;
+ }
+
+ /// <summary>
+ /// Delivers a message over SendGrid's Web interface
+ /// </summary>
+ /// <param name="message"></param>
+ public void Deliver(ISendGrid message)
+ {
+ var client = new HttpClient
+ {
+ BaseAddress = new Uri("https://" + BaseUrl)
+ };
+
+ var content = new MultipartFormDataContent();
+ AttachFormParams(message, content);
+ AttachFiles(message, content);
+ var response = client.PostAsync(Endpoint + ".xml", content).Result;
+ CheckForErrors(response);
+ }
/// <summary>
- /// Creates a new Web interface for sending mail. Preference is using the Factory method.
- /// </summary>
- /// <param name="credentials">SendGrid user parameters</param>
+ /// Factory method for Web transport of sendgrid messages
+ /// </summary>
+ /// <param name="credentials">SendGrid credentials for sending mail messages</param>
/// <param name="https">Use https?</param>
- internal Web(NetworkCredential credentials)
- {
- _credentials = credentials;
- }
-
- /// <summary>
- /// Delivers a message over SendGrid's Web interface
- /// </summary>
- /// <param name="message"></param>
- public void Deliver(ISendGrid message)
- {
- var client = new HttpClient
- {
- BaseAddress = new Uri("https://" + BaseUrl)
- };
-
- var content = new MultipartFormDataContent();
- AttachFormParams(message, content);
- AttachFiles(message, content);
- var response = client.PostAsync(Endpoint + ".xml", content).Result;
- CheckForErrors(response);
- }
-
- /// <summary>
- /// Asynchronously delivers a message over SendGrid's Web interface
- /// </summary>
- /// <param name="message"></param>
- public async Task DeliverAsync(ISendGrid message)
- {
- var client = new HttpClient
- {
- BaseAddress = new Uri("https://" + BaseUrl)
- };
-
- var content = new MultipartFormDataContent();
- AttachFormParams(message, content);
- AttachFiles(message, content);
- var response = await client.PostAsync(Endpoint + ".xml", content);
- await CheckForErrorsAsync(response);
- }
-
- #region Support Methods
- private void AttachFormParams(ISendGrid message, MultipartFormDataContent content)
- {
- var formParams = FetchFormParams(message);
- foreach (var keyValuePair in formParams)
- {
- content.Add(new StringContent(keyValuePair.Value), keyValuePair.Key);
- }
- }
-
- private void AttachFiles(ISendGrid message, MultipartFormDataContent content)
+ /// <returns>New instance of the transport mechanism</returns>
+ public static Web GetInstance(NetworkCredential credentials)
+ {
+ return new Web(credentials);
+ }
+
+ /// <summary>
+ /// Asynchronously delivers a message over SendGrid's Web interface
+ /// </summary>
+ /// <param name="message"></param>
+ public async void DeliverAsync(ISendGrid message)
+ {
+ var client = new HttpClient
+ {
+ BaseAddress = new Uri("https://" + BaseUrl)
+ };
+
+ var content = new MultipartFormDataContent();
+ AttachFormParams(message, content);
+ AttachFiles(message, content);
+ var response = await client.PostAsync(Endpoint + ".xml", content);
+ await CheckForErrorsAsync(response);
+ }
+
+ #region Support Methods
+
+ private void AttachFormParams(ISendGrid message, MultipartFormDataContent content)
+ {
+ var formParams = FetchFormParams(message);
+ foreach (var keyValuePair in formParams)
+ {
+ content.Add(new StringContent(keyValuePair.Value), keyValuePair.Key);
+ }
+ }
+
+ private void AttachFiles(ISendGrid message, MultipartFormDataContent content)
{
- var files = FetchFileBodies (message);
- foreach (var file in files)
- {
- var fs = new FileStream(file.Key, FileMode.Open, FileAccess.Read);
- var fileContent = new StreamContent(fs);
-
- fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
- {
- Name = "files[" + Path.GetFileName(file.Key) + "]",
- FileName = Path.GetFileName(file.Key)
- };
-
- fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
- content.Add(fileContent);
- }
-
- var streamingFiles = FetchStreamingFileBodies(message);
- foreach (KeyValuePair<string, MemoryStream> file in streamingFiles) {
- var name = file.Key;
+ var files = FetchFileBodies(message);
+ foreach (var file in files)
+ {
+ var fs = new FileStream(file.Key, FileMode.Open, FileAccess.Read);
+ var fileContent = new StreamContent(fs);
+
+ fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
+ {
+ Name = "files[" + Path.GetFileName(file.Key) + "]",
+ FileName = Path.GetFileName(file.Key)
+ };
+
+ fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
+ content.Add(fileContent);
+ }
+
+ var streamingFiles = FetchStreamingFileBodies(message);
+ foreach (var file in streamingFiles)
+ {
var stream = file.Value;
- var fileContent = new StreamContent(stream);
-
- fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
- {
- Name = "files[" + Path.GetFileName(file.Key) + "]",
- FileName = Path.GetFileName(file.Key)
- };
-
- fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
- content.Add(fileContent);
+ var fileContent = new StreamContent(stream);
+
+ fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
+ {
+ Name = "files[" + Path.GetFileName(file.Key) + "]",
+ FileName = Path.GetFileName(file.Key)
+ };
+
+ fileContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/octet-stream");
+ content.Add(fileContent);
+ }
+ }
+
+ private static void CheckForErrors(HttpResponseMessage response)
+ {
+ //transport error
+ if (response.StatusCode != HttpStatusCode.OK)
+ {
+ throw new Exception(response.ReasonPhrase);
+ }
+
+ var content = response.Content.ReadAsStreamAsync().Result;
+
+ FindErrorsInResponse(content);
+ }
+
+ private static void FindErrorsInResponse(Stream content)
+ {
+ using (var reader = XmlReader.Create(content))
+ {
+ while (reader.Read())
+ {
+ if (!reader.IsStartElement()) continue;
+ switch (reader.Name)
+ {
+ case "result":
+ break;
+ case "message": // success
+ if (reader.ReadToNextSibling("errors"))
+ throw new ProtocolViolationException();
+ return;
+ case "error": // failure
+ throw new ProtocolViolationException();
+ default:
+ throw new ArgumentException("Unknown element: " + reader.Name);
+ }
+ }
}
- }
+ }
- private void CheckForErrors (HttpResponseMessage response)
+ private static async Task CheckForErrorsAsync(HttpResponseMessage response)
{
//transport error
- if (response.StatusCode != HttpStatusCode.OK) {
+ if (response.StatusCode != HttpStatusCode.OK)
+ {
throw new Exception(response.ReasonPhrase);
}
- var content = response.Content.ReadAsStreamAsync().Result;
-
- FindErrorsInResponse(content);
- }
-
- private static void FindErrorsInResponse(Stream content)
- {
- using (var reader = XmlReader.Create(content))
- {
- while (reader.Read())
- {
- if (reader.IsStartElement())
- {
- switch (reader.Name)
- {
- case "result":
- break;
- case "message": // success
- bool errors = reader.ReadToNextSibling("errors");
- if (errors)
- throw new ProtocolViolationException();
- return;
- case "error": // failure
- throw new ProtocolViolationException();
- default:
- throw new ArgumentException("Unknown element: " + reader.Name);
- }
- }
- }
- }
- }
-
- private async Task CheckForErrorsAsync(HttpResponseMessage response)
- {
- //transport error
- if (response.StatusCode != HttpStatusCode.OK)
- {
- throw new Exception(response.ReasonPhrase);
- }
-
- var content = await response.Content.ReadAsStreamAsync();
-
- FindErrorsInResponse(content);
- }
-
- internal List<KeyValuePair<String, String>> FetchFormParams(ISendGrid message)
- {
- var result = new List<KeyValuePair<string, string>>
- {
- new KeyValuePair<String, String>("api_user", _credentials.UserName),
- new KeyValuePair<String, String>("api_key", _credentials.Password),
- new KeyValuePair<String, String>("headers", message.Headers.Count == 0 ? null : Utils.SerializeDictionary(message.Headers)),
- new KeyValuePair<String, String>("replyto", message.ReplyTo.Length == 0 ? null : message.ReplyTo.ToList().First().Address),
- new KeyValuePair<String, String>("from", message.From.Address),
- new KeyValuePair<String, String>("fromname", message.From.DisplayName),
- new KeyValuePair<String, String>("subject", message.Subject),
- new KeyValuePair<String, String>("text", message.Text),
- new KeyValuePair<String, String>("html", message.Html),
- new KeyValuePair<String, String>("x-smtpapi", message.Header.JsonString())
- };
- if(message.To != null)
- {
- result = result.Concat(message.To.ToList().Select(a => new KeyValuePair<String, String>("to[]", a.Address)))
- .Concat(message.To.ToList().Select(a => new KeyValuePair<String, String>("toname[]", a.DisplayName)))
- .ToList();
- }
- if(message.Bcc != null)
- {
- result = result.Concat(message.Bcc.ToList().Select(a => new KeyValuePair<String, String>("bcc[]", a.Address)))
- .ToList();
- }
- if(message.Cc != null)
- {
- result = result.Concat(message.Cc.ToList().Select(a => new KeyValuePair<String, String>("cc[]", a.Address)))
- .ToList();
- }
- return result.Where(r => !String.IsNullOrEmpty(r.Value)).ToList();
- }
-
- internal List<KeyValuePair<String, MemoryStream>> FetchStreamingFileBodies(ISendGrid message)
- {
- return message.StreamedAttachments.Select(kvp => kvp).ToList();
- }
-
- internal List<KeyValuePair<String, FileInfo>> FetchFileBodies(ISendGrid message)
- {
- if(message.Attachments == null)
- return new List<KeyValuePair<string, FileInfo>>();
- return message.Attachments.Select(name => new KeyValuePair<String, FileInfo>(name, new FileInfo(name))).ToList();
- }
-
- #endregion
- }
-}
+ var content = await response.Content.ReadAsStreamAsync();
+
+ FindErrorsInResponse(content);
+ }
+
+ internal List<KeyValuePair<String, String>> FetchFormParams(ISendGrid message)
+ {
+ var result = new List<KeyValuePair<string, string>>
+ {
+ new KeyValuePair<String, String>("api_user", _credentials.UserName),
+ new KeyValuePair<String, String>("api_key", _credentials.Password),
+ new KeyValuePair<String, String>("headers",
+ message.Headers.Count == 0 ? null : Utils.SerializeDictionary(message.Headers)),
+ new KeyValuePair<String, String>("replyto",
+ message.ReplyTo.Length == 0 ? null : message.ReplyTo.ToList().First().Address),
+ new KeyValuePair<String, String>("from", message.From.Address),
+ new KeyValuePair<String, String>("fromname", message.From.DisplayName),
+ new KeyValuePair<String, String>("subject", message.Subject),
+ new KeyValuePair<String, String>("text", message.Text),
+ new KeyValuePair<String, String>("html", message.Html),
+ new KeyValuePair<String, String>("x-smtpapi", message.Header.JsonString())
+ };
+ if (message.To != null)
+ {
+ result = result.Concat(message.To.ToList().Select(a => new KeyValuePair<String, String>("to[]", a.Address)))
+ .Concat(message.To.ToList().Select(a => new KeyValuePair<String, String>("toname[]", a.DisplayName)))
+ .ToList();
+ }
+ if (message.Bcc != null)
+ {
+ result = result.Concat(message.Bcc.ToList().Select(a => new KeyValuePair<String, String>("bcc[]", a.Address)))
+ .ToList();
+ }
+ if (message.Cc != null)
+ {
+ result = result.Concat(message.Cc.ToList().Select(a => new KeyValuePair<String, String>("cc[]", a.Address)))
+ .ToList();
+ }
+ return result.Where(r => !String.IsNullOrEmpty(r.Value)).ToList();
+ }
+
+ internal IEnumerable<KeyValuePair<string, MemoryStream>> FetchStreamingFileBodies(ISendGrid message)
+ {
+ return message.StreamedAttachments.Select(kvp => kvp).ToList();
+ }
+
+ internal List<KeyValuePair<String, FileInfo>> FetchFileBodies(ISendGrid message)
+ {
+ return message.Attachments == null
+ ? new List<KeyValuePair<string, FileInfo>>()
+ : message.Attachments.Select(name => new KeyValuePair<String, FileInfo>(name, new FileInfo(name))).ToList();
+ }
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/SendGrid/SendGridMail/packages.config b/SendGrid/SendGridMail/packages.config
index aa0484c..0470bc3 100644
--- a/SendGrid/SendGridMail/packages.config
+++ b/SendGrid/SendGridMail/packages.config
@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
+
<packages>
- <package id="Microsoft.Bcl" version="1.0.19" targetFramework="net40" />
- <package id="Microsoft.Bcl.Async" version="1.0.16" targetFramework="net40" />
- <package id="Microsoft.Bcl.Build" version="1.0.10" targetFramework="net40" />
- <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
- <package id="smtpapi" version="1.0.0" targetFramework="net40" />
+ <package id="Microsoft.Bcl" version="1.0.19" targetFramework="net40" />
+ <package id="Microsoft.Bcl.Async" version="1.0.16" targetFramework="net40" />
+ <package id="Microsoft.Bcl.Build" version="1.0.10" targetFramework="net40" />
+ <package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net45" />
+ <package id="smtpapi" version="1.0.0" targetFramework="net40" />
</packages> \ No newline at end of file