Second Life of a Hungarian SharePoint Geek

April 24, 2013

Creating a mail distributor system using the incoming mail feature of SharePoint

Filed under: Incoming email, Reflection, SP 2010 — Tags: , , — Peter Holpar @ 21:56

Wouldn’t it be great to implement your own custom logic to distribute mails to targeted addresses (for example, based on sender or subject of the mail) using SharePoint lists and event receivers? In this post I show you the fundamental technical issues and their solution to achieve that goal. My “custom logic” is quite simple, I send the same mail back to the sender, however you can build more sophisticated logic using the same technique, but more on that later.

Before starting Visual Studio, I’ve created a simple custom list called MailDistributor on my SharePoint site.

In Visual Studio I chose the Empty SharePoint Project template, and added a new List Email Event event receiver item.

Having these artifacts, I altered the default Elements.xml, to register the event receiver to the list I created earlier:

Code Snippet
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  3.     <Receivers ListUrl="Lists/MailDistributor">
  4.         <Receiver>
  5.             <Name>IncomingMailHandlerEmailReceived</Name>
  6.             <Type>EmailReceived</Type>
  7.             <Assembly>$SharePoint.Project.AssemblyFullName$</Assembly>
  8.             <Class>MailDistributor.IncomingMailHandler</Class>
  9.             <SequenceNumber>10000</SequenceNumber>
  10.         </Receiver>
  11.     </Receivers>
  12. </Elements>

Regarding the code, the first step was to create the extension method GetMailMessage for the SPEmailMessage type to convert it to a MailMessage object (System.Net.Mail namespace). Fortunately, both of these object types have the same stream format in the background. This stream is directly accessible from the SPEmailMessage, however, MailMessage  is not creatable from the stream (or ByteArray / String) format. To solve this issue, I utilized the RxMailMessage type (copyright by Peter Huber, Singapore), that is a derived class of MailMessage with Stream and File support.

Code Snippet
  1. public static MailMessage GetMailMessage(this SPEmailMessage spEmailMessage)
  2. {
  3.     MailMessage result = null;
  4.     if (spEmailMessage != null)
  5.     {
  6.         result = RxMailMessage.CreateFromStream(spEmailMessage.GetMessageStream());
  7.     }
  8.  
  9.     return result;
  10. }

Below is the structure of the solution, highlighted the classes borrowed from Peter Huber.

image

The next challenge was to set the addressee (To property) of the MailMessage instance. Since there is no way to change this read-only property using the public methods of the type (in practice, you should set it already in the constructor), I had to apply my experience with Reflection, and set the private field to of the private field message of the MailMessage instance. Although I set only the To property in the example using the SetTo extension method, you could (and should!) set the Cc and Bcc fields as well. For example, clear these values to avoid perpetual sending / receiving the same message in the case the one of the Cc / Bcc fields contain the incoming mail address of the SharePoint list. To do that, you should implement the SetCc and SetBcc methods and from these methods call the SetMailAddressCollection method with the parameters “cc” and “bcc” accordingly.

Code Snippet
  1. public static void SetTo(this MailMessage mailMessage, MailAddressCollection mailAddressCollection)
  2. {
  3.     if ((mailMessage != null) && (mailAddressCollection != null))
  4.     {
  5.         SetMailAddressCollection(mailMessage, mailAddressCollection, "to");
  6.     }
  7. }
  8.  
  9. private static void SetMailAddressCollection(MailMessage mailMessage, MailAddressCollection mailAddressCollection, string fieldName)
  10. {
  11.     if ((mailMessage != null) && (mailAddressCollection != null) && (fieldName != null))
  12.     {
  13.         Type typeMailMessage = typeof(MailMessage);
  14.  
  15.         FieldInfo fi = typeMailMessage.GetField("message", BindingFlags.NonPublic | BindingFlags.Instance);
  16.  
  17.         if (fi != null)
  18.         {
  19.             object message = fi.GetValue(mailMessage);
  20.  
  21.             if (message != null)
  22.             {
  23.                 Type typeMessage = message.GetType(); // it is internal class System.Net.Mail.Message
  24.  
  25.                 FieldInfo fi2 = typeMessage.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
  26.  
  27.                 if (fi2 != null)
  28.                 {
  29.                     fi2.SetValue(message, mailAddressCollection);
  30.                 }
  31.             }
  32.         }
  33.     }
  34. }

In the event receiver, we convert the SPEmailMessage into a MailMessage instance, set its To property to the e-mail address of the original poster (From property of the MailMessage) and send the mail using an SmtpClient object. The Host property of the SmtpClient instance can be set using the Address of the configured SMTP server of the OutboundMailServiceInstance in the current web application.

As you can see, most of the code below is just tracing to help us to follow the process using DebugView. You are free to remove these lines without affecting the functionality, of course.

Code Snippet
  1. using System;
  2. using System.Diagnostics;
  3. using System.Net.Mail;
  4. using Microsoft.SharePoint;
  5. using Microsoft.SharePoint.Utilities;
  6.  
  7. namespace MailDistributor
  8. {
  9.     public class IncomingMailHandler : SPEmailEventReceiver
  10.     {
  11.         public override void EmailReceived(SPList list, SPEmailMessage emailMessage, string receiverData)
  12.         {
  13.             try
  14.             {
  15.                 Trace.TraceInformation("IncomingMailHandler starting…");
  16.  
  17.                 foreach (SPEmailHeader header in emailMessage.Headers)
  18.                 {
  19.                     Trace.TraceInformation("EmailReceived emailMessage header {0}, {1}", header.Name, header.Value);
  20.                 }
  21.  
  22.                 SmtpClient smtpClient = new SmtpClient();
  23.  
  24.                 smtpClient.Host = list.ParentWeb.Site.WebApplication.OutboundMailServiceInstance.Server.Address;
  25.  
  26.                 Trace.TraceInformation("IncomingMailHandler: getting mail message from stream");
  27.  
  28.                 MailMessage mailMessage = emailMessage.GetMailMessage();
  29.  
  30.                 Trace.TraceInformation("IncomingMailHandler: setting mail message To field");
  31.  
  32.                 mailMessage.SetTo(new MailAddressCollection { mailMessage.From });
  33.  
  34.                 Trace.TraceInformation("IncomingMailHandler: sending mail");
  35.  
  36.                 smtpClient.Send(mailMessage);
  37.                 
  38.                 Trace.TraceInformation("IncomingMailHandler: finished");
  39.             }
  40.             catch (Exception ex)
  41.             {
  42.                 Trace.TraceInformation("IncomingMailHandler exception: {0}", ex.Message);
  43.             }
  44.  
  45.         }
  46.  
  47.     }
  48.  
  49. }

After deploying the solution and activating the feature, we should enable the incoming mail for the MailDistributor list, set the mail address alias, and send a test message to this address. If there is no error, within about a minute we should receive the same mail, including formatting and attachments to the mailbox of the sender.

Using the Category settings of the mail to route the message

The rule we implemented is really a trivial one and has not much sense, but one can implement more complicated and more useful routing rules as well. My plan is to build a routing “engine” based on the Category settings of the mail.

As part of the Options / Tracking properties in Outlook, we can set not only Blue or Green categories, but our own custom categories (like SharePoint and Silverlight below) as well. As long as these categories are transferred within the mail, we can process them in our event receiver, look up SharePoint user profiles having the same values set in the Ask me about property, and route the message exactly to those users, implementing thus a simple but efficient knowledge management solution.

It would be even better if the user could choose those category values from a SharePoint Managed Metadata keyword list (a candidate for an Office 2013 mail-app?).

image

However there are some issues with the Category property that you should be aware of.

Based on this information, Outlook removes category settings from outgoing mails due to privacy concerns. One can alter this settings via registry (HKEY_CURRENT_USER\Software\Policies\Microsoft\Office\xx.0\Outlook\Preferences\SendPersonalCategories, where xx is the version number of Outlook, like 14 for Outlook 2010).

Exchange 2010 also removes the categories from the outgoing messages by default, as I’ve learned here. This behavior can be changes using the following PowerShell command:

Set-TransportConfig -ClearCategories:$False

A workaround for these issues might be an Outlook add-in, or a simply VBA code like this one, that illustrates, how to copy the mail categories to a custom mail header called X-Categories when sending the mail, thus avoiding losing of the categories:

Private Sub Application_ItemSend(ByVal item As Object, Cancel As Boolean)
    Dim mi As MailItem
    Set mi = item
    If Not mi Is Nothing Then
       item.PropertyAccessor.SetProperty "http://schemas.microsoft.com/mapi/string/{00020386-0000-0000-C000-000000000046}/X-Categories", item.Categories
    End If
End Sub

Of course, we should filter the mails affected by this behavior based on the To e-mail address, limiting it to the mails sent to our MailDistributor list.

Advertisements

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Create a free website or blog at WordPress.com.

%d bloggers like this: