Other Posts in Exchange

  1. Getting Free/Busy Data from an Exchange Server
  2. Getting a Users Contacts List from an Exchange Server
  3. Getting the GAL from Exchange in C#
  4. Using WebDAV to Get Calendar Items (Appointments) from Exchange in C#/ASP.Net

Using WebDAV to Get Calendar Items (Appointments) from Exchange in C#/ASP.Net

11/7/2008

I'm a big believer in integration of pretty much everything. Not that big on mash ups (as most of them are terrible) but I believe that information should be easily used in various systems. That's part of the reason that I like Microsoft products now (during the 90s, I was on the other end of the spectrum though so I'm happy with the changes they've been making). I especially like Exchange because it is extremely easy to pull information from it for your own purposes.

In my instance, I had to create a personalized calendar that pulled information from exchange and displayed it on an intranet page. With the help of WebDAV, it took about 10 minutes to write (and since people only come here for the code, I figured I'd share):

   1: /// <summary>
   2: /// Returns a list of the user's calendar items.
   3: /// </summary>
   4: /// <param name="UserName">User name used for logging in</param>
   5: /// <param name="Password">Password for logging in</param>
   6: /// <param name="Directory">User's directory</param>
   7: /// <param name="StartDate">Date to start at</param>
   8: /// <param name="EndDate">Date to end at</param>
   9: /// <param name="Server">Server Name</param>
  10: /// <returns>A list of the user's calendar items in VCalendar format, between two date ranges</returns>
  11: public static List<VCalendar> GetCalendarItems(string UserName, string Password, string Server, string Directory, DateTime StartDate, DateTime EndDate)
  12: {
  13:     List<VCalendar> ReturnArray = new List<VCalendar>();
  14:     string Uri = string.Format("http://{0}/exchange/{1}/calendar/", Server, Directory);
  15:  
  16:     string Query = "<?xml version=\"1.0\"?>"
  17:          + "<g:searchrequest xmlns:g=\"DAV:\">"
  18:          + "<g:sql>SELECT \"urn:schemas:calendar:location\", \"urn:schemas:httpmail:subject\", "
  19:          + "\"urn:schemas:httpmail:textdescription\", "
  20:          + "\"urn:schemas:calendar:dtstart\", \"urn:schemas:calendar:dtend\", "
  21:          + "\"urn:schemas:calendar:busystatus\", \"urn:schemas:calendar:instancetype\" "
  22:          + "FROM Scope('SHALLOW TRAVERSAL OF \"" + Uri + "\"') "
  23:          + "WHERE ((\"urn:schemas:calendar:dtstart\" &gt;= '" + StartDate.ToString("yyyy/MM/dd hh:mm:ss") + "' "
  24:          + "AND \"urn:schemas:calendar:dtstart\" &lt;= '" + EndDate.ToString("yyyy/MM/dd hh:mm:ss") + "') "
  25:          + "OR (\"urn:schemas:calendar:dtend\" &gt;= '" + StartDate.ToString("yyyy/MM/dd hh:mm:ss") + "' "
  26:          + "AND \"urn:schemas:calendar:dtstart\" &lt;= '" + EndDate.ToString("yyyy/MM/dd hh:mm:ss") + "')) "
  27:          + "AND \"DAV:contentclass\" = 'urn:content-classes:appointment' "
  28:          + "AND NOT \"urn:schemas:calendar:instancetype\" = 1 "
  29:          + "ORDER BY \"urn:schemas:calendar:dtstart\" ASC"
  30:          + "</g:sql></g:searchrequest>";
  31:     byte[] Contents = System.Text.Encoding.UTF8.GetBytes(Query);
  32:  
  33:     HttpWebRequest Request = HttpWebRequest.Create(Uri) as HttpWebRequest;
  34:     System.Net.CredentialCache MyCredentialCache = new System.Net.CredentialCache();
  35:     if (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password))
  36:     {
  37:         MyCredentialCache.Add(new System.Uri(Uri),
  38:                            "NTLM",
  39:                            new System.Net.NetworkCredential(UserName, Password));
  40:     }
  41:     else
  42:     {
  43:         MyCredentialCache.Add(new System.Uri(Uri),
  44:                                "Negotiate",
  45:                                (System.Net.NetworkCredential)CredentialCache.DefaultCredentials);
  46:     }
  47:     Request.Credentials = MyCredentialCache;
  48:     Request.Method = "SEARCH";
  49:     Request.ContentLength = Contents.Length;
  50:     Request.ContentType = "text/xml";
  51:  
  52:     using (System.IO.Stream RequestStream = Request.GetRequestStream())
  53:     {
  54:         RequestStream.Write(Contents, 0, Contents.Length);
  55:  
  56:         using (HttpWebResponse Response = Request.GetResponse() as HttpWebResponse)
  57:         {
  58:             using (System.IO.Stream ResponseStream = Response.GetResponseStream())
  59:             {
  60:                 XmlDocument Document = new XmlDocument();
  61:                 Document.Load(ResponseStream);
  62:                 foreach (XmlElement Element in Document.GetElementsByTagName("a:prop"))
  63:                 {
  64:                     VCalendar Cal = new VCalendar();
  65:                     if (Element["e:textdescription"] != null)
  66:                     {
  67:                         Cal.Description = Element["e:textdescription"].InnerText;
  68:                     }
  69:                     if (Element["e:subject"] != null)
  70:                     {
  71:                         Cal.Subject = Element["e:subject"].InnerText;
  72:                     }
  73:                     if (Element["d:location"] != null)
  74:                     {
  75:                         Cal.Location = Element["d:location"].InnerText;
  76:                     }
  77:                     if (Element["d:dtstart"] != null)
  78:                     {
  79:                         Cal.StartTime = DateTime.Parse(Element["d:dtstart"].InnerText);
  80:                     }
  81:                     if (Element["d:dtend"] != null)
  82:                     {
  83:                         Cal.EndTime = DateTime.Parse(Element["d:dtend"].InnerText);
  84:                     }
  85:                     ReturnArray.Add(Cal);
  86:                 }
  87:             }
  88:         }
  89:     }
  90:     return ReturnArray;
  91: }

So let's talk about the code a bit. First off, it's using my VCalendar code from my utility library. Well actually it's using a slightly different version that contains a GethCalendar function. I'll talk about hCalendar items in a different post though... So beyond that, you'll notice that there is a large section that sets up the Query string object. That's the actual WebDAV query. It's very similar to SQL but you call fields based on the schemas that were set up (I'm using the urn:schemas:calendar schema the most in this bit of code). We also ask it to only give us items within the start and end dates (you'll notice I use &gt; and &lt;. The reason for this is pretty simple... It's XML that we're sending. XML+ > or < = bad...).

After the query itself we simply set up an HttpWebRequest object with the proper user name/password, content (in bytes), etc. We make the call to the exchange server and it returns to us a nice big chunk of XML in return. If you want to look through the structure of what it returns, go ahead (it's very basic). But the items themselves are located within a:prop objects. So we just get the list of those items and iterate through grabbing the information that we want. That's all there is to the code really. Anyway, I hope this helps someone out. So give it a try, leave feedback, and happy coding.



Comments

James Craig
October 27, 2010 8:43 AM

First off, WebDAV was dropped in the release of Exchange 2010 (note that this was posted in 2008). So if you're using Exchange 2010, you'll need to use Exchange Web Services instead. I haven't been able to get my hands on 2010 yet so I haven't been able to roll out that post. If you're using 2000 or 2003, then this should work (assuming you've set up your security, etc. correctly). As far as "Directory" is concerned, it should be the first part of the user's email address before the @ (whomever's calendar you want to view/edit). You're basically doing a search on the following location:http://YOUREXCHANGESERVER/exchange/USER'SEMAILADDRESS/calendar

Peter
October 27, 2010 7:59 AM

i'm getting zero items back, what is "Directory" supposed to be in this sample or what can it be?