#region Summary ///============================================================================ /// Author : Simon /// Create Date : 11/09/2006 /// Purpose : WorkerClass for the SQLRouter Service /// /// Special Notes : This class connects to the specified mailbox via WebDAV and processes each e-mail into the specifed Database. // Once processing has finished the e-mail is deleted .As this is a service worker thread all logging is to the event log. /// ///============================================================================ #endregion using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Security.Permissions; using Microsoft.Win32; using System.Data.Common; using System.Diagnostics; using System.Xml; using System.Net; // N-Tier DataSets and data objects and EntLib - Not included (beacause I am to lazy to strip out sensitive info) so write your own or convert to non-tiered !! using SQLRouter.DataEntities; using SQLRouter.DLC.MailBox; using SQLRouter.DataEntities.MailBox; using Microsoft.Practices.EnterpriseLibrary.Data; namespace SharedMailSQLRouterService { class RouterWorker { #region private storage private EventLog log; private string ex_server; private string ex_user; private string ex_password; private string EntLib; private string NotifURL; private string Notif_User; private string Notif_Password; private int LogLevel; #endregion #region Constructor public RouterWorker(string LogName, String ex_server, String ex_user, String ex_password, String EntLib, int LogLevel, String NotifURL, String Notif_User, String Notif_Password) { this.log = new EventLog(); this.log.Source = LogName; this.ex_server = ex_server; this.ex_password = ex_password; this.ex_user = ex_user; this.EntLib = EntLib; this.LogLevel = LogLevel; this.NotifURL = NotifURL; this.Notif_User = Notif_User; this.Notif_Password = Notif_Password; } #endregion #region Email Processing Methods public void Route() { //Main call routine. //This is a class used by a service so all logging is to the windows event log. Log_Message("Router Thread started for DB: " + this.EntLib, 0); try { Log_Message("Trying WEBDAV Connection to " + ex_server + " for " + ex_user + " with EntLib:" + EntLib, 0); GetEmails(); } catch (Exception ex) { Log_Message(ex); } finally { } Log_Message("Router Thread ended for DB: " + this.EntLib, 0); } private void GetEmails() { //Create an XML Document to store the emails in System.Xml.XmlDocument xmlDoc = new XmlDocument(); XmlNodeList xmlNodeList; //Create a http request HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://" + this.ex_server + "/exchange/" + this.ex_user + "/Inbox/"); //Set the webdav method and content type request.Method = "PROPFIND"; //Get the data back as XML request.ContentType = "xml"; //Put in the credentials to connect to the Exchange mailbox request.Credentials = new NetworkCredential(this.ex_user, this.ex_password); //Get the response in a response object WebResponse response = request.GetResponse(); //Create a Streamreader to read the response object appropriately System.IO.StreamReader reader = new System.IO.StreamReader(response.GetResponseStream()); //Load the xml response data into an XML Document xmlDoc.LoadXml(reader.ReadToEnd()); //Fill the NodeList to process the response xmlNodeList = xmlDoc.GetElementsByTagName("a:response"); //Iterate through our NodeLists, pulling out the nodes of the XML for (int i = 0; i < xmlNodeList.Count; i++) { XmlNode href = null; XmlNode propStat = null; XmlNodeList properties = null; string subject = ""; string content = ""; string eiEntryID = ""; string ConversationTopic = ""; string SubjectPrefix = ""; string MessageID = ""; string fromName = ""; string fromEmail = ""; string ToEmail = ""; string textdesc = ""; string hasattach = "false"; string cc = ""; DateTime received = DateTime.MinValue; for (int m = 0; m < xmlNodeList[i].ChildNodes.Count; m++) { if (xmlNodeList[i].ChildNodes[m].Name == "a:href") { href = xmlNodeList[i].ChildNodes[m]; break; } } for (int n = 0; n < xmlNodeList[i].ChildNodes.Count; n++) { if (xmlNodeList[i].ChildNodes[n].Name == "a:propstat") { propStat = xmlNodeList[i].ChildNodes[n]; break; } } if (propStat == null) { continue; } for (int o = 0; o < propStat.ChildNodes.Count; o++) { if (propStat.ChildNodes[o].Name == "a:prop") { properties = propStat.ChildNodes[o].ChildNodes; break; } } if (properties == null) { continue; } for (int j = 0; j < properties.Count; j++) { if (properties[j].Name == "e:sendername") { fromName = properties[j].InnerText; } if (properties[j].Name == "d:return-path") { fromEmail = properties[j].InnerText; fromEmail = fromEmail.Replace(">", "").Replace("<", ""); } if (properties[j].Name == "e:normalizedsubject") { ConversationTopic = properties[j].InnerText; } if (properties[j].Name == "e:htmldescription") { content = properties[j].InnerText; } if (properties[j].Name == "a:creationdate") { received = DateTime.Parse(properties[j].InnerXml); } if (properties[j].Name == "a:getetag") { eiEntryID = properties[j].InnerText.Replace("\"", ""); } if (properties[j].Name == "e:subject") { subject = properties[j].InnerText; } if (properties[j].Name == "d:message-id") { MessageID = properties[j].InnerText; } if (properties[j].Name == "e:textdescription") { textdesc = properties[j].InnerText; if (textdesc.Length > 255) { textdesc = textdesc.Substring(0, 254); } } if (properties[j].Name == "e:to") { ToEmail = properties[j].InnerText; if (ToEmail.Length > 800) { ToEmail = ToEmail.Substring(0, 799); } } if (properties[j].Name == "e:cc") { cc = properties[j].InnerText; if (cc.Length > 800) { cc = cc.Substring(0, 799); } } if (properties[j].Name == "e:hasattachment" && properties[j].InnerText == "1") { hasattach = "true"; } } if (subject.Length > 0 && content.Length > 0) { if (subject.Contains(":")) { SubjectPrefix = subject.Split(@":".ToCharArray())[0]; } //We have a new e-mail so log it Log_Message("Retreiving a new message at " + href.InnerText, 1); //Create a new Inbox DataRow and fill it with the data InboxDS ids = new InboxDS(); InboxDS.InboxRow idr = ids.Inbox.NewInboxRow(); idr.CCEmail = cc; idr.ConversationTopic = ConversationTopic; idr.DateSent = received; idr.DAVhref = href.InnerText; idr.EntryID = eiEntryID; idr.FromEmail = fromEmail; idr.FromName = fromName; idr.hasattach = hasattach; idr.MessageID = MessageID; idr.Subject = subject; idr.Subjectprefix = SubjectPrefix; idr.TextBody = content; idr.ToEmail = ToEmail; //Add the DataRow via the custom business object if (MailBoxDLC.Add(idr, "Convertor", EntLib)) { //if the transaction was successful then log it to the event log Log_Message("Record added with id of: " + idr.ID.ToString() + " in " + this.EntLib, 1); //if we have attachments then go and get them if (hasattach == "true") { GetAttachList(href.InnerText, idr.id); } //Delete the message from Exchange. DeleteMessage(href.InnerText); } else { // If the commit failed then log the error in the event log Log_Message("Message at " + href.InnerText + " did not commit to " + this.EntLib, EventLogEntryType.Error, 99); } Log_Message("End of Processing of new message at " + href.InnerText, 0); } } } private void GetAttachList(string webdavurl, int eiEntryID) { //Create an XML Document to store the list of files System.Xml.XmlDocument xmlDoc = new XmlDocument(); //xmlNodeList will store our "ordered collection of nodes" //for the XML Document XmlNodeList xmlNodeList; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(webdavurl); request.Method = "X-MS-ENUMATTS"; //Get the data back as XML request.ContentType = "xml"; //Put in the credentials to connect to the Exchange mailbox request.Credentials = new NetworkCredential(this.ex_user, this.ex_password); //Get the response in a response object WebResponse response = request.GetResponse(); //Create a Streamreader to read the response object appropriately System.IO.StreamReader reader = new System.IO.StreamReader(response.GetResponseStream()); //Load the xml response data into our XML Document xmlDoc.LoadXml(reader.ReadToEnd()); xmlNodeList = xmlDoc.GetElementsByTagName("a:response"); //Iterate through our NodeLists, pulling out the nodes of the XML for (int i = 0; i < xmlNodeList.Count; i++) { XmlNode href = null; XmlNode propStat = null; XmlNodeList properties = null; string ext = ""; string filename = ""; DateTime received = DateTime.MinValue; for (int m = 0; m < xmlNodeList[i].ChildNodes.Count; m++) { if (xmlNodeList[i].ChildNodes[m].Name == "a:href") { href = xmlNodeList[i].ChildNodes[m]; break; } } for (int n = 0; n < xmlNodeList[i].ChildNodes.Count; n++) { if (xmlNodeList[i].ChildNodes[n].Name == "a:propstat") { propStat = xmlNodeList[i].ChildNodes[n]; break; } } if (propStat == null) { continue; } for (int o = 0; o < propStat.ChildNodes.Count; o++) { if (propStat.ChildNodes[o].Name == "a:prop") { properties = propStat.ChildNodes[o].ChildNodes; break; } } if (properties == null) { continue; } for (int j = 0; j < properties.Count; j++) { if (properties[j].Name == "d:x3703001f") { ext = properties[j].InnerText; } if (properties[j].Name == "e:attachmentfilename") { filename = properties[j].InnerText; } } if (ext.Length > 0 && filename.Length > 0) { //We have found an attachment that requires downloading. Log_Message("Found message attachment at " + href.InnerText, 1); //Create a new DataRow from the Strongly typed dataset InBox_AttachmentsDS hds = new InBox_AttachmentsDS(); InBox_AttachmentsDS.InBox_AttachmentsRow hdr = hds.Inbox_Attachments.NewInBox_AttachmentsRow(); //Fill the data row with the atttachment data hdr.InboxID = eiEntryID; //Get the mimetype of the file hdr.ContentType = GetMIMEType(ext); hdr.AttachNum = i; hdr.FileName = filename; //Download the data via http hdr.FileData = DownloadAttachment(href.InnerText); //Add the datarow via custom business object. if (InboxDLC.AttachmentAdd(this.EntLib, hdr, "Convertor")) { //Log on success Log_Message("Attachment " + filename + " added to " + this.EntLib + " with id:" + hdr.ID.ToString(), 1); } else { //Log on failure Log_Message("SQL Storage of " + filename + " FAILED to " + this.EntLib, EventLogEntryType.Error, 99); } } } } private void DeleteMessage(string webdavurl) { //Connects to Exchange and deletes the message at the specified URL HttpWebRequest request = (HttpWebRequest)WebRequest.Create(webdavurl); request.Method = "DELETE"; //Put in the credentials to connect to the Exchange mailbox request.Credentials = new NetworkCredential(this.ex_user, this.ex_password); //Get the response in a response object HttpWebResponse response = (HttpWebResponse)request.GetResponse(); if (response.StatusCode == HttpStatusCode.OK) { //Log success Log_Message("Message at " + webdavurl + " removed from Exchange Message Store", 1); } else { //Log Failure Log_Message("Deletetion Failed for message:" + webdavurl + " with status code " + response.StatusCode.ToString(), EventLogEntryType.Error, 99); } } private byte[] DownloadAttachment(string url) { //connect to server and download file Stream stream = null; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); request.Credentials = new NetworkCredential(this.ex_user, this.ex_password); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); stream = response.GetResponseStream(); return StreamtoByteArray(stream, 32768); } #endregion #region Utils and Logging private byte[] StreamtoByteArray(Stream stream, int initialLength) { byte[] buffer = new byte[initialLength]; int read = 0; int chunk; while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0) { read += chunk; /* If we've reached the end of our buffer, check to see if there's any more information */ if (read == buffer.Length) { int nextByte = stream.ReadByte(); /* End of stream? If so, we're done */ if (nextByte == -1) { return buffer; } /* Nope. Resize the buffer, put in the byte we've just read, and continue */ byte[] newBuffer = new byte[buffer.Length * 2]; Array.Copy(buffer, newBuffer, buffer.Length); newBuffer[read] = (byte)nextByte; buffer = newBuffer; read++; } } /* Buffer is now too big. Shrink it. */ byte[] ret = new byte[read]; Array.Copy(buffer, ret, read); return ret; } private string GetMIMEType(string dotExt) { //Look in the registry and try to find the MIMEType for the extension of the file. RegistryKey rkContentTypes = Registry.ClassesRoot.OpenSubKey(dotExt); if (rkContentTypes != null) { object key = rkContentTypes.GetValue("Content Type", "binary/octet-stream"); return key.ToString(); } else { return "binary/octet-stream"; } } public void Log_Message(string message, EventLogEntryType type, int MessageLevel) { if (MessageLevel > LogLevel) { this.log.WriteEntry(message, type); } } public void Log_Message(string message, int MessageLevel) { this.Log_Message(message, EventLogEntryType.Information, MessageLevel); } public void Log_Message(Exception e) { this.Log_Message(e.Message + "\n" + e.StackTrace, EventLogEntryType.Error, 99); } #endregion } }