How do I access my Outlook contacts from my web application?

Question

How do I access my Outlook contacts from my web application?

Short Answer

You don't, well at least you shouldn't (It is theoretically possible to access Outlook data from a web application, assuming Outlook is installed and the profile you wish to access data from is setup on the web server).

Long Answer

To address this question we need to realize that Outlook is a client application which is only a front end and local cache for a mail server (typically exchange server). Most web applications are server applications and thus run on servers which usually don't have client applications, such as Outlook, installed on them. Perhaps a better question would be "How do I access my Exchange contacts from my web application?". 

With that in mind lets explore a couple possible solutions for accessing data (not really Outlook data) from the Exchange server.

  • WebDAV - Protocol that Outlook Web Access (OWA) uses to access your Exchange store
  • Exchange Web Services (EWS) - New services added to Exchange 2007 to allow access to most data
  • Active Directory Services Interface (ADSI) - For accessing general Exchange user data information (i.e. access the Global Address List)

These are just three common technologies for accessing data on a Exchange server, for more details see the Exchange Server Development Center or Overview of Programming Features for Exchange Server. To provide a more complete example I've included some code samples and links for each of the three technologies. The code examples demonstrate how to do find contacts using each of the three methods. Keep in mind these samples can be extended to other data types such as mail and appointments, with the exception of ADSI. All the code samples are written in C# and write the data to the console, they can easily be converted to be used by an ASP.Net web application. Also remember to substitute <ExchangeServer>, <username>, <password>, and <domain> with the appropriate values for your Exchange server.

WebDAV

The sample retrieves all contacts from an exchange store which have a first name beginning with 'wes'. Keep in mind before making any Exchange WebDAV requests you must make an forms authorization request first (assuming the Exchange server has forms authorization enabled, which seems to generally be the case). You need to make the authorization request to get the set of authorization cookies to use with any subsequent WebDAV requests.

using System;

using System.IO;

using System.Net;

using System.Text;

using System.Xml;

 

// When Exchange is setup for Forms Authentication you need to do the login separately

public static CookieCollection GetAuthCookies(string server, NetworkCredential credentials)

{

    // URI to OWA authorization dll

    string authURI = string.Format("{0}/exchweb/bin/auth/owaauth.dll",

        server, credentials.UserName);

 

    // Get byte stream of the post request

    byte[] bytes = Encoding.UTF8.GetBytes(string.Format(

        "destination={0}/exchange/{1}&username={2}\\{1}&password={3}",

        server, credentials.UserName, credentials.Domain, credentials.Password));

 

    HttpWebRequest request = WebRequest.Create(authURI) as HttpWebRequest;

    request.Method = "POST";

    request.ContentType = "application/x-www-form-urlencode";

    request.CookieContainer = new CookieContainer();

    request.ContentLength = bytes.Length;

    request.AllowAutoRedirect = false;

 

    using (Stream requestStream = request.GetRequestStream())

        requestStream.Write(bytes, 0, bytes.Length);

 

    // Get response cookies - keep in mind this may throw exceptions

    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)

        return response.Cookies;

}

public static void PrintContactsUsingExchangeWebDAV()

{

    string server = "http<s>://<ExchangeServer>";

    NetworkCredential credentials = new NetworkCredential("<username>", "<password>", "<domain>");

    string uri = string.Format("{0}/exchange/{1}", server, credentials.UserName);

 

    // Create a byte stream of the SQL query to run against the server

    // This query searches for contacts with the givenName the begins with 'wes'

    // Link to Exchange store property names

    byte[] contents = Encoding.UTF8.GetBytes(string.Format(

       @"<?xml version=""1.0""?>

        <g:searchrequest xmlns:g=""DAV:"">

            <g:sql>

                SELECT

                    ""urn:schemas:contacts:sn"", ""urn:schemas:contacts:givenName"",

                    ""urn:schemas:contacts:email1"", ""urn:schemas:contacts:telephoneNumber""

                FROM

                    Scope('SHALLOW TRAVERSAL OF ""{0}/exchange/{1}/contacts""')

                WHERE

                    ""urn:schemas:contacts:givenName"" LIKE 'wes%'

            </g:sql>

        </g:searchrequest>",

      server, credentials.UserName));

 

    HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest;

    request.Credentials = credentials;

    request.Method = "SEARCH";

    request.ContentLength = contents.Length;

    request.ContentType = "text/xml";

    request.CookieContainer = new CookieContainer();

    // Keep in mind you may actually want to cache these cookies for other requests

    request.CookieContainer.Add(GetAuthCookies(server, credentials));

 

    using (Stream requestStream = request.GetRequestStream())

        requestStream.Write(contents, 0, contents.Length);

 

    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)

    using (Stream responseStream = response.GetResponseStream())

    {

        // Process the response as an XML document

        XmlDocument document = new XmlDocument();

        document.Load(responseStream);

 

        foreach (XmlElement element in document.GetElementsByTagName("a:prop"))

        {

            // Do work with data returned for each contact

            Console.WriteLine("Name:  {0} {1}\nEmail: {2}\nPhone: {3}",

                (element["d:givenName"] != null ? element["d:givenName"].InnerText : ""),

                (element["d:sn"] != null ? element["d:sn"].InnerText : ""),

                (element["d:email1"] != null ? element["d:email1"].InnerText : ""),

                (element["d:telephoneNumber"] != null ? element["d:telephoneNumber"].InnerText : ""));

        }

    }

}

Helpful WebDAV Links:

Infinitec Exchange posts - C# and JavaScript forms authentication examples
Marc Charbonneau - Using .NET and WebDAV to access an Exchange server
Hitchhiker guide to enterprise development - Programmatically accessing to OWA through Web Dav
Exchange store property names

Exchange Web Services

This sample code goes through the process of creating a Exchange web service FindItem request. In essentially searches through the contact folder looking for any contact with first name starting with 'wes'.

using System;

using System.Net;

// Generate Exchange Web Services proxy classes by adding a VS web

// reference to http<s>://<ExchangeServer>/EWS/Services.wsdl

// then add a using reference to your proxy classes

 

public static void PrintContactsUsingExchangeWebServices()

{

    ExchangeServiceBinding esb = new ExchangeServiceBinding();

    esb.Credentials = new NetworkCredential("<username>", "<password>", "<domain>");

    esb.Url = @"http<s>://<ExchangServer>/EWS/Exchange.asmx";

 

    // Tell it you want all the item properties

    ItemResponseShapeType itemProperties = new ItemResponseShapeType();

    itemProperties.BaseShape = DefaultShapeNamesType.AllProperties;

 

    // Tell it you only want to look in the contacts folder

    DistinguishedFolderIdType[] folderIDArray = new DistinguishedFolderIdType[1];

    folderIDArray[0] = new DistinguishedFolderIdType();

    folderIDArray[0].Id = DistinguishedFolderIdNameType.contacts;

 

    PathToUnindexedFieldType field = new PathToUnindexedFieldType();

    field.FieldURI = UnindexedFieldURIType.contactsGivenName;

 

    ConstantValueType fieldValue = new ConstantValueType();

    fieldValue.Value = "wes";

 

    // Look for contacts which have a given name that begins with 'wes'

    ContainsExpressionType expr = new ContainsExpressionType();

    expr.ContainmentModeSpecified = true;

    expr.ContainmentMode = ContainmentModeType.Prefixed;

    expr.ContainmentComparisonSpecified = true;

    expr.ContainmentComparison = ContainmentComparisonType.IgnoreCase;

    expr.Constant = fieldValue;

    expr.Item = field;

 

    RestrictionType restriction = new RestrictionType();

    restriction.Item = expr;

 

    // Form the FindItem request

    FindItemType findItemRequest = new FindItemType();

    findItemRequest.Traversal = ItemQueryTraversalType.Shallow;

    findItemRequest.ItemShape = itemProperties;

    findItemRequest.ParentFolderIds = folderIDArray;

    findItemRequest.Restriction = restriction;

 

    // Send the request and get the response

    FindItemResponseType findItemResponse = esb.FindItem(findItemRequest);

 

    if (findItemResponse.ResponseMessages.Items.Length > 0)

    {

        FindItemResponseMessageType responseMessage =

            findItemResponse.ResponseMessages.Items[0] as FindItemResponseMessageType;

        ArrayOfRealItemsType realItems =

            responseMessage.RootFolder.Item as ArrayOfRealItemsType;

 

        foreach (ContactItemType contact in realItems.Items)

        {

            // Do work with data returned for each contact

            Console.WriteLine("Name:  {0} {1}\nEmail: {2}\nPhone: {3}",

                contact.GivenName, contact.Surname,

                (contact.EmailAddresses != null && contact.EmailAddresses.Length > 0 ?

                    contact.EmailAddresses[0].Value : ""),

                (contact.PhoneNumbers != null && contact.PhoneNumbers.Length > 0 ?

                    contact.PhoneNumbers[0].Value : ""));

        }

    }

}

Useful Exchange Web Service Links:

MSDN Exchange Web Services Reference
Stephen GriffinExchange Web Services and MAPI Props
Eric LeeExchange Server 2007 Web Service API for Developers

Active Directory Services Interface

This is a very simplified .Net sample of doing a search in the default active directory for any users that have a first name beginning with 'wes'. Keep in mind that this uses the default active directory on the machine you are running on, see DirectorySearcher.SearchRoot for more details. If you need to specify a particular machine/directory to start your search from then you need to create a DirectoryEntry and pass that to the DirectorySearcher.

using System;

using System.DirectoryServices;

 

// Get user information from your default active directory repository

public static void PrintUsersFromADSI()

{

    // Find any user that has a name that begins with 'wes'

    string filter = "(&(objectCategory=person)(objectClass=user)(givenName=wes*))";

    DirectorySearcher search = new DirectorySearcher(filter);

    foreach(SearchResult result in search.FindAll())

    {

        // Do work with data returned for each address entry

        DirectoryEntry entry = result.GetDirectoryEntry();

        Console.WriteLine("Name:  {0} {1}\nEmail: {2}\nPhone: {3}",

            entry.Properties["givenName"].Value,

            entry.Properties["sn"].Value,

            entry.Properties["mail"].Value,

            entry.Properties["telephonenumber"].Value);

    }

}

Useful ADSI/LDAP Links:

 

InfiniTec - How to get the Global Address List programmatically
Amit Zinman - Creating a list of Users and their e-mail addresses
Steve Schofield - .NET sample LDAP query looking for a specific user name of (smith) and System.DirectoryServices namespace
Mow - PowerShell and Active Directory Part 1, Part 2, Part 3, Part 4, Part 5

 

Disclaimer: The sample code described here is provided on an “as is” basis, without warranty of any kind, to the fullest extent permitted by law. It is for demonstration purposes only and is by no means production quality.

13 Comments

  • I used WebDav with an api from IndependentSoft and it worked quite well using ASP.NET.

  • Hi I'm getting Timeout error for WebDav. Do you have any idea

  • I am using WebDAV method to read contact details, where i am getting following error
    "The remote server returned an error: (401) Unauthorized." could you please help me to solve this issue.

  • Interesting, but what if I want to give a user of my web app the possibility to access his Outlook contacts for use in my web app. How do I know where the Exchange server with his contacts is located?

  • its ok,but i dont get the complete information on that...
    i want to get the user name from outlook user in asp.net

  • This code saved more than 4 hours of my manual work!!!!

    Great collections!!!

    Thanks-
    Saravana Prakash P.

  • Good article, but it doesn't address the question: How do I access my Outlook contacts from my web application?

    Accessing Exchange Server accounts is not difficult; it's accessing the folder items, such as contacts, that's a little different.

  • hi, great tutorial, although it doesn't solve my problem. Is there a possibility to bypass the authentification ? I don't know at runtime which contact to add / lookup, it depends on the recipient of the mail (with transportagents and so on... ) lets say it's not specific for just one contact ..´. ?

    Thanks in advance

    reinhard

  • hi. Great work ! How can I search for mailadress matches , it doesn't work with the ConstantFieldType !

    Thx

    Thom

  • Hi,

    when I read my Exchange contacts I want the picture of each contact, but in the ContactItemType I can't found the picture.

    How/Where can i found it?

    thanks

  • Hi,

    I tried to access to Web Service Exchange.asmx, and it works when I set explicit credentials, but it does not work when i try to pass through default credential (i get 401 error unauthorized).

    Some ideas ?

    Thanks

  • I've followed the example as closely as I can in Java. I get a 403 whenever I attempt to run the query against the contacts. Any ideas?

  • Thanx for the code man.........I had another block of code just like this one......Bt it couldn't connect with SSL for acquiring ticket.....This one worked......Gr8 man

Comments have been disabled for this content.