Route public folder content requests

All requests for public folder information that involve the content of the public folder need to be routed to the public folder mailbox that holds the content for the target folder. To route the requests to that mailbox, you need to set the X-AnchorMailbox and X-PublicFolderMailbox headers to specific values.

The following table provides an overview of the process:

Public folder overview

Header What do I need? How do I get it?
X-AnchorMailbox
1. The X-AnchorMailbox and X-PublicFolderInformation values for the public folder hierarchy mailbox.

2. The GUID of the public folder mailbox that contains the mailbox content, which is sent to the Autodiscover service.

The AutoDiscoverSMTPAddress in the Autodisover response becomes the value of the X-AnchorMailbox header.
TODO
1. Use the code example in this article, which implements the EWS Managed API. Or use EWS and convert your results to obtain a GUID.

2. Make an Autodiscover request by using the GUID plus the domain name.

3. Use the value of the AutoDiscoverSMTPAddress element returned in the Autodiscover response to populate the value of the headers.
X-PublicFolderMailbox
Your work is done, the X-PublicFolderMailbox value is the same as the X-AnchorMailbox value!
You already have it!

After you have determined the header values, include them when you make public folder content requests.

The steps in this article are specific to public folder content requests. To determine whether your request is a public folder hierarchy or content request, see Routing public folder requests.

Determine the GUID of the public folder mailbox by using the EWS Managed API

To determine the GUID of the public folder content mailbox, use the following code example, which does the following:

The PR_REPLICA_LIST value identifies the mailbox GUID of the public folder mailbox that has the content for the folder. The PR_REPLICA_LIST property is a byte array, but is cast as a GUID for this scenario. The GUID and the domain name are concatenated to form the address on which to call Autodiscover.

This example assumes that service is the ExchangeService object for the mailbox user, PFHAnchorHeader and PFHMailboxHeader are the values of the X-AnchorMailbox and X-PublicFolderMailbox headers, and domain is the domain name used by the tenant.

public static string GetMailboxGuidAddress(ExchangeService service, String PFHAnchorHeader, String PFHMailboxHeader, String domain)
{
    // Create a new folder view, and pass in the maximum number of folders to return.
    FolderView view = new FolderView(10);
    // Create an extended property definition for the PR_REPLICA_LIST property.
    ExtendedPropertyDefinition PR_REPLICA_LIST = new ExtendedPropertyDefinition(0x6698, MapiPropertyType.Binary);
    // As a best practice, limit the properties returned to only those required.
    // In this case, return the folder ID, the folder display name, and 
    // the value of the PR_REPLICA_LIST extended property definition.
    view.PropertySet = new PropertySet(BasePropertySet.IdOnly, FolderSchema.DisplayName, PR_REPLICA_LIST);
    service.HttpHeaders.Add("X-AnchorMailbox", PFHAnchorHeader);
    service.HttpHeaders.Add("X-PublicFolderMailbox", PFHMailboxHeader);
    // Add a call to the CertificateValidationCallback method here if needed.
    // ServicePointManager.ServerCertificateValidationCallback = CertificateValidationCallBack;
    // Call FindFolders to retrieve the folder hierarchy, starting with the PublicFolderRoot folder.
    // This method call results in a FindFolder call to EWS.
    FindFoldersResults findResults = service.FindFolders(WellKnownFolderName.PublicFoldersRoot, view);
    string GuidAsString = null;
    List<string> Guids = new List<string>();
    // For each folder under the root, display the name, and copy the value of the 
    // PR_REPLICA_LIST byte array to a string value. 
    foreach (Folder folder in findResults.Folders)
    {
        Console.WriteLine("Public folder display name: {0}", folder.DisplayName);
        byte[] ByteArr = (byte[])folder.ExtendedProperties[0].Value;
        GuidAsString = System.Text.Encoding.ASCII.GetString(ByteArr, 0, 36);
        Guids.Add(GuidAsString);
        Console.WriteLine("Address for Autodiscover: {0}.{1}\r\n", GuidAsString, domain);
    }
    // Concatenate the GUID value of the PR_REPLICA_LIST with the domain name to generate the 
    // SMTP address to use for the AutoDiscover request for the public folder content mailbox.
    string AutoDSMTPAddress = GuidAsString + "@" + domain;
    // Check that all folders have the same GUID value. If they do not, use the GUID value of the
    // folder that you're requesting content for.
    string commonGuid = CompareGuidsForEquality(Guids);
    if (commonGuid == "Not Equal")
    {
        Console.WriteLine("The GUIDs for all the folders in the hierarchy are not the same. Run the Autodiscover sample using the address returned above that is associated with the folder in your hierarchy request.", AutoDSMTPAddress);
        return null;
    }
    else
    {
        Console.WriteLine("The GUIDs for all public folders in the hierarchy are the same. Run the Autodiscover sample using the {0} address.", AutoDSMTPAddress);
        return AutoDSMTPAddress;
    }
}
// Method to compare the GUID for each folder under the public folder root.
// If each GUID is the same, return the GUID.
// If the GUIDs are not the same, return "Not equal".
public static string CompareGuidsForEquality(List<string> list)
{
    string NotEqual = "Not equal";
    string first = list.First();
    return list.All(x => x == first) ? first : NotEqual;
}

If you received the error "The request failed. The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel", you'll need to add a call to a validation callback method. A placeholder and comment for that method is included in the code example.

If the mailbox GUID is the same for all the public folders under the public folder root, the example indicates the address to use when calling Autodiscover in the console output and as the return value. If the mailbox GUID is not the same for all public folders under the public folder root, you need to Make an Autodiscover request on the address associated with the folder in your content request.

Determine the GUID of the public folder mailbox by using EWS

The following code example shows how retrieve the value of the PR_REPLICA_LIST (0x66980102) property by using the EWS FindFolder operation. For the ExtendedFieldURI element, the PropertyTag attribute is set to the decimal value (26264) of the PR_REPLICA_LIST property, and the PropertyType attribute is set to Binary.

This is also the XML request that the EWS Managed API sends when you use the FindFolders method to determine the GUID of the public folder mailbox by using the EWS Managed API.

POST https://outlook.office365.com/EWS/Exchange.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
Accept: text/xml
User-Agent: ExchangeServicesClient/15.00.0913.015
Accept-Encoding: gzip,deflate
Authorization: Basic c29ueWFmQGNvbnRvc28xMDAwLm9ubWljcm9zb2Z0LmNvbTpFWENIIzIwMTQ=
Host: outlook.office365.com
Cookie: ClientId=KZPBLKA9ZMPXAQDW
Content-Length: 1005
Expect: 100-continue
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <t:RequestServerVersion Version="Exchange2013_SP1" />
  </soap:Header>
  <soap:Body>
    <m:FindFolder Traversal="Shallow">
      <m:FolderShape>
        <t:BaseShape>IdOnly</t:BaseShape>
        <t:AdditionalProperties>
          <t:FieldURI FieldURI="folder:DisplayName" />
          <t:ExtendedFieldURI PropertyTag="26264" PropertyType="Binary" />
        </t:AdditionalProperties>
      </m:FolderShape>
      <m:IndexedPageFolderView MaxEntriesReturned="10" Offset="0" BasePoint="Beginning" />
      <m:ParentFolderIds>
        <t:DistinguishedFolderId Id="publicfoldersroot" />
      </m:ParentFolderIds>
    </m:FindFolder>
  </soap:Body>
</soap:Envelope>

The server responds to the FindFolder request with a FindFolderResponse message that includes the value of the PR_REPLICA_LIST extended property. Note that the value of the property appears on the EWS response as the string format of a base-64 encoded byte array. Some header values in the response are shortened for readability.

<?xml version="1.0" encoding="utf-8"?><s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:ServerVersionInfo MajorVersion="15" MinorVersion="0" MajorBuildNumber="1019" MinorBuildNumber="15" Version="V2_17" xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
  </s:Header>
  <s:Body>
    <m:FindFolderResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:FindFolderResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
          <m:RootFolder IndexedPagingOffset="2" TotalItemsInView="2" IncludesLastItemInRange="true">
            <t:Folders>
              <t:ContactsFolder>
                <t:FolderId Id="AAEuAAAAAADL8shaNEKnQYVvRbpoY9vDAQBGDloItRzyTrAt+XVzRr/YAABdofPkAAA=" ChangeKey="AwAAABYAAABGDloItRzyTrAt+XVzRr/YAABdo/2h"/>
                <t:DisplayName>My Public Contacts</t:DisplayName>
                <t:ExtendedProperty>
                  <t:ExtendedFieldURI PropertyTag="0x6698" PropertyType="Binary"/>
                  <t:Value>MWVjMmEyMzYtZWQ5My00Zjg4LWI5YzYtMzNlNjNmYTRhYTQ0AA==</t:Value>
                </t:ExtendedProperty>
              </t:ContactsFolder>
              <t:Folder>
                <t:FolderId Id="AQEuAAADy/LIWjRCp0GFb0W6aGPbwwEARg5aCLUc8k6wLfl1c0a/2AAAAxEAAAA=" ChangeKey="AQAAABYAAABGDloItRzyTrAt+XVzRr/YAABdo/W/"/>
                <t:DisplayName>SampleFolder</t:DisplayName>
                <t:ExtendedProperty>
                  <t:ExtendedFieldURI PropertyTag="0x6698" PropertyType="Binary"/>
                  <t:Value>MWVjMmEyMzYtZWQ5My00Zjg4LWI5YzYtMzNlNjNmYTRhYTQ0AA==</t:Value>
                </t:ExtendedProperty>
              </t:Folder>
            </t:Folders>
          </m:RootFolder>
        </m:FindFolderResponseMessage>
      </m:ResponseMessages>
    </m:FindFolderResponse>
  </s:Body>
</s:Envelope>

In order to use the value of the PR_REPLICA_LIST returned in the XML, MWVjMmEyMzYtZWQ5My00Zjg4LWI5YzYtMzNlNjNmYTRhYTQ0AA==, to determine the mailbox GUID, the value must be converted into a GUID in a format similar to how the value is converted in the EWS Managed API code example. The GUID is then concatenated with the domain name to create an SMTP address, which is included in the Autodiscover request.

Make an Autodiscover request

Use the address returned by the GetMailboxGuidAddress method to call Autodiscover. We recommend that you use the Exchange 2013: Get user settings with Autodiscover code sample to call the Autodiscover service because it streamlines the Autodiscover process for you. This code sample uses the command-line arguments listed in the following table to call the POX Autodiscover service to retrieve the AutoDiscoverSMTPAddress value associated with the mailbox GUID.

Argument Description
emailAddress
The address returned by the GetMailboxGuidAddress method in Determine the GUID of the public folder mailbox.
-skipSOAP
Indicates that POX Autodiscover requests are required.
-auth authEmailAddress
The mailbox user's email address, which is used for authentication. You will be prompted to enter the mailbox user's password when you run the sample.

For example, the command-line arguments should look like this:

1ec2a236-ed93-4f88-b9c6-33e63fa4aa44@contoso.com -skipSOAP -auth sonyaf@contoso.com

Where 1ec2a236-ed93-4f88-b9c6-33e63fa4aa44@contoso.com is the address returned by the GetMailboxGuidAddress method, and sonyaf@contoso.com is the mailbox user.

When you run the Exchange 2013: Get user settings with Autodiscover sample, the last Autodiscover response should be successful and include all the user settings associated with the mailbox GUID. Save the AutoDiscoverSMTPAddress user setting locally, as you'll use that in the next step.

Alternatively, if you do not want to use Exchange 2013: Get user settings with Autodiscover sample, you can get the AutoDiscoverSMTPAddress user setting by generating a list of Autodiscover endpoints, and then sending the following POX Autodiscover request to each URL until you receive a successful response.

<?xml version="1.0" encoding="utf-8"?>
<Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
  <Request>
    <EMailAddress>1ec2a236-ed93-4f88-b9c6-33e63fa4aa44@contoso.com</EMailAddress>
    <AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>
  </Request>
</Autodiscover>

For more information about the Autodiscover process, see Autodiscover for Exchange, Generate a list of Autodiscover endpoints, and Get user settings from Exchange by using Autodiscover.

Set the values of the X-AnchorMailbox and X-PublicFolderMailbox headers

Using the value for the AutoDiscoverSMTPAddress acquired in Make an Autodiscover request, set the values of the X-AnchorMailbox and X-PublicFolderMailbox headers in your public folder content request.

For example, given an AutoDiscoverSMTPAddress of NewPublicFolder@contoso.com, include the following headers when making calls to the following methods or operations.

X-AnchorMailbox: NewPublicFolder@contoso.com
X-PublicFolderMailbox: NewPublicFolder@contoso.com

Public folder calls that require the X-AncorMailbox and X-PublicFolder headers

EWS Managed API methods EWS operations
Item.Bind
Item.Update
Item.Copy
Item.Move
Item.Delete
Folder.Bind
Folder.FindItems
CreateItem
GetItem
UpdateItem
CopyItem
MoveItem
DeleteItem
GetFolder
FindItem

To add these headers by using the EWS Managed API, use the HttpHeaders.Add method.

service.HttpHeaders.Add("X-AnchorMailbox", "NewPublicFolder@contoso.com");
service.HttpHeaders.Add("X-PublicFolderMailbox", "NewPublicFolder@contoso.com");

The following code shows a GetFolder request with the X-AnchorMailbox and X-PublicFolderMailbox header set to the values retrieved in the examples in this article.

POST https://outlook.office365.com/EWS/Exchange.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
User-Agent: SoapSender1.0
X-AnchorMailbox: NewPublicFolder@contoso.com
X-PublicFolderMailbox: NewPublicFolder@contoso.com
Authorization: Basic c29ueWFmQGNvbnRvc28xMDAwLm9ubWljcm9zb2Z0LmNvbTpFWENIIzIwMTQ=
Host: outlook.office365.com
Content-Length: 688
Expect: 100-continue
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <t:RequestServerVersion Version="Exchange2013_SP1" />
  </soap:Header>
  <soap:Body>
    <m:GetFolder>
      <m:FolderShape>
        <t:BaseShape>AllProperties</t:BaseShape>
      </m:FolderShape>
      <m:FolderIds>
        <t:DistinguishedFolderId Id="publicfoldersroot" />
      </m:FolderIds>
    </m:GetFolder>
  </soap:Body>
</soap:Envelope>

See also