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