Are you a world traveler? ZoneTick is a cool utility that'll help you stay in touch over multiple time zones!
 
Using IExchangeModifyTable to Modify Public Folder Access  
Nik Okuntseff  MS Exchange Server Programming 

Using IExchangeModifyTable to Modify Public Folder Access

Second way of dealing with public folder ACLs is using the IExchangeModifyTable interface. Using this approach you obtain ACL in a form of a MAPI table. You can manipulate this table (for example, by inserting or modifying rows) and then save your changes.
 

Where ACL Data Is Stored?

You may wonder where public folder ACL data is stored. It is stored in PR_ACL_DATA property of the folder object. It is a binary object. Being binary reminds of a Windows NT DACL, but instead of security identifiers public folder ACL deals with MAPI ENTRYIDs, which are usually bigger in size.
 

Reading Public Folder ACL with IExchangeModifyTable

The following sample (Security/IExchangeModifyTableDump) dumps contents of a public folder ACL:

#include <afxwin.h>
#include <edk.h>

int main()
{
    /*
     * Algorithm:
     *  1. Open the folder.
     *  2. Obtain IExchangeModifyTable.
     *  3. Get ACL table with data.
     *  4. Scan the table and dump member names with their rights.
     */
 
    char * pszFolder = "@PR_IPM_SUBTREE_ENTRYID\\All Public Folders\\MyPublicFolder";

    HRESULT hr;
    LPMAPISESSION pSession = NULL;

    // Initialize MAPI
    hr = MAPIInitialize(NULL);
    if (FAILED(hr)) throw -1;

    // Obtain MAPI session
    hr = MAPILogonEx(0,    // Handle to parent window
      "MS Exchange Settings", // Profile name
      NULL,   // Password
      MAPI_NEW_SESSION |
      MAPI_EXTENDED |
      MAPI_LOGON_UI, // Logon flags
      &pSession);  // Resulting MAPI session
    if (FAILED(hr)) throw -1;
 
    // Open Exchange public message store
    LPMDB pPublicMsgStore = NULL;
    hr = HrOpenExchangePublicStore(pSession, &pPublicMsgStore);
    if (FAILED(hr)) throw -1;

    // Open the folder
    LPMAPIFOLDER pFolder = NULL;
    hr = HrMAPIOpenFolderEx(pPublicMsgStore,
       '\\',
       pszFolder,
       &pFolder);
    if (FAILED(hr)) throw -1;

    // Try to open IExchangeModifyTable
    LPEXCHANGEMODIFYTABLE pExchangeTable = NULL;
    hr = pFolder->OpenProperty(PR_ACL_TABLE,
        (LPGUID) &IID_IExchangeModifyTable,
        0,
        MAPI_DEFERRED_ERRORS,
        (LPUNKNOWN *) &pExchangeTable);
    if (FAILED(hr)) throw -1;

    // Get the table
    LPMAPITABLE pAclDataTable = NULL;
    hr = pExchangeTable->GetTable(0, &pAclDataTable);
    if (FAILED(hr)) throw -1;

    // Get row count
    ULONG ulRows = 0;
    hr = pAclDataTable->GetRowCount(0, &ulRows);
    if (FAILED(hr)) throw -1;
    assert(ulRows > 0);

    // Get all rows
    SRowSet * pRowSet = NULL;
    hr = pAclDataTable->QueryRows(ulRows, 0, &pRowSet);
    assert(ulRows == pRowSet->cRows);

    SRow row;
    for (ULONG ul = 0; ul < ulRows; ul++)
    {
        row = pRowSet->aRow[ul];
        // Dump member name
        for (ULONG ulIndex = 0; ulIndex < row.cValues; ulIndex++)
        {
            if (PR_MEMBER_NAME == row.lpProps[ulIndex].ulPropTag)
            {
                char * pszMemberName = row.lpProps[ulIndex].Value.lpszA;
                printf("%s", pszMemberName);
                break;
            }
        }

        // Dump member rights
        for (ulIndex = 0; ulIndex < row.cValues; ulIndex++)
        {
            if (PR_MEMBER_RIGHTS == row.lpProps[ulIndex].ulPropTag)
            {
                LONG lRights = row.lpProps[ulIndex].Value.l;
                printf(" rights: 0x%08X\n", lRights);
                break;
            }
        }
    }
 
    // Cleanup
    if (pRowSet)
        MAPIFreeBuffer(pRowSet);
    if (pAclDataTable)
        pAclDataTable->Release();
    if (pExchangeTable)
        pExchangeTable->Release();
    if (pFolder)
        pFolder->Release();
    if (pPublicMsgStore)
        pPublicMsgStore->Release();
    if (pSession)
        pSession->Release();

    return 0;
}
 
A few comments about the code above. Notice that I obtain IExchangeModifyTable interface via the IMAPIFolder::OpenProperty call requesting PR_ACL_TABLE. This property is of type PT_OBJECT. However, if you examine properties of the folder with a tool such as mdbvu32.exe, it may not be there. For example, for a folder I am experimenting with now PR_ACL_TABLE is not available, but PR_ACL_DATA is.

This is how these properties are defined in EdkMdb.h:

#define pidExchangeXmitReservedMin  0x3FE0
#define PR_ACL_TABLE    PROP_TAG( PT_OBJECT, pidExchangeXmitReservedMin)
#define PR_ACL_DATA     PROP_TAG( PT_BINARY, pidExchangeXmitReservedMin)

Mdbvu32.exe does not know their names and displays PR_ACL_DATA as 0x3FE0.

It turns out that it is still possible to use the OpenProperty specifying PR_ACL_DATA. Exchange creates IExchangeModifyTable interface and gives it back to us for use. Binary table data is actually stored in PR_ACL_DATA. Using IExchangeModifyTable interface is just a more convenient way of working with this data. Having obtained the interface with the OpenProperty call I then use its GetTable method to get a MAPI table filled with data. I scan this table looking for member names and their access rights.

If you run this code you will see that the Default and Anonymous accounts actually have their names listed in the table. Remember that when we were using the IExchangeFolderACLs the names and ENTRYIDs were NULLs. This is a nice advantage of using IExchangeModifyTable interface.
 

Modifying Public Folder ACL with IExchangeModifyTable

Modifying ACL with IExchangeModifyTable is tricky. If you are trying to accomplish this - be prepared to spend some time guessing how it is supposed to work. IExchangeFolderACLs interface is implemented on top of IExchangeModifyTable. If you examine CFolderACLs class implementation in aclclsf.cpp file in your build environment samples\dbmsg\Exchange\libsrc\aclcls project, you will see that for yourself. Apparently, it was designed to simplify working with IExchangeModifyTable. Pay attention to comments, they reveal important details about how IExchangeModifyTable is supposed to be used. For example, you need to drop PR_MEMBER_NAME property when modifying the table. I have spent a few hours trying to modify a single row in the table with no luck (ERROR_INVALID_PARAMETER was returned by the ModifyTable method). Apparently, I was doing something wrong. Anyway, the best algorithm I could suggest here now would be as follows: study libsrc code and do it as they do.
 

[ Contents | Home ]

Send comments and suggestions to niko@wrconsulting.com
Copyright © 1997-1998 by Nik Okuntseff