Here, you can download the latest version of the demo .NET application, with C# or VB.NET source code:
(last updated on 2016-06-10)
Introduction
Some time ago, I needed a solution for sending email replies using C#. An internet search didn’t return any usable examples, so I needed to investigate it on my own. I wrote this article in the hope that it would be useful for someone trying to create and send an email message reply programmatically in a .NET application. It was mainly to save you the trouble of going through RFC documents and understanding the email message format (MIME) for which, as a developer of GemBox.Email component, I gained quite some knowledge.
Email Reply Standard
The MIME standard extends the original ASCII text-only email format to support character sets other than ASCII, non-text attachments and multipart message bodies. Although the standard is very old, it is still in use and most of today’s email messages are still sent and received in this format.
The format is specified in a few RFC documents, but since each of them has been updated, it took me a while to find the info on writing reply messages. RFC 2822 defines “identification fields”, better known as headers, which need to be defined in an email message reply. For an email message to qualify as a reply, it needs to contain the headers “In-Reply-To” and “References”.
The standard also states that you should prefix the email message subject with “Re:” (from Latin “res” meaning “in the matter of”), although it is not mandatory.
What About the Email Message Body?
It came as a surprise to me that the email message reply examples in RFC 2822 don’t include the original email message body, especially since all of the email replies in my inbox do. It’s because most of the commonly used email clients have this option enabled by default, but it can be disabled.
Although there is no standard defining the format for quoting in a plain text message, the '>' sign is the most commonly used quote prefix char. Basically, you prefix every line of the original message text with a '>' sign. You can read more about it here. For HTML message bodies, there is no standard at all. Not even a convention. You are free to include the original text any way you see fit. Most of the email clients use the <blockquote>
tag, but some use the <div>
tag with custom styling.
Screenshot of an email message reply
C# / VB.NET Code
Now that we have some knowledge of the subject, we can take a look at the code.
static void Main(string[] args)
{
IEnumerable<MailMessage> messages = GetMessages();
if (messages != null)
{
Console.WriteLine(messages.Count().ToString() + " new message(s).");
List<MailMessage> replies = new List<MailMessage>();
foreach (MailMessage msg in messages)
{
replies.Add(CreateReply(msg));
msg.Dispose();
}
SendReplies(replies);
}
else
{
Console.WriteLine("No new messages.");
}
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
Sub Main()
Dim messages As IEnumerable(Of MailMessage) = GetMessages()
If messages IsNot Nothing Then
Console.WriteLine(messages.Count().ToString() & " new email message(s).")
Dim replies As New List(Of MailMessage)()
For Each msg As MailMessage In messages
replies.Add(CreateReply(msg))
msg.Dispose()
Next
SendReplies(replies)
Else
Console.WriteLine("No new email messages.")
End If
Console.WriteLine("Press any key to exit...")
Console.ReadKey()
End Sub
Method Main
first checks if there are any new unseen email messages on the server. If GetMessages
returns a not-null
enumeration of email messages, the result is enumerated and a reply message is created for each item, using the CreateReply
method. Since the original email messages won’t be needed any more, they are disposed of after creating the replies. At the end, the replies are passed to the SendReplies
method which will send them using System.Net.SmtpClient
. Since this is a console application, progress info is displayed on the console window.
Reading Emails with an IMAP Client
There are two standard email protocols that can be used for fetching email messages, POP and IMAP. Unfortunately, the .NET framework doesn’t provide support for either of them. It supports only SMTP, which can be used only for sending email messages. The following code uses S22.Imap, a third party IMAP client for downloading messages from the server.
private static IEnumerable<MailMessage> GetMessages()
{
using (ImapClient client = new ImapClient(imapHost, 993, true))
{
Console.WriteLine("Connected to " + imapHost + '.');
client.Login(imapUser, imapPassword, AuthMethod.Auto);
Console.WriteLine("Authenticated.");
client.DefaultMailbox = "INBOX";
IEnumerable<uint> uids = client.Search(SearchCondition.Unseen());
if (uids.Count() == 0)
return null;
return client.GetMessages(uids);
}
}
Private Function GetMessages() As IEnumerable(Of MailMessage)
Using client As New ImapClient(imapHost, 993, True)
Console.WriteLine("Connected to " & imapHost & "."c)
client.Login(imapUser, imapPassword, AuthMethod.Auto)
Console.WriteLine("Authenticated.")
client.DefaultMailbox = "INBOX"
Dim uids As IEnumerable(Of UInteger) = client.Search(SearchCondition.Unseen())
If (uids.Count = 0) Then Return Nothing
Return client.GetMessages(uids)
End Using
End Function
S22.Imap
makes retrieving new messages a trivial task. First, you need to initialize a new ImapClient
, which connects to the server. The next step is authentication, which is executed by calling the Login
method with the mailbox username, password and authentication method as parameters. Use AuthMethod.Auto
because it will select the best authentication method that the server supports.
The next step is to set “INBOX” as a default mailbox and query the server for all unseen messages. The method returns an enumeration of message IDs. If that enumeration is not empty, the GetMessages
method is called, which will return an enumeration of System.Net.Mail.MailMessage
instances for the provided message IDs.
Creating a Reply Message
After all the unseen messages have been downloaded, replies are created by calling the CreateReply
method in a loop, as you can see in the following code snippet:
List<MailMessage> replies = new List<MailMessage>();
foreach (MailMessage msg in messages)
{
replies.Add(CreateReply(msg));
msg.Dispose();
}
Dim replies As New List(Of MailMessage)()
For Each msg As MailMessage In messages
replies.Add(CreateReply(msg))
msg.Dispose()
Next
The CreateReply
method first creates a new mail message with the sender and receiver addresses swapped.
MailMessage reply = new MailMessage(new MailAddress(imapUser, "Sender"), source.From);
Dim reply As New MailMessage(New MailAddress(imapUser, "Sender"), source.From)
source.To
cannot be used as a from parameter since it is a collection of email addresses instead of a single address and it might contain multiple addresses, so you could end up with the wrong one. In the example code, I have used a username defined for IMAP server authentication. Be sure to specify the full email address of the user as a username, when using your credentials, or it won’t work as expected.
The first thing you need to do with a reply message is to add the required headers. As explained in the Email reply standard section, these are “In-Reply-To” and “References”.
string id = source.Headers["Message-ID"];
reply.Headers.Add("In-Reply-To", id);
string references = source.Headers["References"];
if (!string.IsNullOrEmpty(references))
references += ' ';
reply.Headers.Add("References", references + id);
Dim id As String = source.Headers("Message-ID")
reply.Headers.Add("In-Reply-To", id)
Dim references As String = source.Headers("References")
If Not String.IsNullOrEmpty(references) Then references &= " "c
reply.Headers.Add("References", references & id)
Although “Message-ID” header is not mandatory, most messages will contain it, especially those from major providers like Gmail, Yahoo, Outlook, etc. For that reason, the id
variable is not checked.
Next, if not already prefixed, prefix the reply subject with “Re:”.
if (!source.Subject.StartsWith("Re:", StringComparison.OrdinalIgnoreCase))
reply.Subject = "Re: ";
reply.Subject += source.Subject;
If Not source.Subject.StartsWith("Re:", StringComparison.OrdinalIgnoreCase) Then
reply.Subject = "Re: "
End If
reply.Subject &= source.Subject
Finally, the reply body is composed, depending on the source message body type.
StringBuilder body = new StringBuilder();
if (source.IsBodyHtml)
{
body.Append("<p>Thank you for your email!</p>");
body.Append("<p>We are currently out of the office,
but we will respond as soon as possible.</p>");
body.Append("<p>Best regards,<br/>");
body.Append(senderName);
body.Append("</p>");
body.Append("<br/>");
body.Append("<div>");
if (source.Date().HasValue)
body.AppendFormat
("On {0},", source.Date().Value.ToString(CultureInfo.InvariantCulture));
if (!string.IsNullOrEmpty(source.From.DisplayName))
body.Append(source.From.DisplayName + ' ');
body.AppendFormat("<<a href=\"mailto:{0}\">{0}</a>>
wrote:<br/>", source.From.Address);
if (!string.IsNullOrEmpty(source.Body))
{
body.Append("<blockqoute style=\"margin: 0 0 0 5px;
border-left:2px blue solid;padding-left:5px\">");
body.Append(source.Body);
body.Append("</blockquote>");
}
body.Append("</div>");
}
else
{
body.AppendLine("Thank you for your email!");
body.AppendLine();
body.AppendLine("We are currently out of the office,
but we will respond as soon as possible.");
body.AppendLine();
body.AppendLine("Best regards,");
body.AppendLine(senderName);
body.AppendLine();
if (source.Date().HasValue)
body.AppendFormat("On {0},
", source.Date().Value.ToString(CultureInfo.InvariantCulture));
body.Append(source.From);
body.AppendLine(" wrote:");
if (!string.IsNullOrEmpty(source.Body))
{
body.AppendLine();
body.Append("> " +
source.Body.Replace("\r\n", "\r\n> "));
}
}
reply.Body = body.ToString();
reply.IsBodyHtml = source.IsBodyHtml;
Dim body As New StringBuilder()
If source.IsBodyHtml Then
body.Append("<p>Thank you for your email!</p>")
body.Append("<p>We are currently out of the office, _
but we will respond as soon as possible.</p>")
body.Append("<p>Best regards,<br/>")
body.Append(senderName)
body.Append("</p>")
body.Append("<br/>")
body.Append("<div>")
If source.Date().HasValue Then body.AppendFormat("On {0}, _
", source.Date().Value.ToString(CultureInfo.InvariantCulture))
If Not String.IsNullOrEmpty(source.From.DisplayName) _
Then body.Append(source.From.DisplayName & " "c)
body.AppendFormat("<<a href=""mailto:{0}"">_
{0}</a>> wrote:<br/>", source.From.Address)
If Not String.IsNullOrEmpty(source.Body) Then
body.Append("<blockqoute style=""margin:0 0 0 5px;_
border-left:2px blue solid;padding-left:5px"">")
body.Append(source.Body)
body.Append("</blockquote>")
End If
body.Append("</div>")
Else
body.AppendLine("Thank you for your email!")
body.AppendLine()
body.AppendLine("We are currently out of the office, _
but we will reply as soon as possible.")
body.AppendLine()
body.AppendLine("Best regards,")
body.AppendLine(senderName)
body.AppendLine()
If source.Date().HasValue Then body.AppendFormat("On {0}, _
", source.Date().Value.ToString(CultureInfo.InvariantCulture))
body.Append(source.From)
body.AppendLine(" wrote:")
If Not String.IsNullOrEmpty(source.Body) Then
body.AppendLine()
body.Append("> " & source.Body.Replace(vbCrLf, vbCrLf & ">"c))
End If
End If
reply.Body = body.ToString()
reply.IsBodyHtml = source.IsBodyHtml
I have used very simple text for the reply body, but you can see that the <blockqoute>
tag is used for the HTML body and the '>' prefix for the plain text body.
Sending Emails with an SMTP Client
Newly created message replies are sent using the standard .NET SmtpClient
. The process is quite straightforward as you can see in the code below:
using (SmtpClient client = new SmtpClient(smtpHost, 587))
{
client.EnableSsl = true;
client.UseDefaultCredentials = false;
client.Credentials = new NetworkCredential(smtpUser, smtpPassword);
client.DeliveryFormat = SmtpDeliveryFormat.International;
bool retry = true;
foreach (MailMessage msg in replies)
{
try
{
client.Send(msg);
retry = true;
}
catch (Exception ex)
{
if (!retry)
{
Console.WriteLine("Failed to send reply to " +
msg.To.ToString() + '.');
Console.WriteLine("Exception: " + ex.Message);
return;
}
retry = false;
}
finally
{
msg.Dispose();
}
}
Console.WriteLine("All replies successfully sent.");
}
Using client As New SmtpClient(smtpHost, 587)
client.EnableSsl = True
client.UseDefaultCredentials = False
client.Credentials = New NetworkCredential(smtpUser, smtpPassword)
Dim retry As Boolean = True
For Each msg As MailMessage In replies
Try
client.Send(msg)
retry = True
Catch ex As Exception
If Not retry Then
Console.WriteLine("Failed to send email reply to _
" & msg.To.ToString() & "."c)
Console.WriteLine("Exception: " & ex.Message)
Exit Sub
End If
retry = False
Finally
msg.Dispose()
End Try
Next
Console.WriteLine("All email replies successfully sent.")
To prevent the application from crashing on single message errors, all exceptions are ignored. Still, there might be an error with the SMTP server or credentials that will cause every message to fail, so for that reason, I have used a very simple and primitive retry pattern. Basically, it uses a bool variable to break the sending loop when there are two exceptions in a row.
Screenshot of the EmailReply application’s console window
Alternatives
Since the .NET Framework doesn’t provide any means for downloading email messages from the server, the code presented in this article uses the S22.Imap component for that task. As the name suggests, the library communicates with the server using the IMAP protocol. Another very commonly used protocol is POP, and there are even more components for it on the internet since POP is a much older and simpler protocol. OpenPop is the most popular, but S22.Pop3 is a good choice as well since it uses the System.Net.Mail.MailMessage
class instead of the custom message class used by OpenPop.
Gmail Security
Being one of the most used email providers, Google’s Gmail service has very high security standards. By default, POP, IMAP and common authentication mechanisms are disabled. To be able to use your Gmail account in the demo application supplied with this article, you need to do the following:
Enable POP or IMAP Access for Your Account
-
Log in to your Gmail account and select 'Settings' from the right side menu.
-
Select the 'Forwarding and POP/IMAP' tab and enable POP or IMAP, depending on which protocol you are going to use.
Allow Access for Less Secure Applications
-
Click on your account icon in the top right corner and select 'My Account'.
-
Click on 'Sign-in & security' and, at the bottom of the page, set 'Allow less secure apps:' to ON.
Conclusion
The aim of this article was to show that creating an email message reply is quite simple. All you need to do is add headers: “In-Reply-To” and “References”. Everything else is optional. I hope this article and the attached console application will help you with your future .NET email applications.