HOWTO: Create Search Folder using Exchange Impersonation for multiple users via Exchange Web Services

What are we doing here? What is Search Folder? Why do I care?

Search folders are a quick way to bookmark your frequently searched items. It allows you to save a search query with predefined parameters for quick access. For instance I am always looking for emails with attachments and from a particular sender where the subject has some specific words. Yeah a rule could do that but I want to search in multiple folders and keep the individual mails in their individual folders. Complex? not really!

Search folders can help you in creating all those complex queries and mapping them to a single folder to bring all the items under one umbrella, whether its raining or not.

So far everything sounds good now I want to implement this solution enterprise wide, I want to role out a specific query for everyone in my organization is it possible from GUI, server side script or any way to do this. Don’t even think about sending Outlook Macro Project to everyone in your organization. You have a better solution with Exchange 2007 SP1 – Exchange Web Services

I feel blessed with the existence of Exchange Web Services. You can use Exchange Impersonation to act on behalf of every individual in your Organization, go to every individual’s mailbox and create a search folder for them.

Ok now… stop talking and show me the code.. here you go!

 using System;
using System.Collections.Generic;
using System.Text;
using SearchFolderCreator.MyEWS;
using System.Net;
using System.IO;

namespace SearchFolderCreator
{
    class Program
    {

        static ExchangeServiceBinding esb = null;
        const string strSearchText = "IPM.Note";
        const string strEWSURL = "https://server/ews/exchange.asmx";
        const string strUsername = "Username";
        const string strPassword = "********";
        const string strDomain = "Domain.com";
        const string strSearchFolderName = "EWS Created Search Folder";

        static void Main(string[] args)
        {

            esb = new ExchangeServiceBinding();
            esb.Credentials = new NetworkCredential(strUsername, strPassword, strDomain);
            esb.AllowAutoRedirect = true;
            esb.Url = strEWSURL;

            try
            {
                InstallForUser("NewUser@exchangeserver.com");
                InstallForUser("Vikas@exchangeserver.com");
            }
            catch (Exception e)
            {
                Console.WriteLine("Exception Caught: " + e.ToString());
            }

        }

        static BaseFolderType CreateSearchFolder()
        {

            FolderIdType folderID = new FolderIdType();

            //create the request that will create a new searchfolder under the finder directory
            CreateFolderType folderType = new CreateFolderType();
            SearchFolderType[] folderArray = new SearchFolderType[1];

            folderArray[0] = new SearchFolderType();

            folderArray[0].SearchParameters = new SearchParametersType();
            folderArray[0].SearchParameters.Traversal = SearchFolderTraversalType.Deep;        // deep traversal
            folderArray[0].SearchParameters.TraversalSpecified = true;                        // must set it to true otherwise traversal will have no effect
            folderArray[0].SearchParameters.BaseFolderIds = new DistinguishedFolderIdType[1];
            

            DistinguishedFolderIdType dType = new DistinguishedFolderIdType();
            dType.Id = new DistinguishedFolderIdNameType();
            dType.Id = DistinguishedFolderIdNameType.inbox;                                    //  we are looking at Inbox only
            folderArray[0].SearchParameters.BaseFolderIds[0] = dType;

            PathToUnindexedFieldType path = new PathToUnindexedFieldType();
            path.FieldURI = UnindexedFieldURIType.itemItemClass;                            // Evaluating a string with Item Class

            folderArray[0].SearchParameters.Restriction = new RestrictionType();
            ContainsExpressionType expressionType = new ContainsExpressionType();

            expressionType.Item = path;

            expressionType.ContainmentModeSpecified = true;
            expressionType.ContainmentMode = ContainmentModeType.FullString;                // Matching full string

            expressionType.ContainmentComparison = ContainmentComparisonType.Exact;            // With Exact match
            expressionType.ContainmentComparisonSpecified = true;

            expressionType.Constant = new ConstantValueType();

            expressionType.Constant.Value = strSearchText;                                    // Subject to look for
            folderArray[0].SearchParameters.Restriction.Item = expressionType;

            folderArray[0].DisplayName = strSearchFolderName;                        // Give your search folder a unique name

            folderType.Folders = folderArray;

            TargetFolderIdType targetFolder = new TargetFolderIdType();

            //Create the searchfolder under the Finder Folder

            DistinguishedFolderIdType searchFolder = new DistinguishedFolderIdType();

            searchFolder.Id = DistinguishedFolderIdNameType.searchfolders;                    // Saving it under searchfolders root

            targetFolder.Item = searchFolder;

            folderType.ParentFolderId = targetFolder;

            // Uncomment the following lines of code to make it a hidden search folder, can be consumed by other programs and not visible to users
            // See the explanintion below
            
            // folderArray[0].ExtendedProperty = new ExtendedPropertyType[1];
            // folderArray[0].ExtendedProperty[0] = new ExtendedPropertyType();
            // folderArray[0].ExtendedProperty[0].ExtendedFieldURI = new PathToExtendedFieldType();
            // folderArray[0].ExtendedProperty[0].ExtendedFieldURI.PropertyTag="0x10F4";   //PR_ATTR_HIDDEN
            // folderArray[0].ExtendedProperty[0].ExtendedFieldURI.PropertyType = MapiPropertyTypeType.Boolean;
            // folderArray[0].ExtendedProperty[0].Item = "true";


            

            //Create the search folder
            CreateFolderResponseType createFolderResponse = esb.CreateFolder(folderType);
            //Return the newly created search folder

            FolderInfoResponseMessageType folderInfo = (FolderInfoResponseMessageType)createFolderResponse.ResponseMessages.Items[0];

            return folderInfo.Folders.Length == 0 ? null : folderInfo.Folders[0];

        }
        
        static bool DeleteSearchFolder()
        {

            string strSearchFolderID = null;
            
            //get the search folder's ID
            strSearchFolderID = SearchFolderExists();
            
            if (strSearchFolderID==string.Empty || strSearchFolderID ==null)
                return false;

            FolderIdType folderID = new FolderIdType();

            folderID.Id = strSearchFolderID;

            //Now create the request that will delete the searchfolder using FolderID we have already
            DeleteFolderType folderType = new DeleteFolderType();


            folderType.DeleteType = DisposalType.HardDelete;
            folderType.FolderIds = new BaseFolderIdType[1];

            folderType.FolderIds[0] = folderID;
            
            //Delete the search folder
            DeleteFolderResponseType deleteFolderResponse = esb.DeleteFolder(folderType);
            return deleteFolderResponse.ResponseMessages.Items[0].ResponseClass == ResponseClassType.Success ? true : false;


        }
        
        // Search for folder if it already exists
        static string SearchFolderExists()
        {
            if (null == esb)
                return false;
            //get the root folder ID
            DistinguishedFolderIdType[] fit = new DistinguishedFolderIdType[1];
            fit[0] = new DistinguishedFolderIdType();
            fit[0].Id = DistinguishedFolderIdNameType.searchfolders;
            //set the props that we want to retrieve
            FolderResponseShapeType frst = new FolderResponseShapeType();
            frst.BaseShape = DefaultShapeNamesType.IdOnly;
            //restrict the search on the folder name
            PathToUnindexedFieldType ftFolderName = new PathToUnindexedFieldType();
            ftFolderName.FieldURI = UnindexedFieldURIType.folderDisplayName;
            ConstantValueType cvt = new ConstantValueType();
            cvt.Value = strSearchFolderName;
            FieldURIOrConstantType ctFolderName = new FieldURIOrConstantType();
            ctFolderName.Item = cvt;
            ContainsExpressionType cet = new ContainsExpressionType();
            cet.Constant = cvt;
            cet.Item = ftFolderName;

            cet.ContainmentComparison=ContainmentComparisonType.Exact;
            cet.ContainmentComparisonSpecified = true;
            cet.ContainmentMode = ContainmentModeType.FullString;
            cet.ContainmentModeSpecified = true;
            RestrictionType rt = new RestrictionType();
            rt.Item = cet;
            //find the folder
            FindFolderType fft = new FindFolderType();
            fft.Traversal =FolderQueryTraversalType.Shallow;
            fft.ParentFolderIds = fit;
            fft.FolderShape = frst;
            fft.Restriction = rt;
            FindFolderResponseType ffrt = esb.FindFolder(fft);
            ResponseMessageType rmt =
            ((ResponseMessageType)ffrt.ResponseMessages.Items[0]);


            if (rmt.ResponseClass == ResponseClassType.Success)
            {
                FindFolderResponseMessageType folderInfo = (FindFolderResponseMessageType)ffrt.ResponseMessages.Items[0];

                if (folderInfo.RootFolder.Folders.Length == 0)
                    return string.Empty;
                else
                {
                    return folderInfo.RootFolder.Folders[0].FolderId.Id;
                }

            }
            else
                return  string.Empty;
        }
        
        
        static void InstallForUser(string primarySMTPaddress)
        {
            

            string SubscriptionID = null;
            esb.ExchangeImpersonation = new ExchangeImpersonationType();
            esb.ExchangeImpersonation.ConnectingSID = new ConnectingSIDType();
            esb.ExchangeImpersonation.ConnectingSID.PrimarySmtpAddress = primarySMTPaddress;

            Console.WriteLine("Processing \"" + primarySMTPaddress + "\"...");
            Console.WriteLine("Checking if search folder already exists...");
            if (!SearchFolderExists())
            {
                Console.WriteLine("Search folder does not exist, creating one...");
                strSearchFolderID = CreateHiddenSearchFolder().FolderId.Id;
                Console.WriteLine("Search folder created.");
            }
            else
            {
                Console.WriteLine("Search folder already exist... deleting existing one...");
                DeleteHiddenSearchFolder();
                Console.WriteLine("Search folder deleted, creating new one...");
                strSearchFolderID = CreateHiddenSearchFolder().FolderId.Id;
                Console.WriteLine("Search folder created.");
            }

            Console.WriteLine("Search Folder ID: " + strSearchFolderID);
            
        }
    }
}

I talked about inside code, about Hidden Search Folder. You can make your search folders hidden but what could be the use of search folders if they are hidden? Users cannot see them so who will use them? Well that’s for later :)