ACLCLSF.CPP

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