SROWLST.CPP
// --srowlst.cpp--------------------------------------------------------------- 
// 
// Implementation of classes CSROWNODE and CSROWLST. 
//   
// Copyright (C) Microsoft Corp., 1986-1996.  All rights reserved. 
// 
//----------------------------------------------------------------------------- 
 
#include "edk.h" 
#include "srowlst.h" 
 
// $--CSROWNODE::CSROWNODE----------------------------------------------------- 
// 
// DESCRIPTION:CSROWNODE constructor. 
// 
// INPUT: 
// 
//[lpSRow]-- Ptr to SRow for the CSROWNODE to be constructed. 
// 
// RETURNS:Nothing. 
// 
//----------------------------------------------------------------------------- 
 
CSROWNODE::CSROWNODE( 
INLPSRowlpSRow  // row set ptr 
    ) 
{ 
    DEBUGPRIVATE("CSROWNODE::CSROWNODE()\n"); 
 
    m_pNxt =NULL; 
    m_pPrv =NULL; 
m_SRow =*lpSRow; 
} 
 
 
// $--CSROWNODE::~CSROWNODE---------------------------------------------------- 
// 
// DESCRIPTION:CSROWNODE destructor. 
// 
// INPUT:None. 
// 
// RETURNS:Nothing. 
// 
//----------------------------------------------------------------------------- 
 
CSROWNODE::~CSROWNODE() 
{ 
    DEBUGPRIVATE("CSROWNODE::~CSROWNODE()\n"); 
 
MAPIFREEBUFFER(m_SRow.lpProps); 
} 
 
// $--CSROWLST::CSROWLST------------------------------------------------------- 
// 
// DESCRIPTION:CSROWLST constructor. 
// 
// INPUT:None. 
// 
// RETURNS:Nothing. 
// 
//----------------------------------------------------------------------------- 
 
CSROWLST::CSROWLST() 
{ 
    DEBUGPRIVATE("CSROWLST::CSROWLST()\n"); 
 
m_cNodes =0; 
m_cOtherProvNodes =0; 
m_lPos =RULE_PAST_END; 
m_lpszProvider =NULL; 
    m_pCurNode =NULL; 
    m_pLstHd =NULL; 
m_pOtherProvLstHd =NULL; 
} 
 
 
// $--CSROWLST::~CSROWLST------------------------------------------------------ 
// 
// DESCRIPTION:CSROWLST destructor. 
// 
// INPUT:None. 
// 
// RETURNS:Nothing. 
// 
//----------------------------------------------------------------------------- 
 
CSROWLST::~CSROWLST() 
{ 
    CSROWNODE *pNode =NULL; 
 
    DEBUGPRIVATE("CSROWLST::~CSROWLST()\n"); 
 
MAPIFREEBUFFER(m_lpszProvider); 
 
    while (m_pLstHd != NULL) 
    { 
        pNode = m_pLstHd; 
        m_pLstHd = m_pLstHd->m_pNxt; 
delete pNode; 
    } 
 
    while (m_pOtherProvLstHd != NULL) 
    { 
        pNode = m_pOtherProvLstHd; 
        m_pOtherProvLstHd = m_pOtherProvLstHd->m_pNxt; 
delete pNode; 
    } 
} 
 
 
// $--CSROWLST::HrInitialize--------------------------------------------------- 
// 
// DESCRIPTION:Initialize an CSROWLST.  An SRowSet is stored in the CSROWLST. 
//The SROWNODES are sorted in ascending order based on the 
//value of PR_RULE_SEQUENCE in the SRow's.  Upon completion of 
//this method, either successful or unsuccessful, all storage 
//associated with lpRows has either been reassigned to the 
//CSROWLST, or has been deallocated.  Only rules associated 
//with lpszProvider are visible to users.  Other rules are 
//stored in the m_pOtherProvLstHd list so they may be written back 
//out to the rules table when necessary. 
// 
// INPUT: 
// 
//  [lpszProvider]-- Provider for rules.  Multiple providers may have 
//   rules on a folder. The IExchangeFolderRules interface 
//   provides access to the rules associated with a 
//   single specified provider.  May be NULL, in which case 
//   a provider list can be obtained, but no other operations 
//   are possible. 
// 
//INPUT/OUTPUT: 
// 
//[lpRows]-- Ptr to SRowSet to use in initializing the CSROWLST. 
// 
// RETURNS:NOERROR on success; 
//              E_INVALIDARGif bad input; 
//E_OUTOFMEMORYif insufficient memory; 
//              E_FAILotherwise. 
// 
// Warning:lpRows is NO LONGER VALID after calling this function and 
//should NOT be used or deallocated! 
// 
//----------------------------------------------------------------------------- 
 
HRESULT 
CSROWLST::HrInitialize(// RETURNS: HRESULT 
INLPSTRlpszProvider,// provider name 
IN OUTLPSRowSetlpRows      // row set ptr 
) 
{ 
HRESULThr =NOERROR; 
ULONGi =0; 
    CSROWNODE *pCurNode =NULL; 
    CSROWNODE *pNewNode =NULL; 
ULONGulCurSeqNo =0; 
ULONGulNewSeqNo =0; 
 
    DEBUGPRIVATE("CSROWLST::HrInitialize()\n"); 
 
// Verify that all rows have the necessary properties.  We are somewhat 
// draconian in insisting that everything is right; given a multiplicity 
// of providers, however, it is a good safeguard against the unexpected. 
 
for (i = 0; i < lpRows->cRows; i++) 
{ 
if (lpRows->aRow[i].cValues < C_RULEPROPS|| 
lpRows->aRow[i].lpProps[I_RULE_SEQUENCE].ulPropTag != 
PR_RULE_SEQUENCE|| 
lpRows->aRow[i].lpProps[I_RULE_STATE].ulPropTag != 
PR_RULE_STATE|| 
lpRows->aRow[i].lpProps[I_RULE_CONDITION].ulPropTag != 
PR_RULE_CONDITION|| 
lpRows->aRow[i].lpProps[I_RULE_ACTIONS].ulPropTag != 
PR_RULE_ACTIONS|| 
lpRows->aRow[i].lpProps[I_RULE_PROVIDER].ulPropTag != 
PR_RULE_PROVIDER|| 
lpRows->aRow[i].lpProps[I_RULE_PROVIDER].Value.lpszA == NULL|| 
lpRows->aRow[i].lpProps[I_RULE_LEVEL].ulPropTag != 
PR_RULE_LEVEL|| 
lpRows->aRow[i].lpProps[I_RULE_NAME].ulPropTag != 
PR_RULE_NAME) 
{ 
hr = HR_LOG(E_INVALIDARG); 
goto cleanup; 
} 
} 
 
if (lpszProvider == NULL) 
{ 
for (i = 0; i < lpRows->cRows; i++) 
{ 
pNewNode = new CSROWNODE(&lpRows->aRow[i]); 
 
if (pNewNode == NULL) 
{ 
hr = HR_LOG(E_OUTOFMEMORY); 
goto cleanup; 
} 
 
// The lpProps storage no longer belongs to the SRowSet, so: 
 
lpRows->aRow[i].cValues = 0; 
lpRows->aRow[i].lpProps = NULL; 
 
AddToDLLTail(pNewNode, m_pOtherProvLstHd); 
 
m_cOtherProvNodes++; 
} 
} 
else 
{ 
// Add the SRow's in the SRowSet to the row list.  If there are 
// two identical sequence numbers associated with one provider, abort 
// with an error.  Note that SRow's associated with the specified 
// provider are added to the m_pLstHd list, and all other SRow's are 
// added to the m_pOtherProvLstHd list. 
 
hr = MAPIAllocateBuffer(strlen(lpszProvider) + 1, 
(LPVOID FAR *)&m_lpszProvider); 
 
if (FAILED(hr)) 
{ 
hr = HR_LOG(E_OUTOFMEMORY); 
goto cleanup; 
} 
 
strcpy(m_lpszProvider, lpszProvider); 
 
for (i = 0; i < lpRows->cRows; i++) 
{ 
pNewNode = new CSROWNODE(&lpRows->aRow[i]); 
 
if (pNewNode == NULL) 
{ 
hr = HR_LOG(E_OUTOFMEMORY); 
goto cleanup; 
} 
 
// The lpProps storage no longer belongs to the SRowSet, so: 
 
lpRows->aRow[i].cValues = 0; 
lpRows->aRow[i].lpProps = NULL; 
 
if (stricmp(lpszProvider, 
pNewNode->m_SRow.lpProps[I_RULE_PROVIDER].Value.lpszA)) 
{ 
AddToDLLTail(pNewNode, m_pOtherProvLstHd); 
 
m_cOtherProvNodes++; 
} 
else 
{ 
if (m_pLstHd == NULL) 
{ 
AddToDLLHead(pNewNode, m_pLstHd); 
m_cNodes++; 
} 
else 
{ 
ulNewSeqNo = 
pNewNode->m_SRow.lpProps[I_RULE_SEQUENCE].Value.ul; 
 
// We walk through the list backwards to insert the new 
// node, in order to get an O(n) sort for a previously 
// sorted table (which is what we always write, but we have 
// to do this because there are no guarantees regarding 
// what others write).  See llmacro.h to understand the 
// double linked list structure and macro calls. 
 
pCurNode = m_pLstHd->m_pPrv; 
 
ulCurSeqNo = 
pCurNode->m_SRow.lpProps[I_RULE_SEQUENCE].Value.ul; 
 
while (TRUE) 
{ 
if (ulCurSeqNo < ulNewSeqNo) 
{ 
InsertIntoDLL(pNewNode, m_pLstHd, pCurNode); 
m_cNodes++; 
break; 
} 
else if (ulCurSeqNo == ulNewSeqNo) 
{ 
delete pNewNode; 
 
hr = HR_LOG(E_FAIL); 
goto cleanup; 
} 
else 
{ 
if (pCurNode == m_pLstHd) 
{ 
AddToDLLHead(pNewNode, m_pLstHd); 
m_cNodes++; 
break; 
} 
else 
{ 
pCurNode = pCurNode->m_pPrv; 
 
ulCurSeqNo = 
pCurNode-> 
m_SRow.lpProps[I_RULE_SEQUENCE].Value.ul; 
} 
} 
} 
} 
} 
} 
} 
 
if (m_cNodes > 0) 
{ 
m_lPos = 0; 
m_pCurNode = m_pLstHd; 
} 
 
cleanup: 
 
FREEPROWS(lpRows); 
 
RETURN(hr); 
} 
 
 
// $--CSROWLST::HrDelete------------------------------------------------------- 
// 
// DESCRIPTION:Delete the current record. 
// 
// INPUT:None. 
// 
// RETURNS:NOERROR on success; 
//E_FAIL if current cursor value is RULE_PAST_END. 
// 
//----------------------------------------------------------------------------- 
 
HRESULT 
CSROWLST::HrDelete(VOID)// RETURNS: HRESULT 
{ 
    HRESULThr =NOERROR; 
CSROWNODE *pNode =NULL; 
 
    DEBUGPRIVATE("CSROWLST::HrDelete()\n"); 
 
hr = HrRemoveFromLst(&pNode); 
 
if (FAILED(hr)) 
goto cleanup; 
 
delete pNode; 
 
cleanup: 
 
    RETURN(hr); 
} 
 
 
// $--HrGetProviders----------------------------------------------------------- 
// 
// DESCRIPTION:Get an array of rules provider names. 
// 
// OUTPUT: 
// 
//  [lpcProviders]-- Pointer to ulong that will be set to count of 
//   providers on successful return. 
//[lpppszProviders]-- Pointer to array of string pointers that will be set 
//   to point at an array of provider name string pointers 
//   on successful return. 
// 
// RETURNS:     NOERRORif successful; 
//E_OUTOFMEMORYif not enough memory; 
//              E_FAIL otherwise. 
// 
//----------------------------------------------------------------------------- 
 
HRESULT 
CSROWLST::HrGetProviders(// RETURNS: HRESULT 
OUTLPULONGlpcProviders,// count of providers 
OUTLPSTR FAR * FAR *lpppszProviders// ptr to array of providers 
) 
{ 
ULONGcProviders =0; 
ULONGcProvidersMax =(m_cNodes > 0 ? 1 : 0) + m_cOtherProvNodes; 
    HRESULThr =NOERROR; 
ULONGi =0; 
LPSTRlpszProvider=NULL; 
LPSTR *lppszProviders =NULL; 
CSROWNODE *pNode =m_pOtherProvLstHd; 
 
    DEBUGPRIVATE("HrGetProviders()\n"); 
 
*lpppszProviders = NULL; 
 
hr = MAPIAllocateBuffer(cProvidersMax * sizeof(LPSTR), 
(LPVOID FAR *)&lppszProviders); 
if (FAILED(hr)) 
goto cleanup; 
 
// Get an array of ptrs to all the providers. 
 
while (pNode != NULL) 
{ 
lpszProvider = pNode->m_SRow.lpProps[I_RULE_PROVIDER].Value.lpszA; 
 
for (i = 0; i < cProviders; i++) 
if (!stricmp(lpszProvider, lppszProviders[i])) 
break; 
 
if (i == cProviders) 
{ 
lppszProviders[cProviders] = lpszProvider; 
cProviders++; 
} 
 
pNode = pNode->m_pNxt; 
} 
 
if (m_lpszProvider != NULL) 
{ 
lppszProviders[cProviders] = m_lpszProvider; 
cProviders++; 
} 
 
// Now allocate a copy for the user. 
 
{ 
ULONGcch =cProviders * sizeof(LPSTR); 
LPSTR *lppsz =NULL; 
CHAR *pch = NULL; 
 
for (i = 0; i < cProviders; i++) 
cch += (strlen(lppszProviders[i]) + 1); 
 
hr = MAPIAllocateBuffer(cch, (LPVOID FAR *)lpppszProviders); 
 
if (FAILED(hr)) 
goto cleanup; 
 
lppsz = *lpppszProviders; 
 
pch = ((CHAR *)lppsz) + (cProviders * sizeof(LPSTR)); 
 
for (i = 0; i < cProviders; i++) 
{ 
strcpy(pch, lppszProviders[i]); 
 
lppsz[i] = pch; 
 
pch += (strlen(lppszProviders[i]) + 1); 
} 
} 
 
*lpcProviders = cProviders; 
 
cleanup: 
 
MAPIFREEBUFFER(lppszProviders); 
 
    RETURN(hr); 
} 
 
 
// $--CSROWLST::HrInsert------------------------------------------------------- 
// 
// DESCRIPTION:Insert a new CSROWNODE before the current record and advance 
//the cursor.  This also adjusts PR_RULE_SEQUENCE values as 
//appropriate, and provides a value for PR_RULE_PROVIDER (ie., 
//the caller should leave this value NULL). 
// 
// INPUT: 
// 
//[lpSRow]-- Ptr to SRow for the CSROWNODE to be inserted. 
// 
// RETURNS:NOERRORon success; 
//E_OUTOFMEMORYif insufficient memory; 
//              E_FAILotherwise. 
// 
// Notes:The cursor is advanced only if it is not at RULE_PAST_END. 
// 
//----------------------------------------------------------------------------- 
 
HRESULT 
CSROWLST::HrInsert(// RETURNS: HRESULT 
INLPSRowlpSRow      // row set ptr 
) 
{ 
    HRESULThr =NOERROR; 
CSROWNODE *pNewNode =NULL; 
 
    DEBUGPRIVATE("CSROWLST::HrInsert()\n"); 
 
if (m_cNodes == EDK_MAX_QUERY_ROWS) 
    { 
hr = HR_LOG(E_FAIL); 
        goto cleanup; 
    } 
 
if (m_lpszProvider == NULL) 
    { 
hr = HR_LOG(E_FAIL); 
        goto cleanup; 
    } 
 
pNewNode = new CSROWNODE(lpSRow); 
 
if (pNewNode == NULL) 
    { 
    hr = HR_LOG(E_OUTOFMEMORY); 
        goto cleanup; 
    } 
 
pNewNode->m_SRow.lpProps[I_RULE_PROVIDER].Value.lpszA = m_lpszProvider; 
 
if (m_pCurNode == m_pLstHd)// Covers empty list and insertion at head. 
{ 
AddToDLLHead(pNewNode, m_pLstHd); 
} 
else if (m_pCurNode == NULL)// Covers insertion at tail. 
{ 
AddToDLLTail(pNewNode, m_pLstHd); 
} 
else// Covers all other cases. 
{ 
// It is important to store the previous node ptr in a separate 
// location because m_pCurNode->m_pPrv gets stomped on in 
// InsertIntoDLL(). 
 
CSROWNODE *pPrvNode = m_pCurNode->m_pPrv; 
 
InsertIntoDLL(pNewNode, m_pLstHd, pPrvNode); 
} 
 
m_cNodes++; 
 
if (m_lPos != RULE_PAST_END) 
m_lPos++; 
 
ReSequence(); 
 
// Note that m_pCurNode does not change regardless of the insertion point. 
 
cleanup: 
 
    RETURN(hr); 
} 
 
 
// $--CSROWLST::HrRemoveFromLst------------------------------------------------ 
// 
// DESCRIPTION:Remove the current record from the list and make it available 
//to the caller.  The caller is responsible for deleting the 
//record when he no longer needs it. 
// 
// OUTPUT: 
// 
//[ppRemovedNode]-- Ptr that will be set to removed node on successful 
//   return. 
// 
// RETURNS:NOERROR on success; 
//E_FAIL if current cursor value is RULE_PAST_END. 
// 
//----------------------------------------------------------------------------- 
 
HRESULT 
CSROWLST::HrRemoveFromLst( 
OUTCSROWNODE * *ppRemovedNode 
) 
{ 
    HRESULThr =NOERROR; 
CSROWNODE *pNode =NULL; 
 
    DEBUGPRIVATE("CSROWLST::HrRemoveFromLst()\n"); 
 
*ppRemovedNode = NULL; 
 
if (m_lPos == RULE_PAST_END) 
    { 
hr = HR_LOG(E_FAIL); 
        goto cleanup; 
    } 
 
pNode = m_pCurNode; 
 
m_pCurNode = m_pCurNode->m_pNxt; 
 
if (m_pCurNode == NULL) 
m_lPos = RULE_PAST_END; 
 
RmFromDLL(pNode, m_pLstHd); 
 
m_cNodes--; 
 
*ppRemovedNode = pNode; 
 
cleanup: 
 
    RETURN(hr); 
} 
 
 
// $--CSROWLST::SetCursor------------------------------------------------------ 
// 
// DESCRIPTION:Standard C++ set member function which sets the current 
//cursor position. 
// 
// INPUT: 
// 
//[lPos]-- New cursor position.  If < 0 or past end of list, then the 
//   new cursor positon will be set to RULE_PAST_END. 
// 
// RETURNS:The new cursor position. 
// 
//----------------------------------------------------------------------------- 
 
LONG 
CSROWLST::SetCursor(// RETURNS: LONG 
INLONGlPos// cursor position 
) 
{ 
    DEBUGPRIVATE("CSROWLST::SetCursor()\n"); 
 
if (lPos >= 0 && lPos < (LONG) m_cNodes) 
{ 
if (m_lPos == RULE_PAST_END || m_lPos > lPos) 
{ 
m_pCurNode = m_pLstHd; 
m_lPos = 0; 
} 
 
while (m_lPos < lPos) 
{ 
m_pCurNode = m_pCurNode->m_pNxt; 
m_lPos++; 
} 
} 
else 
{ 
m_lPos = RULE_PAST_END; 
m_pCurNode =NULL; 
} 
 
    return m_lPos; 
} 
 
 
// $--CSROWLST::ReSequence----------------------------------------------------- 
// 
// DESCRIPTION:Resequence the PR_RULE_SEQUENCE values so they are valid. 
// 
// INPUT:None. 
// 
// RETURNS:Nothing. 
// 
//----------------------------------------------------------------------------- 
 
VOID 
CSROWLST::ReSequence(VOID)      // RETURNS: VOID 
{ 
CSROWNODE *pNode = NULL; 
ULONGulLastSeqNo =0; 
 
    DEBUGPRIVATE("CSROWLST::ReSequence()\n"); 
 
// The following algorithm is designed to not change sequence numbers 
// unless it is necessary.  This is done to protect providers that may 
// assign special significance to some sequence values (not a great idea, 
// but it is rumored to have occurred).  There is no protection against 
// rollover of values, but that is hopefully not a very likely occurrence. 
 
if (m_pLstHd == NULL) 
goto cleanup; 
 
ulLastSeqNo = m_pLstHd->m_SRow.lpProps[I_RULE_SEQUENCE].Value.ul; 
 
pNode = m_pLstHd->m_pNxt; 
 
while (pNode != NULL) 
{ 
if (pNode->m_SRow.lpProps[I_RULE_SEQUENCE].Value.ul <= ulLastSeqNo) 
pNode->m_SRow.lpProps[I_RULE_SEQUENCE].Value.ul = ulLastSeqNo + 1; 
 
ulLastSeqNo = pNode->m_SRow.lpProps[I_RULE_SEQUENCE].Value.ul; 
 
pNode = pNode->m_pNxt; 
} 
 
cleanup: 
 
    return; 
} 
 
 
// $--CSROWLST::HrWriteToTable------------------------------------------------- 
// 
// DESCRIPTION:Write the contents of the CSROWLST to an EXCHANGEMODIFYTABLE. 
// 
// INPUT: 
// 
//[lpExchTbl]-- Ptr to the EXCHANGEMODIFYTABLE that will be rewritten based 
//   on the contents of the CSROWLST. 
// 
// RETURNS:NOERRORon success;  
//              E_INVALIDARGif bad input; 
//E_OUTOFMEMORYif insufficient memory; 
//              E_FAILotherwise. 
// 
//----------------------------------------------------------------------------- 
 
HRESULT 
CSROWLST::HrWriteToTable(// RETURNS: HRESULT 
INLPEXCHANGEMODIFYTABLElpExchTbl   // Exchange modify tbl i/f ptr 
) 
{ 
HRESULThr =NOERROR; 
LPROWENTRYlpRowEntry =NULL; 
LPROWLISTlpRowList =NULL; 
CSROWNODE *pNode =NULL; 
 
    DEBUGPRIVATE("CSROWLST::HrWriteToTable()\n"); 
 
// Make a ROWLIST for use with the IExchangeModifyTable interface.  We 
// explicitly call MAPIAllocateBuffer() here due to the size vagaries  
// of MAPI structures. 
 
hr = MAPIAllocateBuffer(CbNewROWLIST(m_cNodes + m_cOtherProvNodes), 
(LPVOID FAR *)&lpRowList); 
if (FAILED(hr)) 
{ 
hr = HR_LOG(E_OUTOFMEMORY); 
goto cleanup; 
} 
 
lpRowList->cEntries = m_cNodes + m_cOtherProvNodes; 
 
pNode = m_pLstHd; 
 
lpRowEntry = lpRowList->aEntries; 
 
while (pNode != NULL) 
{ 
lpRowEntry->ulRowFlags =ROW_ADD; 
lpRowEntry->cValues =pNode->m_SRow.cValues; 
lpRowEntry->rgPropVals =pNode->m_SRow.lpProps; 
 
lpRowEntry++; 
 
pNode = pNode->m_pNxt; 
} 
 
pNode = m_pOtherProvLstHd; 
 
while (pNode != NULL) 
{ 
lpRowEntry->ulRowFlags =ROW_ADD; 
lpRowEntry->cValues =pNode->m_SRow.cValues; 
lpRowEntry->rgPropVals =pNode->m_SRow.lpProps; 
 
lpRowEntry++; 
 
pNode = pNode->m_pNxt; 
} 
 
// Modify the entire table. 
 
hr = lpExchTbl->ModifyTable(ROWLIST_REPLACE, lpRowList); 
 
if (FAILED(hr)) 
    { 
        hr = HR_LOG(E_FAIL); 
goto cleanup; 
    } 
 
cleanup: 
 
// Deallocate the ROWLIST (not including the SPropValue ptrs, since they 
// still actually belong to the CSROWLST). 
 
MAPIFREEBUFFER(lpRowList); 
 
RETURN(hr); 
}