Hunting those elusive Public Folders using Exchange Web Services (Part 1)

Recently the Exchange server at work has been upgraded to an Exchange 2007 SP1 server. With this has come the chance to finally get rid of the MAPI CDO solutions we have, by switching to using Exchange Web Services (EWS).

I've spent quite a while looking for easy ways of doing this, and for the majority of the time, it seems ok, but the biggest issue I've had (and unfortunately the solution I needed) was accessing public folders. Specifically public folders in a hierarchy.

 Public Folders\
               \Testing
               \Testing\Test 1\
               \Testing\Test 2\

In all the examples I've seen, we can find folders, but, by and large - they all only work for the first level of folders... and the other issue is that the examples are usually specific - i.e. they expect (for example) DistinguishedFolderIdType instead of it's base - BaseFolderIdType.

Anyhews, on with the show!

First, we need to connect to our service, and that is as simple as adding a web reference (and yes - that's in Visual Studio 2008 as well)... This will create our proxy to the EWS and we can begin to code.

The first object we need is the ExchangeServiceBinding class, this allows us to connect to the service.

ExchangeServiceBinding esb = new ExchangeServiceBinding();
esb.Credentials = CredentialCache.DefaultNetworkCredentials;
esb.Url = @"http://<your-server>/EWS/Exchange.asmx";

You can set credentials to be logged in as who you want, but in this case, I'm just taking the logged in users account. Also - as we need to access the public folders, we need to tell the binding that we'll be using Exchange2007_SP1 (public folders aren't accessible in non-SP1 versions).

esb.RequestServerVersionValue = new RequestServerVersion();
esb.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2007_SP1;

Ok, let's start with a method that can *find* a folder...

private static FolderIdType FindFolder( ExchangeServiceBinding esb, BaseFolderIdType folderId, string folderName )
{
    FindPublicFolderType request = new FindPublicFolderType();
    request.Traversal = FolderQueryTraversalType.Shallow;
    request.FolderShape = new FolderResponseShapeType();
    request.FolderShape.BaseShape = DefaultShapeNamesType.AllProperties;
    request.ParentFolderIds = new BaseFolderIdType[] { folderId }; ;
 
    FindPublicFolderResponseType response = esb.FindPublicFolder( request );
 
    foreach( ResponseMessageType rmt in response.ResponseMessages.Items )
    {
        if( rmt.ResponseClass == ResponseClassType.Success )
        {
            FindPublicFolderResponseMessageType ffResponse = (FindPublicFolderResponseMessageType) rmt;
            if( ffResponse.RootFolder.TotalItemsInView > 0 )
            {
                foreach ( BaseFolderType subFolder in ffResponse.RootFolder.Folders )
                    if( subFolder.DisplayName == folderName )
                        return subFolder.FolderId;
 
                return null;
            }
            Console.WriteLine( "Can't find '" + folderName + "'." );
        }
        else
        {
            Console.WriteLine( "Response was: " + rmt.ResponseClass + Environment.NewLine + rmt.MessageText );
        }
    }
    return null;
}


The basic flow is to generate a request, then call the service and deal with the response.
To use this on a public folder we'd do:

DistinguishedFolderIdType rootPublicFolders = new DistinguishedFolderIdType() { Id=DistinguishedFolderIdNameType.publicfoldersroot };
FolderIdType testingFolderId = FindPublicFolder( esb, rootPublicFolders, "Testing" );

This would find 'Testing' without any problems, but if we want to find 'Test1' we're in trouble, for example, if we try:

FolderIdType test1FolderId = FindPublicFolder( esb, parentFolder, "Test1" );

We get nothing, if we try:

FolderIdType test1FolderId = FindPublicFolder( esb, parentFolder, "Testing\\Test1" );

Nothing.

FolderIdType test1FolderId = FindPublicFolder( esb, parentFolder, "Testing/Test1" );

Again nothing.
The reason?
The search traversal... we're only doing a shallow traversal - so we can only find the folders off of the root. Ok - so why not go 'deep'? Well, the answer is that you can't use a deep traversal on public folders. Presumably because it effects everyone, and not just your own account (just a guess there).

Anyhews - we're stuck on that fact, and as a result need to implement some recursive snazziness here...
We know we can find a folder on the same level (i.e. such as testing)... and if we do something like:

FolderIdType test1FolderId = FindPublicFolder( esb, FindPublicFolder( esb, parentFolder, "Testing" ), "Test1" );

We can get the correct folder ID. So let's begin the coding based on the fact we *know* the folder explicitly... The input we want to take is something like:
 "Testing\Test1"

So, we're gonna want to split this string up:

private static void SplitFolder( string folder, out string first, out string rest )
{
    if( string.IsNullOrEmpty( folder ) )
    {
        first = folder;
        rest = null;
        return;
    }
 
    if( !folder.Contains( "\\" ) )
    {
        first = folder;
        rest = null;
        return;
    }
 
    first = folder.Substring( 0, folder.IndexOf( "\\" ) );
    rest = folder.Substring( folder.IndexOf( "\\" ) + 1, folder.Length - folder.IndexOf( "\\" ) - 1 );
}

In a call:

string first, rest;
SplitFolder("Testing\\Test1", out first, out rest);

We'll get:
first == "Testing"
rest == "Test1"


Now let's modify the FindFolder method to be recursive when needed...

private static FolderIdType FindPublicFolder( ExchangeServiceBinding esb, BaseFolderIdType folderId, string folderName )
{
    string first, rest;
    SplitFolder( folderName, out first, out rest );
 
    FindFolderType request = new FindFolderType();
    request.Traversal = FolderQueryTraversalType.Shallow;
    request.FolderShape = new FolderResponseShapeType();
    request.FolderShape.BaseShape = DefaultShapeNamesType.AllProperties;
    request.ParentFolderIds = new BaseFolderIdType[] { folderId }; ;
 
    FindFolderResponseType response = esb.FindFolder( request );
 
    FolderIdType outId = null;
    foreach( ResponseMessageType rmt in response.ResponseMessages.Items )
    {
        if( rmt.ResponseClass == ResponseClassType.Success )
        {
            FindFolderResponseMessageType ffResponse = (FindFolderResponseMessageType) rmt;
            if( ffResponse.RootFolder.TotalItemsInView > 0 )
            {
                foreach( BaseFolderType subFolder in ffResponse.RootFolder.Folders )
                    if( subFolder.DisplayName == first )
                    {
                        outId = subFolder.FolderId;
                        break;
                    }
 
            }
            else
                Console.WriteLine( "Can't find '" + first + "'." );
        }
        else
        {
            Console.WriteLine( "Response was: " + rmt.ResponseClass + Environment.NewLine + rmt.MessageText );
        }
    }
 
    if(string.IsNullOrEmpty( rest) )
        return outId;
 
    return FindPublicFolder( esb, outId, rest ); 
}

So, now we can do:

FolderIdType folderId = FindPublicFolder(esb, parentFolder, "Testing\\Test1");

and we'll get the correct folder Id.
Phew!

What's next?
There's a few avenues we can take - maybe attempt to implement a deep traversal of the public folders?
But I feel of more interest is actually getting the messages in our newly discovered folder!
I'll cover that in the next post, on the 'FindItem' API call...



[Edit: Part 2 and Part 3 are available!]

Print | posted @ Monday, December 1, 2008 8:20 PM

Comments on this entry:

Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by Karl Mitschke at 1/28/2009 6:27 PM

Hello;

I have Exchange 2007 SP1 on all of my exchange servers, and I get the following:

The type or namespace name 'FindPublicFolderType' could not be found. :
static void Main(string[] args)
{
ExchangeServiceBinding esb = new ExchangeServiceBinding();
esb.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
esb.Url = @"https://MyCASServer/EWS/Exchange.asmx";
esb.RequestServerVersionValue = new RequestServerVersion();
esb.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2007_SP1;

}
private static FolderIdType FindFolder(ExchangeServiceBinding esb, BaseFolderIdType folderId, string folderName)
{
FindPublicFolderType request = new FindPublicFolderType();
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by Chris at 1/29/2009 8:05 AM

Hi Karl,

What you're looking for is a 'FindFolderType' as the request. So, you'd have something like:

FindFolderType req = new FindFolderType();
req.Traversal = FolderQueryTraversalType.Shallow;
req.FolderShape = new FolderResponseShapeType();
req.FolderShape.BaseShape = DefaultShapeNamesType.AllProperties;

DistinguishedFolderIdType baseFolder = new DistinguishedFolderIdType();
baseFolder.Id = DistinguishedFolderIdNameType.publicfoldersroot;

req.ParentFolderIds = new BaseFolderIdType[] { baseFolder };

You're actually making a normal request to get any folder, it's just you set the baseFolder to be the 'publicfoldersroot' instead.

Hope that helps, if not feel free to email me and we can see if we can work it out!

Chris
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by Wayne at 2/12/2009 10:48 PM

Excellent article, exactly what I was looking for. Now where is that GetItem :-)
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by Chris at 2/13/2009 8:24 AM

Just go to part 3! (Though the road might be rough, part 2 may just about smooth it out :))
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by JC at 3/5/2009 3:31 PM

Really newbie thing here :-). I put your code in but when I try to make the call: FolderIdType folderId = FindPublicFolder(esb, parentFolder, "Test\\Test1"); I either get: Error 1 The name 'esb' does not exist in the current context D:\Projects\TDM Mail\TDM Mail\Program.cs 48 54 TDM Mail

Error 2 The name 'parentFolder' does not exist in the current context D:\Projects\TDM Mail\TDM Mail\Program.cs 48 59 TDM Mail

By calling it in main.

If I try calling it anywhere else I get: Error 1 'TDM_Mail.Program.folderId': cannot declare instance members in a static class D:\Projects\TDM Mail\TDM Mail\Program.cs 71 22 TDM Mail

What crazy newbie mistake am I making here? :-)

Thanks!


Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by Chris at 3/5/2009 3:41 PM

Hi JC,

Errm, Error 1: - Do you have the following at the top of your code?

ExchangeServiceBinding esb = new ExchangeServiceBinding();

esb.Credentials = CredentialCache.DefaultNetworkCredentials;

esb.Url = @"http://<your-server>/EWS/Exchange.asmx";

That defines the esb.

For the second error, you'll need to define the parentFolder:

DistinguishedFolderIdType parentFolder = new DistinguishedFolderIdType() { Id=DistinguishedFolderIdNameType.publicfoldersroot };

The third error is because you have something like:

private TYPE folderId;

defined in your class, but your class is marked as static:

public static class XXX{...}

so you'd need to change the declaration of folderId to either be

private static TYPE folderId;

or change your class to be non-static. I would imagine that converting the declaration to static would be your easiest route.

If you still have trouble, feel free to get in contact with me by email and I'll see what I can do...

(Email at the top of the blog in the 'News' section)...

Cheers

Chris
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by Rudi at 3/6/2009 8:02 PM

This is great. Let's say this is a mail-enabled public folder. How can I get it's email address??

Please help the newbie!

Thanks,
Rudi
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by Rudi at 3/6/2009 8:02 PM

This is great. Let's say this is a mail-enabled public folder. How can I get its email address??

Please help the newbie!

Thanks,
Rudi
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by Chris at 3/9/2009 9:42 AM

Hi Rudi,

I'm having a look to see if I can get it a better way, but one thing you can do is get the 'To' address from the email item itself.

var emailAddress = ((MessageType) itemType).ToRecipients[0].EmailAddress;

(You'll need to look at Parts 2 & 3 to see about getting the Items)

There are obvious drawbacks to this though, firstly what if there are 2 'To' addresses, i.e. it was sent to one place and another. The other main drawback would be if the folder was CC'd or BCC'd in..

I suspect that the way to get this info is via the 'GetFolder' method, the problem with that is that it doesn't seem to be too happy receiving a FolderIdType that isn't a Distinguised folder type.

I'm looking into it though!!
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by smash at 9/8/2009 8:18 AM

from where can I download the whole code for reference?
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by SMASH at 9/9/2009 2:03 AM

How do I save th ate ITEM to my local computer? I want to save not just attachment but the whole item. I can do the same with using MS outlook installed, but I need to do it without OUTLOOK.

Please help if you can.

By the way, this article helped me a lot.

Thanks in advance.
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by Chris at 10/17/2009 3:21 AM

Hi Smash, sorry for the delay in replies of late...

Right;
- I'm not sure how you would save the whole Item, I'll try to remember to have a look on Monday - but the way I would (will) be trying would be to serialize the item to the hard drive. I guess it depends on what you're wanting to do with it.. Feel free to email me about it and we can probably discuss it better.

- The code isn't in a friendly pass around state at the moment, I will try to tidy it up and put it somewhere soon!
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by Balram Chavan at 11/16/2011 8:43 PM

Hi,
What would be right solution to access public folders from Exchange server 2003, 2007, 2007SP1 and 2010?
I mean the exchange service will not be available for all of these. So what do you think, which approach should be used for these specific version?
Thanks in advance.
Regards,
Balram
Gravatar # re: Hunting those elusive Public Folders using Exchange Web Services (Part 1)
by Chris at 11/26/2014 2:14 PM

Thanks Chris!! I searched forever to find out this public folder issue.
Post A Comment
Title:
Name:
Email:
Comment:
Verification: