ACL.C

// --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);
}

//------------------------------------------------------------------------------