Send email with custom header

In this article we’ll show how to create and send email message with custom header added.

As a prerequisite you need to add reference to Mail.dll .NET email component to your project.

In contrast to System.Net.Mail, Mail.dll allows almost any manipulation to email message’s MIME tree. This includes adding custom headers on the root level. The easiest way to achieve this, is to use MailBuilder class and AddCustomHeader method:

MailBuilder builder = new MailBuilder();
builder.AddCustomHeader("x-spam-value", "90%");

As you can see this method operates on higher level of abstraction than MIME document, but when the email is created, you can observe that the custom header was actually added to the MIME document root:

IMail email = builder.Create();
string header = email.Document.Root.Headers["x-spam-value"];

Custom headers (those that are not defined by email standards like Date or Subject) should be prefixed with “X-” string (header names case is not important).

Following is the entire sample, that creates new email message, adds custom header. Than it connects to specified SMTP server and sends the message. Please note that some error handling is missing for simplicity and you should examine ISendMessageResult result object returned by SendMessage to be sure that email sending was successful.

// C# version

using System;
using Limilabs.Mail;
using Limilabs.Mail.Headers;
using Limilabs.Client.SMTP;

class Program
{
    static void Main(string[] args)
    {
        // Use builder object to create new email message
        MailBuilder builder = new MailBuilder();
        builder.Subject = "Test";
        builder.Text = "This is plain text message.";
        builder.From.Add(new MailBox("alice@mail.com", "Alice"));
        builder.To.Add(new MailBox("bob@mail.com", "Bob"));

        builder.AddCustomHeader("x-spam-value", "90%");

        IMail email = builder.Create();

        // Send the message
        using (Smtp smtp = new Smtp())
        {
            smtp.Connect("server.example.com");    // or ConnectSSL
            smtp.UseBestLogin("user", "password");

            smtp.SendMessage(email);

            smtp.Close();
        }
    }
};
' VB.NET version

Imports System;
Imports Limilabs.Mail
Imports Limilabs.Mail.Headers
Imports Limilabs.Client.SMTP

Public Module Module1
    Public Sub Main(ByVal args As String())

        ' Use builder object to create new email message
        Dim builder As New MailBuilder()
        builder.Subject = "Test"
        builder.Text = "This is plain text message."
        builder.From.Add(New MailBox("alice@mail.com", "Alice"))
        builder.[To].Add(New MailBox("bob@mail.com", "Bob"))

        builder.AddCustomHeader("x-spam-value", "90%")

        Dim email As IMail = builder.Create()

        ' Send the message
        Using smtp As New Smtp()
            smtp.Connect("server.example.com")    ' or ConnectSSL
            smtp.UseBestLogin("user", "password")

            smtp.SendMessage(email)

            smtp.Close()
        End Using

    End Sub
End Module

Fluent interface version:

// C# version

IMail email = Mail.Text("This is plain text message.")
    .Subject("Test")
    .From(New MailBox("alice@mail.com", "Alice"))
    .To("to@mail.com")
    .AddCustomHeader("X-Header", "x header value")
    .Create();

// Send the message
using (Smtp smtp = new Smtp())
{
    smtp.Connect("server.example.com");
    smtp.UseBestLogin("user", "password");
    smtp.SendMessage(email);
    smtp.Close();
}
' VB.NET version

Dim email As IMail = Mail.Text("This is plain text message.") _
  .Subject("Test") _
  .From(New MailBox("alice@mail.com", "Alice")) _
  .[To](New MailBox("bob@mail.com", "Bob")) _
  .AddCustomHeader("X-Header", "x header value") _
  .Create()

' Send the message
Using smtp As New Smtp()
    smtp.Connect("server.example.com")
    smtp.UseBestLogin("user", "password")
    smtp.SendMessage(email)
    smtp.Close()
End Using

Download Gmail Chat Logs via IMAP

You can download all Gmail and Google Talk conversations using IMAP protocol.

Gmail can turn your instant messaging conversations into threads of emails — available conveniently for download anywhere via IMAP.

To access and export Gmail and Google Talk chat logs make sure IMAP access is enabled for your Gmail account.

Make also sure Show in IMAP is checked for Chats under System Labels.
Gmail-chats

All chat logs are stored inside the “[Gmail]/Chats” folder.

Each email message stored in that folder contains one conversation, stored as HTML (available through IMail.HTML property) and XML (available through IMail.Visuals attachment property). Of course XML is much more interesting from the processing point of view and you can use XDocument class to parse it. Each conversation consists of multiple messages.

// C#

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.gmail.com");
    imap.Login("user@gmail.com", "password");

    imap.Select("[Gmail]/Chats");

    IEnumerable<long> firstFive = imap.GetAll().Take(5);
    foreach (long uid in firstFive)
    {
        IMail email = new MailBuilder().CreateFromEml(
            imap.GetMessageByUID(uid));

        MimeText xml = (MimeText)email.Visuals[0];
        XDocument document = XDocument.Parse(xml.Text);

        XNamespace con = XNamespace.Get("google:archive:conversation");
        XNamespace cli = XNamespace.Get("jabber:client");

        XElement conversation = document.Element(con + "conversation");
        foreach (XElement message in conversation.Elements(cli + "message"))
        {
            XElement body = message.Element(cli + "body");

            Console.WriteLine("{0} -> {1}: {2}",
                message.Attribute("from").Value,
                message.Attribute("to").Value,
                body.Value);
        }
    }
    imap.Close();
}
' VB.NET

Using imap As New Imap()
    imap.ConnectSSL("imap.gmail.com")
    imap.Login("user@gmail.com", "password")

    imap.Select("[Gmail]/Chats")

    Dim firstFive As IEnumerable(Of Long) = imap.GetAll().Take(5)
    For Each uid As Long In firstFive
        Dim email As IMail = New MailBuilder().CreateFromEml( _
            imap.GetMessageByUID(uid))

        Dim xml As MimeText = DirectCast(email.Visuals(0), MimeText)
	Dim document As XDocument = XDocument.Parse(xml.Text)

	Dim con As XNamespace = XNamespace.[Get]("google:archive:conversation")
	Dim cli As XNamespace = XNamespace.[Get]("jabber:client")

	Dim conversation As XElement = document.Element(con + "conversation")
	For Each message As XElement In conversation.Elements(cli + "message")
	    Dim body As XElement = message.Element(cli + "body")

            Console.WriteLine("{0} -> {1}: {2}", _
                message.Attribute("from").Value,  _
                message.Attribute("to").Value,  _
                body.Value)
        Next
    Next
    imap.Close()
End Using

Get Google contacts with OAuth 2.0

Although neither POP3 nor IMAP protocol allows retrieving the list of user’s contacts, it is possible to use Google API for that.

As long as you are using one of OAuth 2.0 scenarios:

Mail.dll email component allows you to easy download Gmail contacts of a particular user.

Turn on Contacts API

Remember to turn on “Contacts API” in Google management console.

Remember to add request for calendar data access using GoogleScope.ContactsScope.

// C#

List<GoogleScope> scope = new List<GoogleScope>
    {
        GoogleScope.ImapAndSmtp.Name,
        GoogleScope.EmailScope,
        GoogleScope.ContactsScope
    };
' VB.NET

Dim scope As New List(Of GoogleScope)() { _
    GoogleScope.ImapAndSmtp.Name, _
    GoogleScope.EmailScope, _
    GoogleScope.ContactsScope _
}

// C#

GoogleApi api = new GoogleApi(accessToken);

XmlDocument contacts = api.GetContacts();

XmlNamespaceManager nsmgr = new XmlNamespaceManager(contacts.NameTable);
nsmgr.AddNamespace("gd", "http://schemas.google.com/g/2005");
nsmgr.AddNamespace("a", "http://www.w3.org/2005/Atom");

foreach (XmlNode contact in contacts.GetElementsByTagName("entry"))
{
    XmlNode title = contact.SelectSingleNode("a:title", nsmgr);
    XmlNode email = contact.SelectSingleNode("gd:email", nsmgr);

    Console.WriteLine("{0}: {1}", 
        title.InnerText, 
        email.Attributes["address"].Value);
}
' VB.NET

Dim api As New GoogleApi(accessToken)

Dim contacts As XmlDocument = api.GetContacts()

Dim nsmgr As New XmlNamespaceManager(contacts.NameTable)
nsmgr.AddNamespace("gd", "http://schemas.google.com/g/2005")
nsmgr.AddNamespace("a", "http://www.w3.org/2005/Atom")

For Each contact As XmlNode In contacts.GetElementsByTagName("entry")
	Dim title As XmlNode = contact.SelectSingleNode("a:title", nsmgr)
	Dim email As XmlNode = contact.SelectSingleNode("gd:email", nsmgr)
	Console.WriteLine("{0}: {1}", _
            title.InnerText, _
            email.Attributes("address").Value)
Next

Requesting Delivery Status Notifications (DSN)

In this article I am going to explain how to request Delivery Status Notifications (DSN) regarding email delivery.

Delivery notifications are used to trace, if the email is delivered, bounced or delayed.

You can find more information on how to process bounced messages here.

Please also note that there is a fundamental difference between Delivery Status Notifications (DSN, read receipts), which are sent by SMTP servers and
Message Delivery Notifications (MDN), which are sent by mail user agents (such as Outlook or AS2 interface).

In case of delayed or failed delivery email server will send an email containing DSN back to sender. Once mail is delivered to the recipient mailbox, delivery notification mail will be sent to the sender mailbox.

There are several delivery notification options, you can request when sending a massage:

  • DeliveryNotificationOptions.None – No notification information will be sent. The mail server will utilize its configured behavior to determine whether it should generate a delivery notification. Usually notification is send when failure or delay occurs.
  • DeliveryNotificationOptions.OnSuccess – Notify if the delivery is successful.
  • DeliveryNotificationOptions.OnFailure – Notify if the delivery is unsuccessful.
  • DeliveryNotificationOptions.Delay – Notify if the delivery is delayed.
  • DeliveryNotificationOptions.Never – A notification should not be generated under any circumstances.

You can set delivery notification options easily. Please check the below code. It is normal mail sending code with one additional line that requests delivery notification to be sent. It uses Smtp.Configuration.DeliveryNotification property:

using (Smtp smtp = new Smtp())
{
    smtp.Connect("smtp.example.com");
    smtp.UseBestLogin("user", "password");
    smtp.Configuration.DeliveryNotification = DeliveryNotificationOptions.OnFailure |
        DeliveryNotificationOptions.Delay;

    IMail email = Fluent.Mail.Text("Some text")
        .Subject("Some subject")
        .From(new MailBox("from@example.com"))
        .To(new MailBox("to@example.com"))
        .Create();

    smtp.SendMessage(email);

    smtp.Close();
}
Using smtp As New Smtp()
    smtp.Connect("smtp.example.com")
    smtp.UseBestLogin("user", "password")
    smtp.Configuration.DeliveryNotification =  _
        DeliveryNotificationOptions.OnFailure _
        Or DeliveryNotificationOptions.Delay

    Dim email As IMail = Fluent.Mail.Text("Some text") _
        .Subject("Some subject") _
        .From(New MailBox("from@example.com")) _
        .[To](New MailBox("to@example.com")) _
        .Create()

    smtp.SendMessage(email)

    smtp.Close()
End Using

Please note that SMTP servers may ignore requests for such notifications, especially OnSuccess options tend to be ignored.

Creating read receipt (MDN)

In this article we’ll show how to create and send read receipt.

Read receipts also known as MDNs or Message Delivery Notifications are used to notify the message sender that some action has happened with their message (it was displayed, processed, deleted)

Check if read receipt was requested

Although several email headers can be used by sender to request a read receipt (‘Disposition-Notification-To’, ‘Return-Receipt-To’, ‘X-Confirm-Reading-To’) checking if read receipt was requested is quite easy. You just need to use IMail.GetReadReceiptAddresses method.

This method checks all previously mentioned headers and removes duplicates. If the returned list is not empty, it means that sender have requested read receipt.

The recipient’s email software may silently ignore the request, or it may prompt the user for permission to send the MDN. There is no obligation or guarantee of the return-receipt sending.

// C#

IMail mail = ...

List<MailBox> addresses = mail.GetReadReceiptAddresses();
if (addresses.Count > 0)
{
    // Read receipt was requested
}
' VB.NET

Dim mail As IMail = ...

Dim addresses As List(Of MailBox) = mail.GetReadReceiptAddresses()
    ' Read receipt was requested
If addresses.Count > 0 Then
End If

Creating read receipt for a message

Use ReadReceiptBuilder class to create MailBuilder that can be used to create actual message (IMail).

// C#
IMail mail = ...

ReadReceiptBuilder mdnBuilder = new ReadReceiptBuilder(mail);
//mdnBuilder.ReadSubjectTemplate = "Read: [Original.Subject]";
//mdnBuilder.ReadTextTemplate = 
//  @"This is a confirmation that your message sent to [OriginalRecipient.Address] was displayed.";
MailBuilder builder = mdnBuilder.WasDisplayed(new MailBox("bob@example.com"));
IMail mdn = builder.Create();

' VB.NET

Dim mail As IMail = ...

Dim mdnBuilder As New ReadReceiptBuilder(mail)
'mdnBuilder.ReadSubjectTemplate = "Read: [Original.Subject]";
'mdnBuilder.ReadTextTemplate =  
'   @"This is a confirmation that your message sent to [OriginalRecipient.Address] was displayed.";
Dim builder As MailBuilder = mdnBuilder.WasDisplayed(New MailBox("bob@example.com"))
Dim mdn As IMail = builder.Create()

Creating new read receipt

You can use ReadReceiptBuilder constructor overloads to create new read receipt when you don’t have IMail object available. You’ll need original message-id however.

// C#

ReadReceiptBuilder mdnBuilder = new ReadReceiptBuilder("messageid@original.com", new MailBox("sender@original.com"));
MailBuilder builder = mdnBuilder.WasDisplayed(new MailBox("recipient@original.com"));
IMail mdn = builder.Create();
' VB.NET

Dim mdnBuilder As New ReadReceiptBuilder("messageid@original.com", New MailBox("sender@original.com"))
Dim builder As MailBuilder = mdnBuilder.WasDisplayed(New MailBox("recipient@original.com"))
Dim mdn As IMail = builder.Create()

Sending read receipt

There some restrictions regarding sending MDNs that you should consider, RFC 3798:

MDNs SHOULD NOT be sent automatically if the address in the
Disposition-Notification-To header differs from the address in the
Return-Path header (IMail.ReturnPath). In this case, confirmation
from the user SHOULD be obtained, if possible. If obtaining consent
is not possible (e.g., because the user is not online at the time),
then an MDN SHOULD NOT be sent.

Confirmation from the user SHOULD be obtained (or no MDN sent) if
there is no Return-Path header (IMail.ReturnPath) in the message, or if there is more
than one distinct address in the Disposition-Notification-To header (IMail.GetReadReceiptAddresses).

// C#

using(Smtp smtp = new Smtp())
{
    smtp.Connect("smtp.server.com");  // or ConnectSSL for SSL
    smtp.UseBestLogin("user", "password");
 
    smtp.SendMessage(mdn);                     
 
    smtp.Close();   
}  
' VB.NET

Using smtp As New Smtp()
    smtp.Connect("smtp.server.com")	' or ConnectSSL for SSL
    smtp.UseBestLogin("user", "password")

    smtp.SendMessage(mdn)

    smtp.Close()
End Using