One of the people in my team ran into a problem yesterday, where a customer could only send data via Email. The customer does not support formal EDI or have IT staffers who can support building or using something more automated. After talking about how the processing needed to be performed 7 days a week, we knew we had to build something automated. I went to my Google and StackOverflow to search for a free IMAP solution (I so wish IMAP/POP/SFTP were supported in the core framework) and found AE.Net.Mail. The product doesn’t have any documentation, but after looking at 2 posts about issues other people were having with with the client, I was able to figure out everything I needed to process my emails and save the attachments out to disk. The project is even support via NuGet, which is a big plus. All of the code below is based on using AE.Net.Mail v1.6.0.0 with .NET 4.0.
Here is the code I built to process email attachments from 2 different customers:
// Connect to Exchange 2007 IMAP server (locally), without SSL.
using (ImapClient ic = new ImapClient("<server address>", "<user account>", "<user password>", ImapClient.AuthMethods.Login, 143, false))
{
// Open a mailbox, case-insensitive
ic.SelectMailbox("INBOX");
// Get messages based on the flag "Undeleted()".
Lazy<MailMessage>[] messages = ic.SearchMessages(SearchCondition.Undeleted(), false);
// Process each message
foreach (Lazy<MailMessage> message in messages)
{
MailMessage m = message.Value;
string sender = m.From.Address;
string fileName = string.Empty;
// Rules by Sender
switch (sender)
{
case "zachary@customerA.com":
fileName = ExtractString(m.Subject, "(", ")");
foreach (Attachment attachment in m.Attachments)
{
attachment.Save(@"C:\Demo\" + fileName + Path.GetExtension(attachment.Filename));
}
break;
case "hunter@customerB.com":
foreach (Attachment attachment in m.Attachments)
{
string filePrefix = attachment.Filename.Substring(0, attachment.Filename.IndexOf('_'));
switch (filePrefix)
{
case "DAILY":
fileName = "CustomerB_Inventory";
break;
case "PULL":
fileName = "CustomerB_Pulls";
break;
case "RECEIPT":
fileName = "CustomerB_Receipts";
break;
}
attachment.Save(@"C:\Demo\" + fileName + Path.GetExtension(attachment.Filename));
}
break;
}
// Delete Message (so it drops out of criteria and won't be double processed)
ic.DeleteMessage(m);
}
}
/// <summary>
/// Helper method to pull out a string between a start and stop string.
/// Example:
/// string story = "The boy and the cat ran to the store.";
/// ExtractString(story, "cat", "to"); //returns "ran"
/// </summary>
/// <param name="stringToParse">String to search</param>
/// <param name="startTag">Starting String Pattern</param>
/// <param name="endTag">Ending String Pattern</param>
/// <returns>String found between the Starting and Ending Pattern.</returns>
static string ExtractString(string stringToParse, string startTag, string endTag)
{
int startIndex = stringToParse.IndexOf(startTag) + startTag.Length;
int endIndex = stringToParse.IndexOf(endTag, startIndex);
return stringToParse.Substring(startIndex, endIndex - startIndex);
}
Most of the processing is done in the switch statement, since I’m going to apply different parsing rules by customer. This is far from a long term elegant and maintainable longterm solution, but it gets the problem solved quick. Here is the rule summary by customer.
case “zachary@customerA.com”:
Everyday I’m going to get 3 emails from this customer, the subject for each email will be:
“Daily Reports for Customer A (CustomerA_Inventory)”
“Daily Reports for Customer A (CustomerA_Receipts)”
“Daily Reports for Customer A (CustomerA_Pulls)”
All three files have a CSV attachment that is called CustomerA.csv. The file in each email contains different data and I need all 3 written to disk for processing in my EDI solution. My solution was to parses the subject using the helper method, and renames each attachment with the name matching in the (parenthesis). The result of processing the emails with the code above, is 3 files written to disk with names that correctly identify their data content.
C:\Demo\CustomerA_Inventory.CSV
C:\Demo\CustomerA_Receipts.CSV
C:\Demo\CustomerA_Pulls.CSV
case “hunter@customerB.com”:
This customer is similar to the top, but I’m only going to get 1 email with 3 attachments that each have a date appended to the file. I need to save each file to disk without a date stamp, so my EDI processor can be setup to pull in the same “file name” everyday. My solution was to parse the attachment file name and rename the file based on the prefix of the attachment name. The results of processing the email is below.
Source Email:
Attachment 1: DAILY_INVENTORY_06_05_2012.XLS
Attachment 2: DAILY_RECEIPT_06_05_2012.XLS
Attachment 3: DAILY_PULL_06_05_2012.XLS
Results of Processing
C:\Demo\CustomerB_Inventory.XLS
C:\Demo\CustomerB_Pulls.XLS
C:\Demo\CustomerB_Receipts.XLS
Note: Awhile back I looked to see if there was a turn-key app/solution that could pull in emails for processing (basically run as a service on a server, and provide a nice GUI to allow non-technical people to setup rules for email processing), but I couldn’t find anything that worked with our setup. This would ideally be a better solution, since it would allow users to add new rules for new emails at a moments notice, versus my hard-code logic used above.