SharePoint Incoming Email Custom Event Receiver

Mike BerrymanI recently needed to implement a custom event receiver on a document library with Incoming E-Mail enabled so that I could extract .zip file attachments from the email and put some metadata on the documents as they entered the library. I had set up the Incoming E-Mail on the library and had tested that it was properly receiving emails/attachments, which all worked just fine.

Then I created my event receiver.

As soon as I had my event receiver deployed, incoming e-mail stopped working.  There were no errors in the sharepoint or windows event logs, and when I debugged the code (btw, you debug incoming email using the SharePoint Timer service) it was triggering as expected.  To make matters even more confusing, when I went to the Incoming E-Mail settings for my document library all the settings I had previously set were now gone!  And I don’t mean the settings were blank, as though I had never touched them – they weren’t even displayed on the page.  The Incoming E-Mail settings page ONLY had the document library’s email address setting.

To make things more confusing, as soon as I retracted my event receiver, all the Incoming E-Mail settings reappeared with the my settings filled-in.

So it would seem that utilizing a custom event receiver for Incoming E-Mail overrides the out-of-the-box functionality on the list, effectively meaning that if you’re using a custom event receiver for Incoming E-Mail, it acts like Incoming E-Mail isn’t setup on the library.

When you create a normal list event receiver, you can trigger any “default” behavior by calling the base version of your event.  For this email event receiver, this would be the base.EmailReceived() function.  However, even calling that base.EmailReceived() function didn’t do anything.  Reflecting into the function shows that it doesn’t actually do anything – how perplexing…

This left me with one option: implement the out-of-the-box functionality of Incoming E-Mail ON TOP OF the custom functionality that prompted the need for a custom event receiver in the first place!  What a headache.  I needed to save the original email and all attachments in the root library, and use the sender’s permissions to verify that they’re allowed to add documents to the library.  So I needed to implement this myself.

Here’s what my event receiver ended up looking like (minus my own custom logic that’s not really applicable to this post).

public class CustomEmailEventReceiver : SPEmailEventReceiver
{
    /// <summary>
    /// The list received an e-mail message.
    /// </summary>
    public override void EmailReceived(SPList list, SPEmailMessage emailMessage, String receiverData)
    {
        base.EmailReceived(list, emailMessage, receiverData); //this does nothing

        var targetSiteId = list.ParentWeb.Site.ID;
        var targetWebId = list.ParentWeb.ID;

        var origSender = emailMessage.Sender; //the fallback sender
        foreach (var header in emailMessage.Headers)
        {
            if (header.Name.Equals("From", StringComparison.InvariantCultureIgnoreCase))
            {
                var rawFrom = header.Value;
                if (rawFrom.Contains("<") && rawFrom.Contains(">"))
                {
                    rawFrom = rawFrom.Substring(rawFrom.IndexOf("<") + 1);
                    rawFrom = rawFrom.Substring(0, rawFrom.IndexOf(">"));
                }
                origSender = rawFrom;
                break;
            }
        }

        //use the sender email to get a user that we'll use to impersonate the sender
        var sender = GetUserByEmail(targetSiteId, targetWebId, origSender);
        if (sender == null)
        {
            //some error handling here
            return;
        }

        //check that this user has write permissions on the target list
        if (!list.DoesUserHavePermissions(sender, SPBasePermissions.AddListItems))
        {
            //some error handling here
            return;
        }

        try
        {
            //now impersonate the user and add the message to the library
            using (SPSite impersonatedSite = new SPSite(targetSiteId, sender.UserToken))
            {
                using (SPWeb impersonatedWeb = impersonatedSite.OpenWeb(targetWebId))
                {
                    var subject = emailMessage.Headers["Subject"];
                    SPList impersonatedList = impersonatedWeb.Lists[list.ID];
                    using (var emailStream = emailMessage.GetMessageStream())
                    {
                        byte[] emailBytes = new byte[emailStream.Length];
                        emailStream.Read(emailBytes, 0, (int)emailStream.Length);
                        var file = impersonatedList.RootFolder.Files.Add(string.Format("{0}.eml", subject), emailBytes);
                        var item = file.Item;
                        item["Title"] = subject;
                        item.Update();
                    }

                    foreach (SPEmailAttachment attachment in emailMessage.Attachments)
                    {
                        byte[] attachmentBytes = new byte[attachment.ContentStream.Length];
                        attachment.ContentStream.Read(attachmentBytes, 0, (int)attachment.ContentStream.Length);
                        var filename = attachment.FileName;
                        var file = impersonatedList.RootFolder.Files.Add(filename, attachmentBytes);
                        var item = file.Item;
                        item["Title"] = filename;
                        item.Update();
                    }
                }
            }
        }
        catch (Exception ex)
        {
            //some error handling
        }
    }
    
    public SPUser GetUserByEmail(Guid siteId, Guid webId, string email)
    {
        SPUser user = null;
        SPSecurity.RunWithElevatedPrivileges(() =>
        {
            using (SPSite site = new SPSite(siteId))
            {
                using (SPWeb web = site.OpenWeb(webId))
                {
                    var userPrincipal = SPUtility.ResolvePrincipal(site.WebApplication, null, email, SPPrincipalType.User, SPPrincipalSource.All, true);
                    if (userPrincipal != null)
                    {
                        user = web.EnsureUser(userPrincipal.LoginName);
                    }
                }
            }
        });

        return user;
    }
}

Just another weird SharePoint-ism…

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