// --aclclsf.cpp---------------------------------------------------------------
//
// Implementation file for the CFolderACLs control class and its
//class factory.
//
// The CFolderACLs class is a controlling class. It controls the
// CIExchangeFolderACLs class (which implements the IExchangeFolderACLs
// programatic interface defined in aclcls.h). When we add automation
// capabilities, it will also control the standard OLE dispatch interface
// for this class.
//
// Copyright (C) Microsoft Corp. 1986-1996. All rights reserved.
//
// ---------------------------------------------------------------------------
#include "edk.h"
#include "srowlst.h"
#include "aclclsf.h"
#include "aclclsf.chk"
// $--CFolderACLs::CFolderACLs-------------------------------------------------
//
// DESCRIPTION:CFolderACLs controlling class class constructor.
//
// INPUT:None.
//
// RETURNS: Ptr to new CFolderACLs instance.
//
//-----------------------------------------------------------------------------
CFolderACLs::CFolderACLs()
{
DEBUGPRIVATE("CFolderACLs::CFolderACLs().\n");
m_hr =NOERROR; // no error so far
m_refs =1;
m_lpMapiTbl =NULL;
m_lpExchTbl =NULL;
m_lpFolder =NULL;
m_prog_interface = new CIExchangeFolderACLs;
if (m_prog_interface == NULL)
{
m_hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Set programming interface ptr to this object.
m_prog_interface->m_pFA = this;
m_lpSession = NULL;
cleanup:
; // no operation
}
// $--CFolderACLs::~CFolderACLs------------------------------------------------
//
// DESCRIPTION:CFolderACLs controlling class class destructor.
//
// INPUT:None.
//
// RETURNS: Nothing.
//
//-----------------------------------------------------------------------------
CFolderACLs::~CFolderACLs()
{
DEBUGPRIVATE("CFolderACLs::~CFolderACLs().\n");
delete m_prog_interface;
ULRELEASE(m_lpSession);
ULRELEASE(m_lpMapiTbl);
ULRELEASE(m_lpExchTbl);
ULRELEASE(m_lpFolder);
}
// $--CFolderACLs::Create------------------------------------------------------
//
// DESCRIPTION:Create a new instance of the CFolderACLs object.
//Also take care of setting up the standard OLE dispatch
//object ptr for the new instance.
//
// INPUT:None.
//
// RETURNS: Ptr to new CFolderACLs object on success; NULL otherwise.
//
//-----------------------------------------------------------------------------
CFolderACLs FAR *
CFolderACLs::Create()// RETURNS CFolderACLs ptr
{
CFolderACLs FAR *pFolderACLs =NULL;
DEBUGPRIVATE("CFolderACLs::Create().\n");
// Create an instance of CFolderACLs.
pFolderACLs = new FAR CFolderACLs();
if (pFolderACLs == NULL)
{
HR_LOG(E_FAIL);
pFolderACLs = NULL;
goto cleanup;
}
if (FAILED(pFolderACLs->m_hr))
{
HR_LOG(pFolderACLs->m_hr);
ULRELEASE(pFolderACLs);
goto cleanup;
}
cleanup:
return pFolderACLs;
}
// $--CFolderACLs::HrGetTableEntry---------------------------------------------
//
// DESCRIPTION:Get a IExchangeModifyTable ACL table entry using the entryid.
//
// INPUT:
//
// [cbentryid]-- Count of bytes in member entry ID.
// [lpentryid]-- Ptr to member entry ID.
//
// OUTPUT:
//
// [lppszDisplayName]-- Member display name buffer (set to NULL if not
//wanted).
// [lplRights]-- ACL rights bits buffer.
//
// RETURNS: NOERRORon success;
//EDK_E_NOT_FOUNDif not found;
// E_FAILotherwise.
//
// Notes:We use this method to examine the table entries to see if a
//requested action was actually completed. IExchangeModifyTable
//code may reset rights, readd entries, etc., so we have to check.
//-----------------------------------------------------------------------------
HRESULT
CFolderACLs::HrGetTableEntry(// RETURNS: HRESULT
INULONGcbentryid,// # of bytes in entry ID
INLPENTRYIDlpentryid,// entry ID ptr
OUTLPSTR FAR *lppszDisplayName,// display name ptr
OUTLPLONGlplRights// rights ptr
)
{
HRESULThr =NOERROR;
ULONGi =0;
LPMAPITABLElpMapiTbl =NULL;
LPSRowlpRow =NULL;
LPSRowSetlpRows =NULL;
LPADRBOOK lpAdrBook = NULL;
ULONG ulResult = FALSE;
// NOTE: The following order of properties is assumed by lots
// of other code (CFolderACLs, CIExchangeFolderACLs).
SizedSPropTagArray(C_ACLPROPS, rgPropTag) =
{
C_ACLPROPS,
{
PR_MEMBER_ENTRYID,
PR_MEMBER_RIGHTS,
PR_MEMBER_NAME
}
};
DEBUGPRIVATE("CFolderACLs::HrGetTableEntry()\n");
if (lppszDisplayName != NULL)
*lppszDisplayName = NULL;
hr = m_lpSession->OpenAddressBook(
0,
NULL,
AB_NO_DIALOG,
&lpAdrBook);
if (FAILED(hr))
{
goto cleanup;
}
// Open a MAPI table on the ACL table property. This table can be
// read to determine what the ACLs table looks like.
hr = m_lpExchTbl->GetTable(0, &lpMapiTbl);
if (FAILED(hr))
{
goto cleanup;
}
// Select columns needed.
hr = lpMapiTbl->SetColumns((LPSPropTagArray)&rgPropTag, 0);
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Get all the rows in the table up to EDK_MAX_QUERY_ROWS. If there
// are more rows than this, fail with MAPI_E_TABLE_TOO_BIG.
hr = lpMapiTbl->QueryRows(EDK_MAX_QUERY_ROWS, 0, &lpRows);
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Check to be sure that table is not bigger than EDK_MAX_QUERY_ROWS.
// We do this by checking whether or not the cursor is positioned at
// the end of the table.
if (lpRows->cRows == EDK_MAX_QUERY_ROWS)
{
ULONGulRow =0;
ULONGulNumerator =0;
ULONGulDenominator =0;
hr = lpMapiTbl->QueryPosition(&ulRow, &ulNumerator, &ulDenominator);
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// SeekRowApprox() doc states that if ulNumerator == ulDenominator, you
// are at the end of the table.
if (ulNumerator != ulDenominator)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
// Search for the entry and return values.
for (i = 0; i < lpRows->cRows; i++)
{
lpRow = &lpRows->aRow[i];
if (lpRow->lpProps[I_MEMBER_ENTRYID].ulPropTag == PR_MEMBER_ENTRYID&&
lpRow->lpProps[I_MEMBER_ENTRYID].Value.bin.cb == cbentryid)
{
// When we pass in an entryid to IExchangeModifyTable, it upcases
// any embedded distinguisheed names. We therefore have to use
// a case insensitive compare. Can't guarantee it won't
// misidentify something, but the odds seem pretty long, and this
// is the only way we can get around this IExchangeModifyTable
// feature (since they also don't guarantee that table position
// will be maintained).
hr = lpAdrBook->CompareEntryIDs(
lpRow->lpProps[I_MEMBER_ENTRYID].Value.bin.cb,
(LPENTRYID)(lpRow->lpProps[I_MEMBER_ENTRYID].Value.bin.lpb),
cbentryid,
lpentryid,
0,
&ulResult);
if(SUCCEEDED(hr) && (ulResult == TRUE))
{
// Found the match! Make sure you have values available, and
// then return them.
if (lpRow->cValues < C_ACLPROPS||
lpRow->lpProps[I_MEMBER_RIGHTS].ulPropTag !=
PR_MEMBER_RIGHTS||
lpRow->lpProps[I_MEMBER_NAME].ulPropTag !=
PR_MEMBER_NAME)
{
hr = HR_LOG(E_UNEXPECTED); // Data from mdb is invalid!
goto cleanup;
}
*lplRights = lpRow->lpProps[I_MEMBER_RIGHTS].Value.l;
if (lppszDisplayName != NULL)
{
if (lpRow->lpProps[I_MEMBER_NAME].Value.lpszA != NULL)
{
ULONG cb =
strlen(lpRow->lpProps[I_MEMBER_NAME].Value.lpszA) +
sizeof(char);
hr = MAPIAllocateBuffer(strlen(lpRow->
lpProps[I_MEMBER_NAME].Value.lpszA) +
sizeof(char),
(LPVOID FAR *)lppszDisplayName);
if (FAILED(hr))
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}
strcpy(*lppszDisplayName,
lpRow->lpProps[I_MEMBER_NAME].Value.lpszA);
}
}
goto cleanup;// Success!
}
}
}
hr = HR_LOG(EDK_E_NOT_FOUND); // Not found!
cleanup:
if (FAILED(hr))
{
if (lppszDisplayName != NULL)
MAPIFREEBUFFER(*lppszDisplayName);
}
FREEPROWS(lpRows);
ULRELEASE(lpMapiTbl);
ULRELEASE(lpAdrBook);
RETURN(hr);
}
// $--CFolderACLs::HrOpen------------------------------------------------------
//
// DESCRIPTION:Open the object on a ACLs folder.
//
// INPUT:
//
// [lpMDB]-- Ptr to MDB object containing the ACLs folder.
// [cbentryid]-- Number of bytes in folder's entry identifier.
// [lpentryid]-- Folder's entry identifier.
//
// RETURNS: NOERROR on success;
// E_INVALIDARG if bad input;
//E_NOINTERFACE if acl table does not exist on folder;
// E_FAIL otherwise.
//
//-----------------------------------------------------------------------------
HRESULT
CFolderACLs::HrOpen(// RETURNS: HRESULT
IN LPMAPISESSION lpSession, // MAPI session pointer
INLPMDBlpMDB,// MDB store ptr
INULONGcbentryid, // # bytes in entry ID
INLPENTRYIDlpentryid // entry ID ptr
)
{
HRESULThr =NOERROR;
LPSRowSetlpRows =NULL;
ULONGulObjType =0;
// NOTE: The following order of properties is assumed by lots
// of other code (CFolderACLs, CIExchangeFolderACLs). We place the
// member name last so we can drop it when we write the ACL's
// back (in ModifyTable()).
SizedSPropTagArray(C_ACLPROPS, rgPropTag) =
{
C_ACLPROPS,
{
PR_MEMBER_ENTRYID,
PR_MEMBER_RIGHTS,
PR_MEMBER_NAME
}
};
DEBUGPRIVATE("CFolderACLs::HrOpen()\n");
hr = CHK_CFolderACLs_HrOpen(lpMDB, cbentryid, lpentryid);
if (FAILED(hr))
RETURN(hr);
// Don't allow multiple opens on one object.
if (m_lpFolder != NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// AddRef the MAPI session pointer
lpSession->AddRef();
m_lpSession = lpSession;
// Open the folder.
hr = lpMDB->OpenEntry(cbentryid,
lpentryid,
NULL,
MAPI_BEST_ACCESS|
MAPI_DEFERRED_ERRORS,
&ulObjType,
(LPUNKNOWN FAR *)&m_lpFolder);
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
if (ulObjType != MAPI_FOLDER)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Open the ACL table property on the folder. The returned table
// ptr may be used to get a MAPI table for reading and it can
// also be used to modify the ACLs table. If there is no acl table,
// this call will fail with E_NOINTERFACE.
hr = m_lpFolder->OpenProperty(PR_ACL_TABLE,
(LPGUID)&IID_IExchangeModifyTable,
0,
MAPI_DEFERRED_ERRORS,
(LPUNKNOWN FAR *)&m_lpExchTbl);
if (FAILED(hr))
goto cleanup;
// Open a MAPI table on the ACL table property. This table can be
// read to determine what the ACLs table looks like.
hr = m_lpExchTbl->GetTable(0, &m_lpMapiTbl);
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Select columns needed.
hr = m_lpMapiTbl->SetColumns((LPSPropTagArray)&rgPropTag, 0);
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Get all the rows in the table up to EDK_MAX_QUERY_ROWS. If there
// are more rows than this, fail with MAPI_E_TABLE_TOO_BIG.
hr = m_lpMapiTbl->QueryRows(EDK_MAX_QUERY_ROWS, 0, &lpRows);
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// Check to be sure that table is not bigger than EDK_MAX_QUERY_ROWS.
// We do this by checking whether or not the cursor is positioned at
// the end of the table.
if (lpRows->cRows == EDK_MAX_QUERY_ROWS)
{
ULONGulRow =0;
ULONGulNumerator =0;
ULONGulDenominator =0;
hr = m_lpMapiTbl->QueryPosition(&ulRow, &ulNumerator, &ulDenominator);
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
// SeekRowApprox() doc states that if ulNumerator == ulDenominator, you
// are at the end of the table.
if (ulNumerator != ulDenominator)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
// Store the table in a sorted and more manageable format.
hr = m_SRowLst.HrInitialize(lpRows);
if (FAILED(hr))
goto cleanup;
// NOTE - lpRows gets deallocated in SROWLST::Initialize(). We NULL it
// out here as a safeguard against future use.
lpRows = NULL;
cleanup:
if (FAILED(hr))
{
ULRELEASE(m_lpSession);
ULRELEASE(m_lpMapiTbl);
ULRELEASE(m_lpExchTbl);
ULRELEASE(m_lpFolder);
}
RETURN(hr);
}
//---------------------------------------------------------------------
// IUnknown methods
//---------------------------------------------------------------------
// $--CFolderACLs::QueryInterface------------------------------------------
//
// DESCRIPTION:Return ptr to object which implements the desired
//interface, if this object supports the interface.
//
// INPUT:
//
// [riid]-- Reference to interface identifier of desired interface.
//
// [ppv]-- Ptr to object which supports interface. NULL if none.
//
// RETURNS: NOERROR if successful;
//E_INVALIDARG if bad input.
//E_NOINTERFACE if interface isn't supported.
//
// Interfaces supported:
//
// IUnknown
// IDispatch (eventually)
// IExchangeFolderACLs
// DFolderACLs (eventually)
//
//---------------------------------------------------------------------------
STDMETHODIMP
CFolderACLs::QueryInterface(// RETURNS: HRESULT
INREFIIDriid, // interface ID reference
OUTLPVOID FAR *ppv // interface ptr ptr
)
{
HRESULT hr =NOERROR;
DEBUGPRIVATE("CFolderACLs::QueryInterface().\n");
hr = CHK_CFolderACLs_QueryInterface(riid, ppv);
if (FAILED(hr))
RETURN(hr);
*ppv = NULL;
// See if we support the requested interface.
if (IsEqualIID(riid, IID_IUnknown)) // IUnknown interface is ourself
{
*ppv = this; // Return ourself
}
else if (IsEqualIID(riid, IID_IExchangeFolderACLs))
{
// User wants our programatic interface.
*ppv = m_prog_interface;
}
else
{
// We don't support the requested interface.
*ppv = NULL;
hr = HR_LOG(E_NOINTERFACE);
goto cleanup;
}
// If we reach this point, no error occurred. Increment reference count.
AddRef();
cleanup:
RETURN(hr);
}
// $--CFolderACLs::AddRef--------------------------------------------------
//
// DESCRIPTION:Increment the reference count on this object.
//
// INPUT:None.
//
// RETURNS: New reference count.
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CFolderACLs::AddRef()// RETURNS: ULONG
{
DEBUGPRIVATE("CFolderACLs::AddRef().\n");
ASSERTERROR(m_refs, "Bad m_refs.");
m_refs++;
return m_refs;
}
// $--CFolderACLs::Release-------------------------------------------------
//
// DESCRIPTION:Decrement the reference count on this object. If the
//reference count reaches 0, destroy this object.
//
// INPUT:None.
//
// RETURNS: New reference count.
//
//---------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CFolderACLs::Release()// RETURNS: ULONG
{
ULONG ulRefCount =0;
DEBUGPRIVATE("CFolderACLs::Release().\n");
ASSERTERROR(m_refs, "Bad m_refs.");
m_refs--;
if (!m_refs)
{
delete this;
ulRefCount = 0;
}
else
{
ulRefCount = m_refs;
}
return ulRefCount;
}