Programmatically accessing to OWA through Web Dav
I have been playing around a couple of days with WebDav and I found it really interesting how simple it was to retrieve and modify messages from OWA.
WebDav (World Wide Web Distributed Authoring and Versioning) is a standard that help us edit and manage files on remote servers through the http protocol. For example BizTalk 2000 uses WebDav to manage the schemas (XDR) of the messages. And as I said before Microsoft Exchange Server exposes the mail through WebDav.
It is really simple to get started to do some programming since there are many resources and samples on the web. But I found some posts that were a little bit misleading and could create some confusion.
Then if we have OWA configured to support Forms Authentication we are likely to bump into a "440 Login Timeout" error (even though it takes less than 1 second to show the error) The problem is that if forms are enabled we first need to authenticate first, grab the authentication cookies and pass it to the next request which will be our WebDav command(s). The catch here is that even though reusing the cookies the 440 error does not disappear. The trick (that at least worked for me) was to use the HttpCookieCollection instead of working with the cookies as plain strings.
You can find a full example of how to search using OWA-WebDav through the inbox for an email which contains a certain word in the subject, using both HttpWebRequest and HttpCookieCollection.
Imports System Imports System.Net Imports System.IO Imports System.Xml Imports System.Text.RegularExpressions Module Module1 ' Code to call the Authentication: Private CookieJar As CookieContainer Private strCookies As String Private WebReq As HttpWebRequest Sub Main() Dim strServerName As String = "<ENTER SERVER NAME>" Dim strDomain As String = "<ENTER DOMAIN NAME>" Dim strUserID As String = "<ENTER USER ID>" Dim strPassword As String = "<ENTER PASSWORD>" Dim strUserName As String = "<ENTER USER NAME>" Dim term As String = "<ENTER A STRING TO SEARCH>" ' Create our destination URL. Dim strURL As String = "https://" & strServerName & "/exchange/" & strUserName & "/inbox" ' Enter your WebDAV-related code here. Dim QUERY As String = "<?xml version=""1.0""?>" _ & "<g:searchrequest xmlns:g=""DAV:"">" _ & "<g:sql>SELECT ""urn:schemas:httpmail:subject"", " _ & """urn:schemas:httpmail:from"", ""DAV:displayname"", " _ & """urn:schemas:httpmail:textdescription"" " _ & "FROM SCOPE('deep traversal of """ & strURL & """') " _ & "WHERE ""DAV:ishidden"" = False AND ""DAV:isfolder"" = False " _ & "AND ""urn:schemas:httpmail:subject"" LIKE '%" & term & "%' " _ & "ORDER BY ""urn:schemas:httpmail:date"" DESC" _ & "</g:sql></g:searchrequest>" Dim Response As System.Net.HttpWebResponse Dim RequestStream As System.IO.Stream Dim ResponseStream As System.IO.Stream Dim ResponseXmlDoc As System.Xml.XmlDocument Dim SubjectNodeList As System.Xml.XmlNodeList Dim SenderNodeList As System.Xml.XmlNodeList Dim BodyNodeList As System.Xml.XmlNodeList Dim URLNodeList As System.Xml.XmlNodeList Dim Request As HttpWebRequest = CType(System.Net.WebRequest.Create(strURL), _ System.Net.HttpWebRequest) Request.CookieContainer = New CookieContainer() Request.CookieContainer.Add(AuthenticateSecureOWA(strServerName, strDomain, strUserID, strPassword)) Request.Credentials = New System.Net.NetworkCredential( _ strUserName, strPassword, strDomain) Request.Method = "SEARCH" Request.ContentType = "text/xml" Request.KeepAlive = True Request.AllowAutoRedirect = False Dim bytes As Byte() = System.Text.Encoding.UTF8.GetBytes(QUERY) Request.Timeout = 30000 Request.ContentLength = bytes.Length RequestStream = Request.GetRequestStream() RequestStream.Write(bytes, 0, bytes.Length) RequestStream.Close() Request.Headers.Add("Translate", "F") Response = Request.GetResponse() ResponseStream = Response.GetResponseStream() ' Create the XmlDocument object from the XML response stream. ResponseXmlDoc = New System.Xml.XmlDocument() ResponseXmlDoc.Load(ResponseStream) SubjectNodeList = ResponseXmlDoc.GetElementsByTagName("d:subject") SenderNodeList = ResponseXmlDoc.GetElementsByTagName("d:from") URLNodeList = ResponseXmlDoc.GetElementsByTagName("a:href") BodyNodeList = ResponseXmlDoc.GetElementsByTagName("d:textdescription") End Sub Private Function AuthenticateSecureOWA(ByVal strServerName As String, ByVal strDomain As String, ByVal strUserName As String, ByVal strPassword As String) As CookieCollection Dim AuthURL As System.Uri Try ' Construct our destination URI. AuthURL = New System.Uri("https://" + strServerName + "/exchweb/bin/auth/owaauth.dll") Catch ex As Exception Throw NewApplicationException("Error occurred while you are creating the URI for OWA authentication!" + vbCrLf + vbCrLf + ex.Message) End Try 'Dim WebReq As HttpWebRequest CookieJar = New CookieContainer ' Create our request object for the constructed URI. WebReq = CType(WebRequest.Create(AuthURL), HttpWebRequest) WebReq.CookieContainer = CookieJar ' Create our post data string that is required by OWA (owaauth.dll). Dim strPostFields As String = "destination=https%3A%2F%2F" & strServerName & "%2Fexchange%2F" + strUserName + "%2F&username=" + strDomain + "%5C" + strUserName + "&password=" + strPassword + "&SubmitCreds=Log+On&forcedownlevel=0&trusted=0" WebReq.KeepAlive = True WebReq.AllowAutoRedirect = False WebReq.Method = "POST" ' Store the post data into a byte array. Dim PostData() As Byte = System.Text.Encoding.ASCII.GetBytes(strPostFields) ' Set the content length. WebReq.ContentLength = PostData.Length Dim tmpStream As Stream Try ' Create a request stream. Write the post data to the stream. tmpStream = WebReq.GetRequestStream() tmpStream.Write(PostData, 0, PostData.Length) tmpStream.Close() Catch ex As Exception Throw NewApplicationException("Error occurred while trying OWA authentication!" + vbCrLf + vbCrLf + ex.Message) End Try
' Get the response from the request. Dim WebResp As HttpWebResponse = WebReq.GetResponse() WebResp.Close() Return WebResp.Cookies End Function End Module
I hope you enjoyed the post (or at least found it useful).