// --acl.c----------------------------------------------------------------------
// Code to manipulate the access control list for public folders.
//
// Copyright (C) Microsoft Corp. 1986-1996. All Rights Reserved.
// -----------------------------------------------------------------------------
#include <edk.h>
// CbNewROWLIST should be defined in EMSMDB.H but it isn't
#define CbNewROWLIST(_centries) \
(offsetof(ROWLIST,aEntries) + (_centries)*sizeof(ROWENTRY))
typedef struct _FOLDERACL
{
LPEXCHANGEMODIFYTABLE lpWriteTable;
LPMAPITABLE lpReadTable;
} FOLDERACL, *LPFOLDERACL;
#define TEST_ROW_FLAGS(x) \
((((x) == ROW_ADD) | \
((x) == ROW_MODIFY) | \
((x) == ROW_REMOVE) | \
((x) == ROW_EMPTY)))
#include "acl.chk"
//$--HrOpenACLInterface------------------------------------------------------
// Opens the access control list interface on a folder.
// -----------------------------------------------------------------------------
static HRESULT HrOpenACLInterface( // RETURNS: return code
IN LPMAPIFOLDER lpFolder, // pointer to folder
OUT LPFOLDERACL *lppACL) // pointer to access control list
{
HRESULT hr = NOERROR;
HRESULT hrT = NOERROR;
SCODE sc = 0;
LPFOLDERACL lpACL = NULL;
DEBUGPUBLIC("HrOpenACLInterface()");
hr = CHK_HrOpenACLInterface(
lpFolder,
lppACL);
if(FAILED(hr))
RETURN(hr);
// Initialize output parameter
*lppACL = NULL;
// Allocate an ACL
sc = MAPIAllocateBuffer(sizeof(ACL), (LPVOID FAR *)&lpACL);
if(FAILED(sc))
{
hr = HR_FAILED(E_OUTOFMEMORY);
goto cleanup;
}
lpACL->lpWriteTable = NULL;
lpACL->lpReadTable = NULL;
// Open the ACL table property on the folder
hrT = MAPICALL(lpFolder)->OpenProperty( lpFolder,
PR_ACL_TABLE,
(LPGUID)&IID_IExchangeModifyTable,
0,
MAPI_DEFERRED_ERRORS,
(LPUNKNOWN FAR *)&lpACL->lpWriteTable);
if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Open a table on the ACL table property
hrT = MAPICALL( lpACL->lpWriteTable)->GetTable( lpACL->lpWriteTable,
0,
&(lpACL->lpReadTable));
if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
*lppACL = lpACL;
cleanup:
if(FAILED(hr) && (lpACL != NULL))
{
ULRELEASE(lpACL->lpReadTable);
ULRELEASE(lpACL->lpWriteTable);
MAPIFREEBUFFER(lpACL);
}
RETURN(hr);
}
//$--HrCloseACLInterface-----------------------------------------------------
// Closes the access control list interface on a folder.
// -----------------------------------------------------------------------------
static HRESULT HrCloseACLInterface( // RETURNS: return code
IN OUT LPFOLDERACL * lppACL) // pointer to access control list
{
HRESULT hr = NOERROR;
DEBUGPUBLIC("HrCloseACLInterface()");
hr = CHK_HrCloseACLInterface(
lppACL);
if(FAILED(hr))
RETURN(hr);
if((*lppACL) != NULL)
{
ULRELEASE((*lppACL)->lpReadTable);
ULRELEASE((*lppACL)->lpWriteTable);
MAPIFREEBUFFER(*lppACL);
}
RETURN(hr);
}
//$--HrOpenAccessControlList-------------------------------------------------
// Opens the access control list for lpFolder and returns the first
// EDK_MAX_QUERY_ROWS rows.
// -----------------------------------------------------------------------------
static HRESULT HrOpenAccessControlList( // RETURNS: return code
IN LPMAPIFOLDER lpFolder, // pointer to folder
OUT LPFOLDERACL * lppACL, // pointer to access control list
OUT LPSRowSet FAR *lppRows) // pointer to SRowSet
{
HRESULT hr = NOERROR;
HRESULT hrT = NOERROR;
static const SizedSPropTagArray(4, rgPropTag) =
{ 4,
{
PR_MEMBER_ID,
PR_MEMBER_NAME,
PR_MEMBER_RIGHTS,
PR_MEMBER_ENTRYID
}
};
DEBUGPUBLIC("HrOpenAccessControlList()");
hr = CHK_HrOpenAccessControlList(
lpFolder,
lppACL,
lppRows);
if(FAILED(hr))
RETURN(hr);
// Initialize output parameters
*lppACL = NULL;
*lppRows = NULL;
// Open the ACL
hr = HrOpenACLInterface(lpFolder, lppACL);
if (FAILED(hr))
{
goto cleanup;
}
// Set the columns to return
hrT = MAPICALL( (*lppACL)->lpReadTable)->SetColumns( (*lppACL)->lpReadTable,
(SPropTagArray *)&rgPropTag,
0);
if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Seek to the beginning of the table of ACL records
hrT = MAPICALL( (*lppACL)->lpReadTable)->SeekRow( (*lppACL)->lpReadTable,
BOOKMARK_BEGINNING,
0,
NULL);
if (FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Get the first EDK_MAX_QUERY_ROWS rows
hrT = MAPICALL( (*lppACL)->lpReadTable)->QueryRows( (*lppACL)->lpReadTable,
(ULONG)EDK_MAX_QUERY_ROWS,
(ULONG)0,
lppRows);
if( FAILED( hrT) || *lppRows == NULL)
{
hr = HR_LOG( E_FAIL);
goto cleanup;
}
if( (*lppRows)->cRows == 0)
{
FREEPROWS(*lppRows);
hr = HR_LOG( EDK_E_END_OF_FILE);
}
cleanup:
if(((*lppACL) != NULL) && FAILED(hr))
{
(void)HrCloseACLInterface(lppACL);
}
RETURN(hr);
}
//$--HrNextAccessControlList-------------------------------------------------
// Returns the next EDK_MAX_QUERY_ROWS rows for this ACL
// -----------------------------------------------------------------------------
static HRESULT HrNextAccessControlList( // RETURNS: return code
IN LPFOLDERACL lpACL, // pointer to access control list
OUT LPSRowSet FAR * lppRows) // pointer to SRowSet
{
HRESULT hr = NOERROR;
HRESULT hrT = NOERROR;
DEBUGPUBLIC("HrNextAccessControlList()");
hr = CHK_HrNextAccessControlList(
lpACL,
lppRows);
if(FAILED(hr))
RETURN(hr);
// Initialize output parameter
*lppRows = NULL;
// Get the next EDK_MAX_QUERY_ROWS rows
hrT = MAPICALL( lpACL->lpReadTable)->QueryRows( lpACL->lpReadTable,
(ULONG)EDK_MAX_QUERY_ROWS,
(ULONG)0,
lppRows);
if( FAILED( hrT) || *lppRows == NULL)
{
hr = HR_LOG( E_FAIL);
goto cleanup;
}
if( (*lppRows)->cRows == 0)
{
FREEPROWS(*lppRows);
hr = HR_LOG( EDK_E_END_OF_FILE);
}
cleanup:
RETURN(hr);
}
//$--HrCloseAccessControlList------------------------------------------------
// Closes the access control list.
// -----------------------------------------------------------------------------
static HRESULT HrCloseAccessControlList( // RETURNS: return code
IN OUT LPFOLDERACL * lppACL) // pointer to access control list
{
HRESULT hr = NOERROR;
DEBUGPUBLIC("HrCloseAccessControlList()");
hr = CHK_HrCloseAccessControlList(
lppACL);
if(FAILED(hr))
RETURN(hr);
(void)HrCloseACLInterface(lppACL);
RETURN(hr);
}
//$--HrFillROWENTRY----------------------------------------------------------
// Helper function used by HrCreateRowList to assign/allocate members
// of a ROWENTRY.
//------------------------------------------------------------------------------
static HRESULT HrFillROWENTRY(// RETURNS: return code
IN ULONG ulRowFlags, // row flags (ROW_ADD|ROW_MODIFY|ROW_REMOVE)
IN LARGE_INTEGER liMemberID, // member ID (for ROW_MODIFY|ROW_REMOVE)
IN ULONG ulRights, // member rights (for ROW_ADD|ROW_MODIFY)
IN ULONG cbeid, // count of bytes in member entry ID (for ROW_ADD)
IN LPVOID lpeid, // pointer to member entry ID (for ROW_ADD)
OUT LPROWENTRY lpRowEntry) // pointer to row entry
{
HRESULT hr = NOERROR;
SCODE sc = 0;
ULONG cb = 0;
LPSPropValue lpSPropVal = NULL;
LPVOID lpeidCopy = NULL;
UINT i = 0;
ULONG cProps = 0;
DEBUGPRIVATE("HrFillROWENTRY()");
hr = CHK_HrFillROWENTRY(
ulRowFlags,
liMemberID,
ulRights,
cbeid,
lpeid,
lpRowEntry);
if(FAILED(hr))
RETURN(hr);
// Allocate Prop Value array
if (ulRowFlags == ROW_ADD)
{
cProps = 2; // PR_MEMBER_ENTRYID and PR_MEMBER_RIGHTS
}
else if (ulRowFlags == ROW_MODIFY)
{
cProps = 2; // PR_MEMBER_ID and PR_MEMBER_RIGHTS
}
else
{
cProps = 1; // PR_MEMBER_ID;
}
cb = CbNewSPropValue(cProps);
sc = MAPIAllocateBuffer(cb, (LPVOID *)&lpSPropVal);
if(FAILED(sc))
{
hr = HR_FAILED(E_OUTOFMEMORY);
goto cleanup;
}
ZeroMemory(lpSPropVal, cb);
// Allocate/Copy Entry ID, link to prop val array allocation
if (cbeid != 0)
{
sc = MAPIAllocateMore(cbeid, lpSPropVal, &lpeidCopy);
if (sc != SUCCESS_SUCCESS)
{
hr = HR_FAILED(E_OUTOFMEMORY);
goto cleanup;
}
CopyMemory(lpeidCopy, lpeid, cbeid);
}
// Set Row Entry values
lpRowEntry->ulRowFlags = ulRowFlags;
lpRowEntry->cValues = cProps;
lpRowEntry->rgPropVals = lpSPropVal;
// Set Prop Value array
i = 0;
// Set PR_MEMBER_ENTRYID property
if (ulRowFlags == ROW_ADD)
{
lpSPropVal[i].ulPropTag = PR_MEMBER_ENTRYID;
lpSPropVal[i].Value.bin.cb = cbeid;
lpSPropVal[i].Value.bin.lpb = (LPBYTE)lpeidCopy;
i++;
}
// Set MEMBER_ID property
if (ulRowFlags != ROW_ADD)
{
lpSPropVal[i].ulPropTag = PR_MEMBER_ID;
lpSPropVal[i].Value.li = liMemberID;
i++;
}
// Set PR_MEMBER_RIGHTS property
if ((ulRowFlags == ROW_ADD) || (ulRowFlags == ROW_MODIFY))
{
lpSPropVal[i].ulPropTag = PR_MEMBER_RIGHTS;
lpSPropVal[i].Value.ul = ulRights;
i++;
}
cleanup:
if(FAILED(hr))
{
MAPIFREEBUFFER(lpSPropVal);
lpRowEntry->ulRowFlags = 0;
lpRowEntry->cValues = 0;
lpRowEntry->rgPropVals = NULL;
}
RETURN(hr);
}
//$--FreeROWENTRY------------------------------------------------------------
// Helper function used wth HrFillROWENTRY to free dynamically allocated
// members of a ROWENTRY.
//------------------------------------------------------------------------------
static VOID FreeROWENTRY( // RETURNS: nothing
IN OUT LPROWENTRY lpRowEntry) // pointer to row entry
{
DEBUGPRIVATE("FreeROWENTRY()");
if (lpRowEntry)
{
if (lpRowEntry->rgPropVals)
{
// Free the property value array and property values
MAPIFREEBUFFER(lpRowEntry->rgPropVals);
}
lpRowEntry->ulRowFlags = 0;
lpRowEntry->cValues = 0;
}
}
//$--HrCreateROWLIST---------------------------------------------------------
// Create a single item ROWLIST for use with HrModifyAccessControlList().
// -----------------------------------------------------------------------------
static HRESULT HrCreateROWLIST( // RETURNS: return code
IN ULONG ulRowFlags, // row flags (ROW_ADD|ROW_MODIFY|ROW_REMOVE)
IN LARGE_INTEGER liMemberID, // member ID (for ROW_MODIFY|ROW_REMOVE)
IN LPTSTR lpszDisplayName, // pointer to member display name
IN ULONG ulRights, // member rights (for ROW_ADD|ROW_MODIFY)
IN ULONG cbeid, // count of bytes in member entry ID (for ROW_ADD)
IN LPVOID lpeid, // pointer to member entry ID (for ROW_ADD)
OUT LPROWLIST *lppRowList) // pointer to row list
{
HRESULT hr = NOERROR;
SCODE sc = 0;
ULONG cb = 0;
DEBUGPUBLIC("HrCreateROWLIST()");
hr = CHK_HrCreateROWLIST(
ulRowFlags,
liMemberID,
lpszDisplayName,
ulRights,
cbeid,
lpeid,
lppRowList);
if(FAILED(hr))
RETURN(hr);
// Initialize output variable
*lppRowList = NULL;
// Allocate a new ROWLIST
cb = CbNewROWLIST(1);
sc = MAPIAllocateBuffer(cb, (LPVOID *)lppRowList);
if(FAILED(sc))
{
hr = HR_FAILED(E_OUTOFMEMORY);
goto cleanup;
}
// Set RowList values
ZeroMemory((*lppRowList), cb);
(*lppRowList)->cEntries = 1;
hr = HrFillROWENTRY(
ulRowFlags,
liMemberID,
ulRights,
cbeid,
lpeid,
&((*lppRowList)->aEntries[0]));
if(FAILED(hr))
{
goto cleanup;
}
cleanup:
if(FAILED(hr))
{
MAPIFREEBUFFER(*lppRowList);
}
RETURN(hr);
}
//$--HrFreeROWLIST-----------------------------------------------------------
// Free a ROWLIST for use with HrModifyAccessControlList().
// -----------------------------------------------------------------------------
static HRESULT HrFreeROWLIST( // RETURNS: return code
IN OUT LPROWLIST *lppRowList) // pointer to row list
{
HRESULT hr = NOERROR;
ULONG i = 0;
DEBUGPUBLIC("HrFreeROWLIST()");
hr = CHK_HrFreeROWLIST(
lppRowList);
if(FAILED(hr))
RETURN(hr);
for(i = 0; i < (*lppRowList)->cEntries; ++i)
{
FreeROWENTRY(&((*lppRowList)->aEntries[i]));
}
MAPIFREEBUFFER(*lppRowList);
}
//$--HrModifyAccessControlList-----------------------------------------------
// Modify the access control list.
// -----------------------------------------------------------------------------
static HRESULT HrModifyAccessControlList( // RETURNS: return code
IN LPFOLDERACL lpACL, // pointer to access control list
IN LPROWLIST FAR lpRowList) // pointer to row list
{
HRESULT hr = NOERROR;
HRESULT hrT = NOERROR;
DEBUGPUBLIC("HrModifyAccessControlList()");
hr = CHK_HrModifyAccessControlList(
lpACL,
lpRowList);
if(FAILED(hr))
RETURN(hr);
if((lpRowList == NULL) || (lpRowList->cEntries == 0))
{
goto cleanup;
}
hrT = MAPICALL( lpACL->lpWriteTable)->ModifyTable( lpACL->lpWriteTable,
0,
lpRowList);
if(FAILED(hrT))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
cleanup:
RETURN(hr);
}
//$--HrModifyACL----------------------------------------------------------------
// If fRemove is FALSE then, for lpFolder, either add a new user with the
// specified lRights or change the rights if the user exists to the specified
// lRights.
//
// If fRemove is TRUE the remove the ACL record for the indicated user.
//------------------------------------------------------------------------------
HRESULT HrModifyACL(
IN LPMAPIFOLDER lpFolder, // Folder to modify ACL for
IN LPTSTR pszUserName, // Name of user to change
IN ULONG cbUserEid, // Byte count for User's entry id.
IN LPENTRYID lpUserEid, // User's entry id.
IN BOOL fRemove, // Flag indicating whether to remove rights
IN DWORD lRights) // New rights (ignored if fRemove)
{
HRESULT hr = NOERROR;
LPFOLDERACL lpACL = NULL;
LPSRowSet lpRows = NULL;
LPROWLIST lpRowList = NULL;
ULONG iUser, iProp;
LPSPropValue lpsCurrentProp = NULL;
BOOL fDisplayNameSet = FALSE;
BOOL fMemberIDSet = FALSE;
LPTSTR lpszMemberName = NULL;
ULONG ulRowFlags = 0;
LARGE_INTEGER liMemberID = { 0, 0 };
LPTSTR lpszDisplayName = NULL;
DEBUGPUBLIC( "HrModifyACL()");
hr = CHK_HrModifyACL( lpFolder, pszUserName, cbUserEid, lpUserEid, fRemove, lRights);
if( FAILED( hr))
RETURN( hr);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Find row in the ACL corresponding to this user
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Open the ACL and read first list.
hr = HrOpenAccessControlList( lpFolder, &lpACL, &lpRows);
while( SUCCEEDED( hr))
{ // Iterate through user rows
for (iUser = 0; iUser < lpRows->cRows; iUser++)
{
// Clear properties for current user
fDisplayNameSet = FALSE;
fMemberIDSet = FALSE;
// Iterate through properties for this user and set local copies
for( iProp = 0; iProp < lpRows->aRow[iUser].cValues; iProp ++)
{
lpsCurrentProp = lpRows->aRow[iUser].lpProps + iProp;
switch( lpsCurrentProp->ulPropTag)
{
case PR_MEMBER_ID:
liMemberID = lpsCurrentProp->Value.li;
fMemberIDSet = TRUE;
break;
case PR_MEMBER_NAME:
lpszDisplayName = lpsCurrentProp->Value.LPSZ;
fDisplayNameSet = TRUE;
break;
}
}
// Make sure all required properties are available.
if ( (! fMemberIDSet) || (! fDisplayNameSet) )
{
hr = HR_LOG( E_FAIL);
goto cleanup;
}
// Does this row correspond to the user to be changed?
if( lstrcmpi( lpszDisplayName, pszUserName) == 0)
{ // YES
if (fRemove)
ulRowFlags = ROW_REMOVE;
else
ulRowFlags = ROW_MODIFY;
goto FoundUser;
}
}
FREEPROWS(lpRows);
// Get the next batch of rows in the access control list (if any).
hr = HrNextAccessControlList( lpACL, &lpRows);
}
if( hr != EDK_E_END_OF_FILE)
goto cleanup;
// Didn't find row for user so need to add row if not fRemove.
if( fRemove)
{
hr = HR_LOG( EDK_E_NOT_FOUND);
goto cleanup;
}
ulRowFlags = ROW_ADD;
liMemberID.LowPart = 0;
liMemberID.HighPart = 0;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Modify the access control list.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
FoundUser:
// Create a row which will be used to modify the ACL table.
hr = HrCreateROWLIST( ulRowFlags, liMemberID, lpszMemberName, lRights, cbUserEid, lpUserEid, &lpRowList);
if( FAILED( hr))
goto cleanup;
ASSERTERROR( lpRowList != NULL, "lpRowList is NULL");
// Modify the ACL on the folder.
hr = HrModifyAccessControlList( lpACL, lpRowList);
if( FAILED( hr))
goto cleanup;
// Save Changes to the folder.
hr = MAPICALL( lpFolder)->SaveChanges( lpFolder, KEEP_OPEN_READWRITE);
if( FAILED( hr))
goto cleanup;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// All done so clean up.
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cleanup:
if (lpRowList)
HrFreeROWLIST( &lpRowList);
FREEPROWS( lpRows);
HrCloseAccessControlList( &lpACL);
RETURN( hr);
}
//------------------------------------------------------------------------------