Creating a RSS feed of an Exchange 2007 Mailbox Folder using Exchange Web Services C# and Powershell
One of the other new things I’ve used in this Code is one of the cooler new features of Exchange Web Services which is Impersonation. Previously if you wanted to write code that was going to run against a mailbox that was being run under the security context of an account that wasn’t the owner of that mailbox that the account in question would need to be given rights to this mailbox eg via delegation or AD Users and Computers etc. With Impersonation you still have to grant rights for another account to access a mailbox other then its own but these rights are just specific to EWS impersonation so granting rights in this way means that the account can use EWS to access a mailbox but it cant use OWA or Outlook (or any other API). So from a security standpoint this is pretty desirable and gives you much more leverage in your applications (and keeps those auditors happy). The Exchange SDK has details on how to give an account Impersonation rights it involves granting two rights the first is on the Server which allows a user to submit impersonation calls and the second is either on the user account you wish to access itself or the Mailbox database if you wanted to access all mailboxes within a particular mail store see. Using Impersonation is relatively easy you can use the UPN, SID or SMTP address of the account you want to access I went for the SMTP address. The impersonation information is added to the SOAP header eg in the powershell script the following represents adding the SOAP header for impersation
+ "<t:ExchangeImpersonation>" `
+ "<t:ConnectingSID>" `
+ "<t:PrimarySmtpAddress>" + $mbMailboxToAccess + "</t:PrimarySmtpAddress>" `
+ "</t:ConnectingSID>" `
+ "</t:ExchangeImpersonation>" `
+ "</soap:Header>" `
+ "<soap:Body>" `
Another thing to note in this script is in the Link node of each RSS item I put a link into so if you double clicked on the item it would open the original email in OWA. Now with Exchange 2003/2000 you could just use the Href value of the email in question and OWA would render the item okay. With OWA on 2007 this doesn’t work instead you need to use the EntryID of the Item which you can get from the FindItem request. The one thing I did find is that this itemID cant be used as is from the FindItem request and needed to be transposed a bit to make it work. I’m not sure if this will vary between servers or not and there’s no documentation on this type of thing so its really a best guess. What I used did work for me on multiple mailboxes on the same server.
I stared out writing this as a WebService in C# using the EWS proxy objects but I decided I’d much rather have it as a powershell script as it would be easy to schedule and run so I ended up creating a C# version and a Powershell version. Both versions require 5 variables to be set
$unUserName = "Username"
$psPassword = "password"
$dnDomainName = "domain"
This is the Username and Password of the account you will be using to do the Impersonation. (You don’t have to hardcode them if you can use NTLM)
$mbMailboxToAccess = user@smtpdomain.com
The code aggregates the last 7 days worth or items in the inbox this could be changed to another folder other then the inbox (if you wanted) or the number of days to aggregate can be changed by modifying the following line.
+ "<t:Constant Value=`"" +
$datetimetoquery.ToUniversalTime().AddDays(-7).ToString("yyyy-MM-ddThh:mm:ssZ")
+ "`"/>"`
I’ve put a download of the C# code and powershell script here the script itself looks like.
function
GetItem($smSoapMessage){
$bdBodytext = ""
$WDRequest1 = [System.Net.WebRequest]::Create($strRootURI)
$WDRequest1.ContentType = "text/xml"
$WDRequest1.Headers.Add("Translate", "F")
$WDRequest1.Method = "Post"
$WDRequest1.Credentials = $cdUsrCredentials
$bytes1 = [System.Text.Encoding]::UTF8.GetBytes($smSoapMessage)
$WDRequest1.ContentLength = $bytes1.Length
$RequestStream1 = $WDRequest1.GetRequestStream()
[void]$RequestStream1.Write($bytes1, 0, $bytes1.Length)
[void]$RequestStream1.Close()
$WDResponse1 = $WDRequest1.GetResponse()
$ResponseStream1 = $WDResponse1.GetResponseStream()
$ResponseXmlDoc1 = new-object System.Xml.XmlDocument
$ResponseXmlDoc1.Load($ResponseStream1)
$tbBodyNodes = @($ResponseXmlDoc1.getElementsByTagName("t:Body"))
for($itemNums=0;$itemNums -lt $tbBodyNodes.Count;$itemNums++){
$bdBodytext = $tbBodyNodes[$itemNums].'#text'.ToString()
}
return $bdBodytext
}
$snServername = "servername"
$unUserName = "user"
$psPassword = "password"
$dnDomainName = "domain"
$mbMailboxToAccess = "user@smtpdomain.com"
$cdUsrCredentials = new-object System.Net.NetworkCredential($unUserName , $psPassword
, $dnDomainName)
$xsXmlFileName = "c:\feedname.xml"
[System.Reflection.Assembly]::LoadWithPartialName("System.Web") > $null
$xrXmlWritter = new-object
System.Xml.XmlTextWriter($xsXmlFileName,[System.Text.Encoding]::UTF8)
$xrXmlWritter.WriteStartDocument()
$xrXmlWritter.WriteStartElement("rss")
$xrXmlWritter.WriteAttributeString("version", "2.0")
$xrXmlWritter.WriteStartElement("channel")
$xrXmlWritter.WriteElementString("title", "Inbox Feed For " + $mbMailboxToAccess)
$xrXmlWritter.WriteElementString("link", "https://" + $snServerName + "/owa/")
$xrXmlWritter.WriteElementString("description", "Exchange Inbox Feed For" +
$mbMailboxToAccess)
$datetimetoquery = get-date
$smSoapMessage = "<?xml version='1.0' encoding='utf-8'?>" `
+ "<soap:Envelope xmlns:soap=`"http://schemas.xmlsoap.org/soap/envelope/`" " `
+ " xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`"
xmlns:xsd=`"http://www.w3.org/2001/XMLSchema`"" `
+ " xmlns:t=`"http://schemas.microsoft.com/exchange/services/2006/types`" >" `
+ "<soap:Header>" `
+ "<t:ExchangeImpersonation>" `
+ "<t:ConnectingSID>" `
+ "<t:PrimarySmtpAddress>" + $mbMailboxToAccess + "</t:PrimarySmtpAddress>" `
+ "</t:ConnectingSID>" `
+ "</t:ExchangeImpersonation>" `
+ "</soap:Header>" `
+ "<soap:Body>" `
+ "<FindItem
xmlns=`"http://schemas.microsoft.com/exchange/services/2006/messages`" " `
+ "xmlns:t=`"http://schemas.microsoft.com/exchange/services/2006/types`"
Traversal=`"Shallow`"> " `
+ "<ItemShape>" `
+ "<t:BaseShape>AllProperties</t:BaseShape>" `
+ "<AdditionalProperties
xmlns=""http://schemas.microsoft.com/exchange/services/2006/types"">" `
+ "<ExtendedFieldURI PropertyTag=""0x3FD9"" PropertyType=""String"" />" `
+ "<ExtendedFieldURI PropertyTag=""0x10F3"" PropertyType=""String"" />" `
+ "<ExtendedFieldURI PropertyTag=""0x0C1A"" PropertyType=""String"" />" `
+ "</AdditionalProperties>" `
+ "</ItemShape>" `
+ "<Restriction>" `
+ "<t:IsGreaterThanOrEqualTo>" `
+ "<t:FieldURI FieldURI=`"item:DateTimeSent`"/>"`
+ "<t:FieldURIOrConstant>" `
+ "<t:Constant Value=`"" +
$datetimetoquery.ToUniversalTime().AddDays(-7).ToString("yyyy-MM-ddThh:mm:ssZ")
+ "`"/>"`
+ "</t:FieldURIOrConstant>"`
+ "</t:IsGreaterThanOrEqualTo>"`
+ "</Restriction>"`
+ "<ParentFolderIds>" `
+ "<t:DistinguishedFolderId Id=`"inbox`"/>" `
+ "</ParentFolderIds>" `
+ "</FindItem>" `
+ "</soap:Body></soap:Envelope>"
$strRootURI = "https://" + $snServername + "/ews/Exchange.asmx"
$WDRequest = [System.Net.WebRequest]::Create($strRootURI)
$WDRequest.ContentType = "text/xml"
$WDRequest.Headers.Add("Translate", "F")
$WDRequest.Method = "Post"
$WDRequest.Credentials = $cdUsrCredentials
$bytes = [System.Text.Encoding]::UTF8.GetBytes($smSoapMessage)
$WDRequest.ContentLength = $bytes.Length
$RequestStream = $WDRequest.GetRequestStream()
$RequestStream.Write($bytes, 0, $bytes.Length)
$RequestStream.Close()
$WDResponse = $WDRequest.GetResponse()
$ResponseStream = $WDResponse.GetResponseStream()
$ResponseXmlDoc = new-object System.Xml.XmlDocument
$ResponseXmlDoc.Load($ResponseStream)
$subjectnodes = @($ResponseXmlDoc.getElementsByTagName("t:Subject"))
$FromNodes = @($ResponseXmlDoc.getElementsByTagName("t:Name"))
$SentNodes = @($ResponseXmlDoc.getElementsByTagName("t:DateTimeSent"))
$SizeNodes = @($ResponseXmlDoc.getElementsByTagName("t:Size"))
$IDNodes = @($ResponseXmlDoc.getElementsByTagName("t:ItemId"))
$dsDescription = @($ResponseXmlDoc.getElementsByTagName("t:Value"))
for($i=0;$i -lt $subjectnodes.Count;$i++){
$Senttime = [System.Convert]::ToDateTime($SentNodes[$i].'#text'.ToString())
$Senttime.ToString() + " " + $FromNodes[$i].'#text' + " " +
$subjectnodes[$i].'#text' + " " + $SizeNodes[$i].'#text'
$IdNodeID = $IDNodes[$i].GetAttributeNode("Id")
$ckChangeKey = $IDNodes[$i].GetAttributeNode("ChangeKey")
$smSoapMessage = "<?xml version='1.0' encoding='utf-8'?>" `
+ "<soap:Envelope xmlns:soap=`"http://schemas.xmlsoap.org/soap/envelope/`" "
`
+ " xmlns:xsi=`"http://www.w3.org/2001/XMLSchema-instance`"
xmlns:xsd=`"http://www.w3.org/2001/XMLSchema`"" `
+ " xmlns:t=`"http://schemas.microsoft.com/exchange/services/2006/types`" >"
`
+ "<soap:Header>" `
+ "<t:ExchangeImpersonation>" `
+ "<t:ConnectingSID>" `
+ "<t:PrimarySmtpAddress>" + $mbMailboxToAccess + "</t:PrimarySmtpAddress>"
`
+ "</t:ConnectingSID>" `
+ "</t:ExchangeImpersonation>" `
+ "</soap:Header>" `
+ "<soap:Body><GetItem
xmlns=`"http://schemas.microsoft.com/exchange/services/2006/messages`"><ItemShape>"
`
+ "<BaseShape
xmlns=`"http://schemas.microsoft.com/exchange/services/2006/types`">Default</BaseShape></ItemShape>"
`
+ "<ItemIds><ItemId Id=`"" + $IdNodeID.'#text' + "`"" `
+ " xmlns=`"http://schemas.microsoft.com/exchange/services/2006/types`"
/></ItemIds></GetItem></soap:Body>" `
+ "</soap:Envelope>"
$xrXmlWritter.WriteStartElement("item")
$xrXmlWritter.WriteElementString("title", $subjectnodes[$i].'#text')
$xrXmlWritter.WriteElementString("link", "https://" + $snServername +
"/owa/?ae=Item&t=IPM.Note&id=Rg" +
[System.Web.HttpUtility]::UrlEncode($IdNodeID.'#text').Substring(58).Replace("%3d","J"))
$xrXmlWritter.WriteElementString("author", $FromNodes[$i].'#text')
$xrXmlWritter.WriteStartElement("description")
$xrXmlWritter.WriteRaw("<![CDATA[")
$bdBodytext = GetItem($smSoapMessage)
$xrXmlWritter.WriteRaw($bdBodytext)
$xrXmlWritter.WriteRaw("]]>")
$xrXmlWritter.WriteEndElement()
$xrXmlWritter.WriteElementString("pubDate", $Senttime.ToString("r"))
$xrXmlWritter.WriteElementString("guid", $IdNodeID.'#text')
$xrXmlWritter.WriteEndElement()
}
$xrXmlWritter.WriteEndElement()
$xrXmlWritter.WriteEndElement()
$xrXmlWritter.WriteEndDocument()
$xrXmlWritter.Close()
"Done"