Access Your Exchange 2000 / 2003 Mailbox With WebDAV

by Lee Derbyshire [Published on 14 Sept. 2004 / Last Updated on 14 Sept. 2004]

There are several Application Programming Interfaces (APIs) that you can use to access an Exchange 2000 or 2003 Mailbox. WebDAV is not the easiest to learn, but can often be the most convenient. This article is an introduction to the topic.

Introduction

There are several Application Programming Interfaces (APIs) that you can use to access an Exchange 2000 or 2003 Mailbox.  WebDAV is not the easiest to learn, but can often be the most convenient.  It doesn’t provide an intuitive, object-oriented interface like CDO, but it has two great advantages:  You can use it to access a Mailbox on a different computer (unlike ADO), and it requires only http port connectivity between client and server (unlike CDO, which requires ports that are nowadays considered unsafe).  WebDAV requires only http connectivity because it is an extension of the http protocol.  When you use a Web browser to access a document on the Web, the browser sends a GET request to the document’s URL.  WebDAV adds extra functionality by providing extra verbs such as SEARCH, PROPFIND, PROPPATCH, MKCOL, etc.  Since the Exchange 2000/2003 store was designed to provide access by WebDAV, each folder, message, appointment, contact, etc., has a URL that we can use to access it with.  Of course, adding extra functionality to a commonly used protocol in this way also has security implications, so WebDAV is disabled by default on most IIS servers, but disabling it on an Exchange server would seriously interfere with its operation, and is not usually done.

WebDAV In Practice

This article will attempt to explain how you can access the contents of your mailbox using a Web browser and some HTML/VBScript code.  The program code in its entirety is available for copying/pasting at the end of the article, but some parts will be reproduced within the article for illustration.  Spaces are added at the beginning of some lines to emphasize the program structure, but this is optional.  It will help to understand what is happening if you print out the code that appears at the end before reading the text.

The code requires the use of the Microsoft.XMLHTTP object, and should run on most Windows 2000 or Windows XP PCs.  To run the code (which you will save in an .htm file), you just load it into Internet Explorer (usually by double-clicking it, if IE is your default browser), and type in the name of your mailbox folder.  The name of your mailbox folder is almost always the same as your network logon ID.  If you see a ‘404 – Resource Not Found’ response on the display, then you have not supplied the correct folder name.  The best way to be 100% sure what the name of your mailbox folder is, is to log on to Outlook Web Access, and hover over your Inbox icon.  The mailbox folder name should be shown in the URL displayed in the status bar between ‘http://servername/exchange/’, and the name of your inbox folder (usually ‘/Inbox/’).  In the code that follows, the term ‘username’ will sometimes appear instead of ‘folder name’, since they are nearly always identical.

The first parts of the code consist of a few subroutines; getMessages_Onclick(), checkXMLHTTPState, and responseXSL .  These are not executed until you have typed in the mailbox folder name.  The first part you actually see on the screen is right at the bottom of the code:

  Mailbox name:<br>
    <input type='text' name='mailbox'><br>
      <input type='button' name='getMessages' value='    GO    '><br>
    <div id='responseStatus'></div>
       <div id='XSLDiv'></div>

This just displays an input box on the screen (figure 1), and a button with the word 'GO' on it.  The two <div>s are placeholders for the program output.


Fig 1:
The initial browser display

The name of the button is called ‘getMessages’, so clicking on it causes the code in getMessages_OnClick() to be executed.

   Sub getMessages_OnClick()
    strUsername = document.all.mailbox.value
     If strUsername <> "" Then
     strInboxURL = strProtocol & "://" & strServername & "/Exchange/"
     strInboxURL = strInboxURL & strUsername & "/" & strInbox
     Set objXMLHTTP = CreateObject("Microsoft.XMLHTTP")
      objXMLHTTP.Open "SEARCH", strInboxURL, True
      objXMLHTTP.setRequestHeader "Content-type:", "text/xml"
      objXMLHTTP.setRequestHeader "Depth", "1"
      objXMLHTTP.onReadyStateChange = getRef("checkXMLHTTPState")
        strXML = "<?xml version='1.0' ?>" & _
        "<a:searchrequest xmlns:a='DAV:'><a:sql>" & _
         "SELECT" & _
         " ""DAV:href""" & _
         ",""urn:schemas:httpmail:subject""" & _
         " FROM scope('shallow traversal of """ & strInboxURL & """')" & _
        " WHERE ""DAV:ishidden""=False" & _
        " AND ""DAV:isfolder""=False" & _
       "</a:sql></a:searchrequest>"
      objXMLHTTP.SetRequestHeader "Range", "rows=0-9"
      objXMLHTTP.Send(strXML)
     End If
   End Sub

This code first checks that the input box (named mailbox) actually has a name typed into it.  If it has, then it will build a target for the WebDAV request of the form http://SERVERNAME/Exchange/username/Inbox .  The WebDAV request is made by the Microsoft.XMLHTTP object named objXMLHTTP.  This object is, in a way, comparable to a Web browser, except that the target URL is programmable (rather than typed in), and the server’s response is retained as one of its properties (instead of displayed on the screen).  The other feature is that it can request responses to verbs other than GET (i.e. the WebDAV ones that we are interested in).

The code causes the following XML text to be sent as part of the request:

  <?xml version='1.0' ?>
   <a:searchrequest xmlns:a='DAV:'><a:sql>
    SELECT ""DAV:href"",""urn:schemas:httpmail:subject""
      FROM scope('shallow traversal of ""http://SERVERNAME/exchange/Username/Inbox""')
    WHERE ""DAV:ishidden""=False AND ""DAV:isfolder""=False
  </a:sql></a:searchrequest>

Those of you who are familiar with SQL will see that the SEARCH request sent to the server is very similar to a SQL statement.  In fact, Exchange recognizes a specialized form of SQL statement designed to access the hierarchical store structure.  The statement is designed to return the DAV:href and urn:schemas:httpmail:subject fields of the 10 most recent non-hidden, non-folder items in the Inbox.  Why do the DAV:href and urn:schemas:httpmail:subject fields have such strange names, you may ask.  The names take the forms of URIs (Uniform Resource Identifiers), which simply ensures that they have a uniquely and precisely defined meaning.

Note the use of namespaces in this request.  A namespace declaration like xmlns:a='DAV:' means that the term ‘DAV’ is replaced throughout the text by the letter ‘a’.  This helps to reduce the size of the request.  Also, note the use of SetRequestHeader "Range", "rows=0-9" .  This is what limits the number of returned rows in this example to 10 .

When the objXMLHTTP object makes the request it may cause a Network Login box to be displayed by the browser.

In response to this SEARCH request, Exchange returns an XML text string something like this:

    <?xml version="1.0"?>
     <a:multistatus xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" xmlns:d="urn:schemas:httpmail:" xmlns:c="xml:" xmlns:a="DAV:">
    <a:contentrange>0-9</a:contentrange>
    <a:response>
    <a:href>http://w2ks1/Exchange/Administrator/Inbox/Undeliverable:re[12]-2.EML</a:href>
    <a:propstat>
    <a:status>HTTP/1.1 200 OK</a:status>
    <a:prop>
       <a:href>http://w2ks1/Exchange/Administrator/Inbox/Undeliverable:re[12]-2.EML</a:href>
       <d:subject>Undeliverable:re[12]</d:subject>
    </a:prop>
    </a:propstat>
  </a:response>
 </a:multistatus>

This example has been edited for simplicity to contain the data for only one message – it would normally be much longer, with data for other messages between <a:prop> and </a:prop>.

When the objXMLHTTP object has received a response (it’s status takes the value 4), then the code in checkXMLHTTPState is executed:

   Sub checkXMLHTTPState
    If objXMLHTTP.readyState = 4 Then
     responseStatus.innerHTML = objXMLHTTP.Status & " - " & objXMLHTTP.StatusText
      Set objXMLDoc = objXMLHTTP.ResponseXML
      XSLDiv.innerHTML = objXMLDoc.TransformNode(responseXSL.documentElement)
      Set objXMLHTTP = Nothing
      Set objXMLDoc = Nothing
    End If
  End Sub

This code simply displays the HTTP status code of the response on the screen, and prepares the returned XML response for display by the XSL (Extensible Style Sheets) code in responseXSL .

  <xsl:template xmlns:xsl='uri:xsl' xmlns:a='DAV:' xmlns:d='urn:schemas:httpmail:'>
    <table>
     <!-- Add a row for each element in the 207 Multistatus response to the SEARCH request. -->
      <xsl:for-each select='a:multistatus/a:response'>
       <tr>
        <!-- Build a hyperlink using the resource href and subject -->
         <td>
          <a>
           <xsl:attribute name='href'>
            <xsl:value-of select='a:propstat/a:prop/a:href' />/?Cmd=open
           </xsl:attribute>
          <xsl:attribute name='target'>_blank</xsl:attribute>
         <xsl:value-of select='a:propstat/a:prop/d:subject' />
        </a>
       </td>
      </tr>
     </xsl:for-each>
    </table>
   </xsl:template>

This code uses the returned DAV:href and urn:schemas:httpmail:subject values to build a table of hyperlinks to the returned messages, and places the table in the placeholder <div> named XSLDiv .  Each hyperlink looks something like this:

  <a href='http://SERVERNAME/Exchange/Username/Inbox/Subject.EML/?Cmd=open' target='_blank'>Message Subject</a>

On the screen, you should see a list of Inbox messages (figure 2):


Fig 2:
Hyperlinks to the messages in the Inbox

Each link is a direct link into the Exchange store, and clicking on one will produce the output normally displayed when clicking on a message in Outlook Web Access.  The target='_blank' attribute tells the browser to open the message in a new window (figure 3).


Fig 3:
A message displayed in a new browser window

Simply close the message window to return to the list of items.

Conclusion

This article has been a brief introduction to the principles of Exchange WebDAV programming, and a few other related topics.  If you want to learn more about this subject, the best way to obtain up-to-date information is to go to http://msdn.microsoft.com , and search for relevant terms, such as WebDAV, Microsoft.XMLHTTP, XSL, etc.

Below is the complete code for the article.  Copy and paste it into Notepad, change the line near the beginning that contains “YOUR_EXCHANGE_SERVER” to contain the name (or IP address) of your own Exchange server, change the values of “http” and “Inbox” if necessary, and then save it with an .htm extension, something like ‘WebDAVexample.htm’.

<html>
<head>
<script language='VBScript'>
Dim objXMLHTTP, objXMLDoc
' Define your protocol; http or https
strProtocol = "http"
' Define your server name
strServername = "YOUR_EXCHANGE_SERVER"
' Define your local name for 'Inbox'
strInbox = "Inbox"
Sub getMessages_OnClick()
  strUsername = document.all.mailbox.value
  If strUsername <> "" Then
    strInboxURL = strProtocol & "://" & strServername & "/Exchange/"
    strInboxURL = strInboxURL & strUsername & "/" & strInbox
    Set objXMLHTTP = CreateObject("Microsoft.XMLHTTP")
    objXMLHTTP.Open "SEARCH", strInboxURL, True
    objXMLHTTP.setRequestHeader "Content-type:", "text/xml"
    objXMLHTTP.setRequestHeader "Depth", "1"
    objXMLHTTP.onReadyStateChange = getRef("checkXMLHTTPState")
    strXML = "<?xml version='1.0' ?>" & _
     "<a:searchrequest xmlns:a='DAV:'><a:sql>" & _
       "SELECT" & _
       " ""DAV:href""" & _
       ",""urn:schemas:httpmail:subject""" & _
       " FROM scope('shallow traversal of """ & strInboxURL & """')" & _
      " WHERE ""DAV:ishidden""=False" & _
      " AND ""DAV:isfolder""=False" & _
     "</a:sql></a:searchrequest>"
    objXMLHTTP.SetRequestHeader "Range", "rows=0-9"
    objXMLHTTP.Send(strXML)
   End If
 End Sub

Sub checkXMLHTTPState
  If objXMLHTTP.readyState = 4 Then
    responseStatus.innerHTML = objXMLHTTP.Status & " - " & objXMLHTTP.StatusText
    Set objXMLDoc = objXMLHTTP.ResponseXML
    XSLDiv.innerHTML = objXMLDoc.TransformNode(responseXSL.documentElement)
    Set objXMLHTTP = Nothing
    Set objXMLDoc = Nothing
  End If
End Sub

</script>
<xml id='responseXSL'>
  <xsl:template xmlns:xsl='uri:xsl' xmlns:a='DAV:' xmlns:d='urn:schemas:httpmail:'>
    <table>
      <!-- Add a row for each element in the 207 Multistatus response to the SEARCH request. -->
      <xsl:for-each select='a:multistatus/a:response'>
        <tr>
          <!-- Build a hyperlink using the resource href and subject -->
          <td>
            <a>
              <xsl:attribute name='href'>
                <xsl:value-of select='a:propstat/a:prop/a:href' />/?Cmd=open
              </xsl:attribute>
              <xsl:attribute name='target'>_blank</xsl:attribute>
              <xsl:value-of select='a:propstat/a:prop/d:subject' />
            </a>
          </td>
        </tr>
      </xsl:for-each>
    </table>
  </xsl:template>
</xml>

</head>
<body>
<font face='Verdana' size='2'>
Mailbox name:<br>
<input type='text' name='mailbox'><br>
<input type='button' name='getMessages' value='    GO    '><br>
<div id='responseStatus'></div>
<div id='XSLDiv'></div>
</body>
</html>

Featured Links