RULECLS.CPP

// --rulecls.cpp------------------------------------------------------------- 
//
// API entry points into the Rule Class Library (rulecls).
//
// Copyright (C) Microsoft Corp. 1986-1996. All rights reserved.
//
// ---------------------------------------------------------------------------

#include "edk.h"
#include <errno.h>
#include "srowlst.h"
#include "ruleclsf.h"
#include "proptag.h"// temporary location for GetPropTag* functions.
#include "toktypes.h"

#include "rulecls.chk"


//
// Manifest constants.
//

#defineRES_UNINITIALIZED((ULONG) 0xffffffff)

//
// Macros
//

// ISNONTERMINALRESTRICTION() returns TRUE if lpRes is a nonterminal restriction

#define ISNONTERMINALRESTRICTION(lpRes)(((lpRes)->rt == RES_AND)||\
((lpRes)->rt == RES_OR)||\
((lpRes)->rt == RES_NOT))

// Forward function declarations.

static
HRESULT
HrAdrListDisplayNamesToString(
INLPADRLISTlpAdrList,
IN OUTLPSTR FAR *lppszString,
IN OUTLPULONGlpcbString,
IN OUTLPULONGlpcbBuffer
);

static
HRESULT
HrAndRestrictionToString(
INLPSRestrictionlpRes,
IN OUTLPSTR FAR *lppszString,
IN OUTLPULONGlpcbString,
IN OUTLPULONGlpcbBuffer
);

static
HRESULT
HrContentRestrictionToString(
INLPSRestrictionlpRes,
IN OUTLPSTR FAR *lppszString,
IN OUTLPULONGlpcbString,
IN OUTLPULONGlpcbBuffer
);

static
HRESULT
HrCopyProps(
INintcprop,
INLPSPropValuergprop,
OUTLPVOIDpvDst,
OUTULONG FAR * pcb
);

static
HRESULT
HrCopyRestrictionArray(
INLPSRestrictionlpResSrc,
INLPVOIDlpObject,
INULONGcRes,
OUTLPSRestrictionlpResDest
);

static
HRESULT
HrCountProps(
INint cprop,
INLPSPropValuergprop,
OUTULONG FAR *pcb
);

static
HRESULT
HrCreateReplyMsg(
INLPMAPIFOLDERlpFolder,
INLPSTRlpszReplyText,
INLPVOIDlpObject,
OUTULONG FAR *lpcbentryid,
OUTLPENTRYID FAR *lppentryid
);

static
HRESULT
HrDupPropset(
INintcprop,
INLPSPropValuergprop,
INLPVOIDlpObject,
OUTLPSPropValue FAR *prgprop
);

static
HRESULT
HrGetExplicitTag(
IN OUTTOKENINFO *pti
);

static
HRESULT
HrGetExpressionToken(
IN OUTTOKENINFO *pti
);

static
HRESULT
HrGetFolderPath(
INLPMDBlpMDB,
INULONGcbentryid,
INLPENTRYIDlpentryid,
OUTLPSTR FAR *lppszFolderPath
);

static
HRESULT
HrGetMoveCopyArgs(
INLPMAPISESSIONlpSession,
INLPSTRpszFolder,
INLPVOIDlpObject,
IN OUTLPACTIONlpAction
);

static
HRESULT
HrGetNamedTag(
IN OUTTOKENINFO *pti
);

static
HRESULT
HrGetNumericLiteral(
IN OUTTOKENINFO *pti
);

static
HRESULT
HrGetStoreName(
INLPMDBlpMDB,
OUTLPSTR FAR *lppszName
);

static
HRESULT
HrGetStringLiteral(
IN OUTTOKENINFO *pti
);

static
HRESULT
HrGetTimeLiteral(
IN OUTTOKENINFO *pti
);

static
HRESULT
HrNotRestrictionToString(
INLPSRestrictionlpRes,
IN OUTLPSTR FAR *lppszString,
IN OUTLPULONGlpcbString,
IN OUTLPULONGlpcbBuffer
);

static
HRESULT
HrOrRestrictionToString(
INLPSRestrictionlpRes,
IN OUTLPSTR FAR *lppszString,
IN OUTLPULONGlpcbString,
IN OUTLPULONGlpcbBuffer
);

static
HRESULT
HrParseAction(
INLPMAPISESSIONlpSession,
INLPMAPIFOLDERlpFolder,
INLPVOIDlpObject,
INCHAR *pch,
IN OUTLPACTIONlpAction
);

static
HRESULT
HrParseCondition(
INLPVOIDlpObject,
IN OUTCHAR **ppch,
IN OUTLPSRestrictionlpRes
);

static
HRESULT
HrPrintToString(
IN OUTLPSTR FAR *lppszString,
IN OUTLPULONGlpcbString,
IN OUTLPULONGlpcbBuffer,
INLPSTRFormat
...
);

static
HRESULT
HrPrivateRestrictionToString(
INLPSRestrictionlpRes,
IN OUTLPSTR FAR *lppszString,
IN OUTLPULONGlpcbString,
IN OUTLPULONGlpcbBuffer
);

static
HRESULT
HrPropertyRestrictionToString(
INLPSRestrictionlpRes,
IN OUTLPSTR FAR *lppszString,
IN OUTLPULONGlpcbString,
IN OUTLPULONGlpcbBuffer
);

static
HRESULT
HrPropValueToString(
INLPSPropValuelpProp,
IN OUTLPSTR FAR *lppszString,
IN OUTLPULONGlpcbString,
IN OUTLPULONGlpcbBuffer
);

inline
CHAR *
lpszSkipNonWhiteSpace(
INCHAR *pch
);

inline
CHAR *
lpszSkipWhiteSpace(
INCHAR *pch
);

//
// Public functions
//

// $--HrCopyActions------------------------------------------------------------
//
// DESCRIPTION:Make a copy of an ACTIONS structure and all its subelements.
//
// INPUT:
//
//[lpActsSrc]-- Ptr to ACTIONS structure to be copied.
//[lpObject]-- Ptr to an existing MAPI buffer allocated by
// MAPIAllocateBuffer(), or NULL if the returned structure
// is to be allocated using MAPIAllocateBuffer().
// OUTPUT:
//
//[lppActsDest]-- Ptr to be set to copy of ACTIONS structure.
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
//E_OUTOFMEMORYif out of memory;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

STDAPI
HrCopyActions(// RETURNS: HRESULT
INLPACTIONSlpActsSrc, // source action ptr
INLPVOIDlpObject,// ptr to existing MAPI buffer
OUTLPACTIONS FAR *lppActsDest // ptr to destination ACTIONS buffer
)
{
BOOLfNullObject =(lpObject == NULL);
HRESULThr =NOERROR;
ULONGi =0;
LPACTIONlpActDest =NULL;
LPACTIONlpActSrc =NULL;
LPACTIONSlpActsDest =NULL;

DEBUGPUBLIC("HrCopyActions()\n");

hr = CHK_HrCopyActions(lpActsSrc, lpObject, lppActsDest);

if (FAILED(hr))
RETURN(hr);

*lppActsDest = NULL;

if (lpActsSrc->cActions <= 0 || lpActsSrc->lpAction == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

if (lpObject != NULL)
{
hr = MAPIAllocateMore(sizeof(ACTIONS),
lpObject,
(LPVOID FAR *)lppActsDest);
}
else
{
hr = MAPIAllocateBuffer(sizeof(ACTIONS),
(LPVOID FAR *)lppActsDest);

lpObject = *lppActsDest;
}

if (FAILED(hr))
goto cleanup;

lpActsDest = *lppActsDest;

*lpActsDest = *lpActsSrc;

lpActsDest->lpAction = NULL;

hr = MAPIAllocateMore(sizeof(ACTION) * lpActsDest->cActions,
lpObject,
(LPVOID FAR *)&(lpActsDest->lpAction));
if (FAILED(hr))
goto cleanup;

// Initialize acttype values for all members of the array to a value
// that will not cause deallocation errors should the copy fail.

for (i = 0; i < lpActsDest->cActions; i++)
lpActsDest->lpAction[i].acttype = OP_BOUNCE;

// Now actually copy all the members of the array. The number of
// actions should be small, so we don't worry about a more efficient
// loop implementation (getting rid of i and just using ptrs).

for (i = 0; i < lpActsDest->cActions; i++)
{
lpActDest =&(lpActsDest->lpAction[i]);
lpActSrc =&(lpActsSrc->lpAction[i]);

*lpActDest = *lpActSrc;

// Note -Any 0 source allocations (ie. cb==0 or ptr==NULL) cause
//the function to fail. If there are instances in which
//such input is valid, we should evaluate them case by
//case.

switch (lpActSrc->acttype)
{
case OP_MOVE:// actMoveCopy
case OP_COPY:
{
if (lpActDest->actMoveCopy.cbStoreEntryId == 0||
lpActDest->actMoveCopy.lpStoreEntryId == NULL||
lpActDest->actMoveCopy.cbFldEntryId == 0||
lpActDest->actMoveCopy.lpFldEntryId == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = MAPIAllocateMore(lpActDest->actMoveCopy.cbStoreEntryId,
lpObject,
(LPVOID FAR *)
&(lpActDest->actMoveCopy.lpStoreEntryId));
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

memcpy(lpActDest->actMoveCopy.lpStoreEntryId,
lpActSrc->actMoveCopy.lpStoreEntryId,
lpActSrc->actMoveCopy.cbStoreEntryId);

hr = MAPIAllocateMore(lpActDest->actMoveCopy.cbFldEntryId,
lpObject,
(LPVOID FAR *)
&(lpActDest->actMoveCopy.lpFldEntryId));
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

memcpy(lpActDest->actMoveCopy.lpFldEntryId,
lpActSrc->actMoveCopy.lpFldEntryId,
lpActSrc->actMoveCopy.cbFldEntryId);

break;
}

case OP_REPLY:// actReply
case OP_OOF_REPLY:
{
if (lpActDest->actReply.cbEntryId == 0||
lpActDest->actReply.lpEntryId == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = MAPIAllocateMore(lpActDest->actReply.cbEntryId,
lpObject,
(LPVOID FAR *)
&(lpActDest->actReply.lpEntryId));
if (FAILED(hr))
goto cleanup;

memcpy(lpActDest->actReply.lpEntryId,
lpActSrc->actReply.lpEntryId,
lpActSrc->actReply.cbEntryId);

break;
}

case OP_DEFER_ACTION:// actDeferAction
{
if (lpActSrc->actDeferAction.pbData == NULL||
lpActSrc->actDeferAction.cbData == 0)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = MAPIAllocateMore(lpActDest->actDeferAction.cbData,
lpObject,
(LPVOID FAR *)
&(lpActDest->actDeferAction.pbData));
if (FAILED(hr))
goto cleanup;

memcpy(lpActDest->actDeferAction.pbData,
lpActSrc->actDeferAction.pbData,
lpActDest->actDeferAction.cbData);

break;
}

case OP_BOUNCE:// scBounceCode
{
// Nothing to do!

break;
}


case OP_FORWARD:// lpadrlist
case OP_DELEGATE:
{
ULONGcEntries =0;
ULONGi =0;
LPADRLISTlpAdrListSrc =lpActSrc->lpadrlist;
LPADRLISTlpAdrListDest =NULL;

lpActDest->lpadrlist = NULL;

if (lpAdrListSrc == NULL || lpAdrListSrc->cEntries == 0)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = MAPIAllocateMore(CbADRLIST(lpAdrListSrc),
lpObject,
(LPVOID FAR *)&(lpActDest->lpadrlist));
if (FAILED(hr))
goto cleanup;

cEntries = lpAdrListSrc->cEntries;

lpAdrListDest = lpActDest->lpadrlist;

lpAdrListDest->cEntries = cEntries;

// Initialize the new ADRENTRY's and validate cValues.

for (i = 0; i < cEntries; i++)
{
lpAdrListDest->aEntries[i] = lpAdrListSrc->aEntries[i];

lpAdrListDest->aEntries[i].rgPropVals = NULL;

if (lpAdrListDest->aEntries[i].cValues == 0)
hr = HR_LOG(E_FAIL);
}

if (FAILED(hr))
goto cleanup;

// Copy the rgPropVals.

for (i = 0; i < cEntries; i++)
{
hr = HrDupPropset(lpAdrListDest->aEntries[i].cValues,
lpAdrListSrc->aEntries[i].rgPropVals,
lpObject,
&lpAdrListDest->aEntries[i].rgPropVals);

if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}

break;
}

case OP_TAG:// propTag
{
hr = PropCopyMore(&lpActDest->propTag,
&lpActSrc->propTag,
MAPIAllocateMore,
lpObject);

if (FAILED(hr))
{
if (hr != MAPI_E_NOT_ENOUGH_MEMORY)
hr = HR_LOG(E_FAIL);

goto cleanup;
}

break;
}

case OP_DELETE:// union not used
case OP_MARK_AS_READ:
{
// Nothing to do!

break;
}

default:// error!
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
}

cleanup:

if (FAILED(hr))
{
if (fNullObject)
MAPIFREEBUFFER(*lppActsDest);
}

RETURN(hr);
}


// $--HrCopyRestriction--------------------------------------------------------
//
// DESCRIPTION:Make a copy of an SRestriction structure and all its
//subelements.
//
// INPUT:
//
//[lpResSrc]-- Ptr to restriction to be copied.
//[lpObject]-- Ptr to an existing MAPI buffer allocated by
// MAPIAllocateBuffer(), or NULL if the returned structure
// is to be allocated using MAPIAllocateBuffer().
//
// OUTPUT:
//
//[lppResDest]-- Ptr to be set to copy of restriction.
//
// RETURNS: NOERRORif successful;
//E_INVALIDARGif bad input;
//E_OUTOFMEMORYif out of memory;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

STDAPI
HrCopyRestriction(// RETURNS: HRESULT
INLPSRestrictionlpResSrc, // source restriction ptr
INLPVOIDlpObject,// ptr to existing MAPI buffer
OUTLPSRestriction FAR *lppResDest // dest restriction buffer ptr
)
{
BOOLfNullObject =(lpObject == NULL);
HRESULThr =NOERROR;

DEBUGPUBLIC("HrCopyRestriction()\n");

hr = CHK_HrCopyRestriction(lpResSrc, lpObject, lppResDest);

if (FAILED(hr))
RETURN(hr);

*lppResDest = NULL;

if (lpObject != NULL)
{
hr = MAPIAllocateMore(sizeof(SRestriction),
lpObject,
(LPVOID FAR *)lppResDest);
}
else
{
hr = MAPIAllocateBuffer(sizeof(SRestriction),
(LPVOID FAR *)lppResDest);

lpObject = *lppResDest;
}

if (FAILED(hr))
goto cleanup;

hr = HrCopyRestrictionArray(lpResSrc, lpObject, 1, *lppResDest);

if (FAILED(hr))
{
if (fNullObject)
MAPIFREEBUFFER(*lppResDest);
}

cleanup:

RETURN(hr);
}


// $--HrFolderRulesGetProviders------------------------------------------------
//
// DESCRIPTION:Get an array of rules provider names for a given folder.
//
// INPUT:
//
//[lpMDB]-- Pointer to message store containing folder.
// [cbentryid]-- Number of bytes in folder's entry identifier.
// [lpentryid]-- Folder's entry identifier.
//
// 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_INVALIDARGif bad input;
//E_OUTOFMEMORYif not enough memory;
//E_NOINTERFACEif rules table does not exist on folder;
// E_FAIL otherwise.
//
//-----------------------------------------------------------------------------

STDAPI
HrFolderRulesGetProviders(// RETURNS: HRESULT
INLPMDBlpMDB,// MAPI MDB store ptr
INULONGcbentryid,// # bytes in entry ID
INLPENTRYIDlpentryid,// entry ID ptr
OUTLPULONGlpcProviders,// count of providers
OUTLPSTR FAR * FAR *lpppszProviders// ptr to array of providers
)
{
HRESULThr =NOERROR;
CFolderRules FAR *pFolderRules =NULL;

DEBUGPUBLIC("HrFolderRulesGetProviders()\n");

hr = CHK_HrFolderRulesGetProviders(cbentryid,
lpentryid,
lpcProviders,
lpppszProviders);
if (FAILED(hr))
RETURN(hr);

// Initialize controlling class object.

pFolderRules = new CFolderRules();

if (pFolderRules == NULL)
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}

// Open the folder in the controlling class object.

hr = pFolderRules->HrOpen(lpMDB, cbentryid, lpentryid, NULL);

if (FAILED(hr))
goto cleanup;

hr = pFolderRules->HrGetProviders(lpcProviders, lpppszProviders);

if (FAILED(hr))
goto cleanup;

cleanup:

// Release our copy of the CFolderRules object.

ULRELEASE(pFolderRules);// Ref count now 1.

RETURN(hr);
}


// $--HrFolderRulesOpen--------------------------------------------------------
//
// DESCRIPTION:Get a pointer to an object which implements the
//IExchangeFolderRules interface defined in rulecls.h.
//
// INPUT:
//
//[lpMDB]-- Pointer to message store containing folder.
// [cbentryid]-- Number of bytes in folder's entry identifier.
// [lpentryid]-- Folder's entry identifier.
// [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.
//
// OUTPUT:
//
// [lppFolderRules]-- Pointer to object which supports interface.
//
// RETURNS: NOERRORif successful;
//E_INVALIDARGif bad input;
//E_OUTOFMEMORYif not enough memory;
//E_NOINTERFACEif rules table does not exist on folder;
// E_FAIL otherwise.
//
//-----------------------------------------------------------------------------

STDAPI
HrFolderRulesOpen(// RETURNS: HRESULT
INLPMDBlpMDB,// MAPI MDB store ptr
INULONGcbentryid,// # bytes in entry ID
INLPENTRYIDlpentryid,// entry ID ptr
INLPSTRlpszProvider,// provider name
OUTLPFOLDERRULES FAR *lppFolderRules // ptr to folder rules buffer
)
{
HRESULThr =NOERROR;
CFolderRules FAR *pFolderRules =NULL;

DEBUGPUBLIC("HrFolderRulesOpen()\n");

hr = CHK_HrFolderRulesOpen(cbentryid,
lpentryid,
lpszProvider,
lppFolderRules);
if (FAILED(hr))
RETURN(hr);

*lppFolderRules = NULL;

// Initialize controlling class object.

pFolderRules = new CFolderRules();

if (pFolderRules == NULL)
{
hr = HR_LOG(E_OUTOFMEMORY);
goto cleanup;
}

// Open the folder in the controlling class object.

hr = pFolderRules->HrOpen(lpMDB, cbentryid, lpentryid, lpszProvider);

if (FAILED(hr))
goto cleanup;

// Give the user access to the programmer interface (CIExchangeFolderRules)
// on the controlling CFolderRules object.

hr = pFolderRules->QueryInterface(IID_IExchangeFolderRules,
(LPVOID FAR *)lppFolderRules);
// Ref count now 2.

if (FAILED(hr))
goto cleanup;

ASSERT_READ_PTR_OR_NULL(*lppFolderRules, sizeof(LPFOLDERRULES),
"Bad lppFolderRules.");

cleanup:

// Release our copy of the CFolderRules object.

ULRELEASE(pFolderRules);// Ref count now 1.

RETURN(hr);
}


// $--HrActionToString---------------------------------------------------------
//
// DESCRIPTION:Get a string representation of an ACTION.
//
// INPUT:
//
// [lpSession]-- MAPI session ptr.
//[lpAction]-- Ptr to ACTION.
//
// OUTPUT:
//
//[lppszString]-- Ptr to ptr that will be set to point at generated
// string representation on successful return.
//
// RETURNS: NOERROR if successful;
// E_INVALIDARG if bad input;
// E_OUTOFMEMORYif not enough memory;
// E_FAILotherwise.
//
//-----------------------------------------------------------------------------

STDAPI
HrActionToString(// RETURNS: HRESULT
INLPMAPISESSIONlpSession, // MAPI session ptr
INLPACTIONlpAction,// action ptr
OUTLPSTR FAR *lppszString// string ptr ptr
)
{
ULONGcbBuffer =0;
ULONGcbString =0;
HRESULThr =NOERROR;
LPSTRlpszActType =NULL;

DEBUGPUBLIC("HrActionToString()\n");

hr = CHK_HrActionToString(lpSession, lpAction, lppszString);

if (FAILED(hr))
RETURN(hr);

*lppszString = NULL;

switch (lpAction->acttype)
{
case OP_MOVE:
lpszActType = "Move to ";
break;

case OP_COPY:
lpszActType = "Copy to ";
break;

case OP_REPLY:
lpszActType = "Reply";
break;

case OP_OOF_REPLY:
lpszActType = "Out-of-Office Reply";
break;

case OP_DEFER_ACTION:
lpszActType = "Provider-specific deferred action";
break;

case OP_BOUNCE:
lpszActType = "Bounce with error code ";
break;

case OP_FORWARD:
lpszActType = "Forward to ";
break;

case OP_DELEGATE:
lpszActType = "Delegate to ";
break;

case OP_TAG:
lpszActType = "Tag ";
break;

case OP_DELETE:
lpszActType = "Delete";
break;

case OP_MARK_AS_READ:
lpszActType = "Mark as Read";
break;

default:
lpszActType = "Invalid action type";
break;
}

hr = HrPrintToString(lppszString, &cbString, &cbBuffer, lpszActType);

if (FAILED(hr))
goto cleanup;

switch (lpAction->acttype)
{
case OP_MOVE:
case OP_COPY:
{
LPMDBlpMDB =NULL;
LPSTRlpszFolderPath =NULL;
LPSTRlpszStoreName = NULL;

// Open the message store.

hr = lpSession->OpenMsgStore(0,
lpAction->actMoveCopy.cbStoreEntryId,
lpAction->actMoveCopy.lpStoreEntryId,
NULL,
MAPI_DEFERRED_ERRORS,
&lpMDB);
if (FAILED(hr))
goto cleanup;

hr = HrGetStoreName(lpMDB, &lpszStoreName);

if (FAILED(hr))
{
ULRELEASE(lpMDB);
goto cleanup;
}

hr = HrGetFolderPath(lpMDB,
lpAction->actMoveCopy.cbFldEntryId,
lpAction->actMoveCopy.lpFldEntryId,
&lpszFolderPath);

if (FAILED(hr))
{
MAPIFREEBUFFER(lpszStoreName);
ULRELEASE(lpMDB);
goto cleanup;
}

hr = HrPrintToString(lppszString,
&cbString,
&cbBuffer,
"%s\\%s", lpszStoreName, lpszFolderPath);

MAPIFREEBUFFER(lpszFolderPath);
MAPIFREEBUFFER(lpszStoreName);
ULRELEASE(lpMDB);

if (FAILED(hr))
goto cleanup;

break;
}

case OP_REPLY:
case OP_OOF_REPLY:
{
;// Nothing more to do.
break;
}

case OP_DEFER_ACTION:
{
;// Nothing more to do.
break;
}

case OP_BOUNCE:
{
LPSTRlpszBounceCode = NULL;

if (lpAction->scBounceCode == BOUNCE_MESSAGE_SIZE_TOO_LARGE)
lpszBounceCode = "BOUNCE_MESSAGE_SIZE_TOO_LARGE";
else if (lpAction->scBounceCode == BOUNCE_FORMS_MISMATCH)
lpszBounceCode = "BOUNCE_FORMS_MISMATCH";
else if (lpAction->scBounceCode == BOUNCE_ACCESS_DENIED)
lpszBounceCode = "BOUNCE_ACCESS_DENIED";

if (lpszBounceCode != NULL)
{
hr = HrPrintToString(lppszString,
&cbString,
&cbBuffer,
lpszBounceCode);
}
else
{
hr = HrPrintToString(lppszString,
&cbString,
&cbBuffer,
"%#x (invalid bounce code)",
lpAction->scBounceCode);
}

if (FAILED(hr))
goto cleanup;

break;
}

case OP_FORWARD:
case OP_DELEGATE:
{
hr = HrAdrListDisplayNamesToString(lpAction->lpadrlist,
lppszString,
&cbString,
&cbBuffer);
if (FAILED(hr))
goto cleanup;

break;
}

case OP_TAG:
{
LPSTRlpszPropTag =NULL;
LPSTRlpszPropValue =NULL;

hr = HrGetPropTagName(lpAction->propTag.ulPropTag,
&lpszPropTag);

if (FAILED(hr))
goto cleanup;

{
ULONGcbString =0;
ULONGcbBuffer =0;

hr = HrPropValueToString(&lpAction->propTag,
&lpszPropValue,
&cbString,
&cbBuffer);
}

if (FAILED(hr))
{
MAPIFREEBUFFER(lpszPropTag);
goto cleanup;
}

hr = HrPrintToString(lppszString,
&cbString,
&cbBuffer,
"%s = %s", lpszPropTag, lpszPropValue);

MAPIFREEBUFFER(lpszPropTag);
MAPIFREEBUFFER(lpszPropValue);

if (FAILED(hr))
goto cleanup;

break;
}

case OP_DELETE:
{
;// Nothing more to do.
break;
}

case OP_MARK_AS_READ:
{
;// Nothing more to do.
break;
}

default:
{
;// Nothing more to do.
break;
}
}

cleanup:

if (FAILED(hr))
{
MAPIFREEBUFFER(*lppszString);
}
else
{
// If buffer is longer than necessary, reallocate.

if (cbBuffer > cbString)
{
LPSTRlpszString = NULL;

hr = MAPIAllocateBuffer(cbString, (LPVOID FAR *)&lpszString);

if (FAILED(hr))
{
MAPIFREEBUFFER(*lppszString);
}
else
{
memcpy(lpszString, *lppszString, cbString);

MAPIFREEBUFFER(*lppszString);

*lppszString = lpszString;
}
}
}

RETURN(hr);
}


// $--HrRestrictionToString----------------------------------------------------
//
// DESCRIPTION:Get a string representation of an SRestriction.
//
// INPUT:
//
//[lpRestriction]-- Ptr to restriction.
//
// OUTPUT:
//
//[lppszString]-- Ptr to ptr that will be set to point at generated
// string representation on successful return.
//
// RETURNS: NOERROR if successful;
// E_INVALIDARG if bad input;
// E_OUTOFMEMORYif not enough memory;
// E_FAILotherwise.
//
//-----------------------------------------------------------------------------

STDAPI
HrRestrictionToString(// RETURNS: HRESULT
INLPSRestrictionlpRestriction,// restriction ptr
OUTLPSTR FAR *lppszString// string ptr ptr
)
{
ULONGcbBuffer =0;
ULONGcbString =0;
HRESULThr =NOERROR;

DEBUGPUBLIC("HrRestrictionToString()\n");

hr = CHK_HrRestrictionToString(lpRestriction, lppszString);

if (FAILED(hr))
RETURN(hr);

*lppszString = NULL;

hr = HrPrivateRestrictionToString(lpRestriction,
lppszString,
&cbString,
&cbBuffer);
if (FAILED(hr))
goto cleanup;

cleanup:

if (FAILED(hr))
{
MAPIFREEBUFFER(*lppszString);
}
else
{
// If buffer is longer than necessary, reallocate.

if (cbBuffer > cbString)
{
LPSTRlpszString = NULL;

hr = MAPIAllocateBuffer(cbString, (LPVOID FAR *)&lpszString);

if (FAILED(hr))
{
MAPIFREEBUFFER(*lppszString);
}
else
{
memcpy(lpszString, *lppszString, cbString);

MAPIFREEBUFFER(*lppszString);

*lppszString = lpszString;
}
}
}

RETURN(hr);
};


// $--HrStringToAction---------------------------------------------------------
//
// DESCRIPTION:Generate an ACTION structure that describes the action
//provided in *lpszString.
//
// INPUT:
//
//[lpSession]-- Ptr to MAPI session.
//[lpFolder]-- Ptr to MAPI folder action applies to. MUST have been
// opened with MAPI_MODIFY access.
//[lpszString]-- Action string specifying ACTION to be generated.
//[lpObject]-- Ptr to an existing MAPI buffer allocated by
// MAPIAllocateBuffer(). NULL is not permitted. Ideally,
// this is a ptr to the parent ACTIONS structure.
//
// INPUT/OUTPUT:
//
//[lpAction]-- Ptr to ACTION structure that will be filled in. This
// is allocated by the caller. Ideally, this is a ptr to
// an ACTION array element that was allocated by the caller
// using a MAPIAllocateMore() that referenced the parent
// ACTIONS structure.
//
// RETURNS: NOERRORif successful;
//E_INVALIDARGif bad input;
// E_OUTOFMEMORYif out of memory;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

STDAPI
HrStringToAction(// RETURNS: HRESULT
INLPMAPISESSIONlpSession,// ptr to MAPI session
INLPMAPIFOLDERlpFolder,// ptr to MAPI folder
INLPSTRlpszString,// string ptr
INLPVOIDlpObject,// ptr to existing MAPI buf
IN OUTLPACTIONlpAction // ACTION ptr
)
{
HRESULThr =NOERROR;
LPSTRlpszStringCopy =NULL;

DEBUGPUBLIC("HrStringToAction()\n");

hr = CHK_HrStringToAction(lpSession,
lpFolder,
lpszString,
lpObject,
lpAction);
if (FAILED(hr))

RETURN(hr); 

// Make copy of lpszString so you can write to it safely.

hr = MAPIAllocateBuffer(strlen(lpszString) + 1,
(LPVOID FAR *)&lpszStringCopy);
if (FAILED(hr))
goto cleanup;

strcpy(lpszStringCopy, lpszString);

hr = HrParseAction(lpSession, lpFolder, lpObject, lpszStringCopy, lpAction);

if (FAILED(hr))
goto cleanup;

cleanup:

MAPIFREEBUFFER(lpszStringCopy);

RETURN(hr);
}


// $--HrStringToRestriction----------------------------------------------------
//
// DESCRIPTION:Generate an SRestriction structure that describes the
//restriction condition provided in *lpszString. The
//grammar for *lpszString is given in the header file
//rulecls.h.
//
// INPUT:
//
//[lpszString]-- Condition string specifying restriction to be generated.
//[lpObject]-- Ptr to an existing MAPI buffer allocated by
// MAPIAllocateBuffer(), or NULL if the returned structure
// is to be allocated using MAPIAllocateBuffer().
//
// OUTPUT:
//
//[lppRestriction]-- Ptr to ptr that will be set to point at generated
// restriction on successful return.
//
// RETURNS: NOERRORif successful;
//E_INVALIDARGif bad input;
// E_OUTOFMEMORYif out of memory;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

STDAPI
HrStringToRestriction(// RETURNS: HRESULT
INLPSTRlpszString,// string
INLPVOIDlpObject,// ptr to existing MAPI buf
OUTLPSRestriction FAR *lppRestriction // restriction buffer ptr
)
{
BOOLfNullObject =(lpObject == NULL);
HRESULThr =NOERROR;
LPSTRlpszStringCopy =NULL;
CHAR *pch =NULL;

DEBUGPUBLIC("HrStringToRestriction()\n");

hr = CHK_HrStringToRestriction(lpszString, lpObject, lppRestriction);

if (FAILED(hr))
RETURN(hr);

// Make copy of lpszString so you can write to it safely.

hr = MAPIAllocateBuffer(strlen(lpszString) + 1,
(LPVOID FAR *)&lpszStringCopy);
if (FAILED(hr))
goto cleanup;

strcpy(lpszStringCopy, lpszString);

if (lpObject != NULL)
{
hr = MAPIAllocateMore(sizeof(SRestriction),
lpObject,
(LPVOID FAR *)lppRestriction);
}
else
{
hr = MAPIAllocateBuffer(sizeof(SRestriction),
(LPVOID FAR *)lppRestriction);

lpObject = *lppRestriction;
}

if (FAILED(hr))
goto cleanup;

(*lppRestriction)->rt = RES_UNINITIALIZED;

pch = lpszStringCopy;

hr = HrParseCondition(lpObject, &pch, *lppRestriction);

if (*pch != '\0')
hr = HR_LOG(E_INVALIDARG);

if (FAILED(hr))
{
if (fNullObject)
MAPIFREEBUFFER(*lppRestriction);

goto cleanup;
}

cleanup:

MAPIFREEBUFFER(lpszStringCopy);

RETURN(hr);
}


//
// Private functions
//


// $--HrAdrListDisplayNamesToString--------------------------------------------
//
// DESCRIPTION:Get a string representation of the display names in an ADRLIST.
//
// INPUT:
//
//[lpAdrList]-- Ptr to ADRLIST, the display names of which are to be
// represented as a string.
//
// INPUT/OUTPUT:
//
//[lppszString]-- Output string ptr ptr. If *lppszString == NULL, then
// a string will be allocated by this routine.
//[lpcbString]-- Ptr to count of bytes in input string on input (including
// terminating '\0'); count of bytes in string after
// appending formatted string on output.
//[lpcbBuffer]-- Ptr to count of bytes in input string buffer on input;
// count of bytes in string buffer after appending formatted
// string on output (buffer size may increase if allocation
// was necessary).
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrAdrListDisplayNamesToString( // RETURNS: HRESULT
INLPADRLISTlpAdrList,// address list ptr
IN OUTLPSTR FAR *lppszString,// output string ptr ptr
IN OUTLPULONGlpcbString,// ptr to count of bytes in string
IN OUTLPULONGlpcbBuffer// ptr to count of bytes in buffer
)
{
HRESULThr =NOERROR;
ULONGi =0;
ULONGj =0;
LPADRENTRYlpAdrEntry =NULL;
LPSTRlpszFormat ="\"%s\"";

DEBUGPRIVATE("HrAdrListDisplayNamesToString()\n");

for (i = 0; i < lpAdrList->cEntries; i++)
{
lpAdrEntry = &lpAdrList->aEntries[i];

for (j = 0; j < lpAdrEntry->cValues; j++)
{
if (lpAdrEntry->rgPropVals[j].ulPropTag == PR_DISPLAY_NAME)
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
lpszFormat,
lpAdrEntry->rgPropVals[j].Value.lpszA);
if (FAILED(hr))
goto cleanup;

lpszFormat = ", \"%s\"";

break;
}
}
}

cleanup:

RETURN(hr);
}


// $--HrAndRestrictionToString-------------------------------------------------
//
// DESCRIPTION:Get a string representation of an SAndRestriction.
//
// INPUT:
//
//[lpRes]-- Ptr to and restriction to be represented as a string.
//
// INPUT/OUTPUT:
//
//[lppszString]-- Output string ptr ptr. If *lppszString == NULL, then
// a string will be allocated by this routine.
//[lpcbString]-- Ptr to count of bytes in input string on input (including
// terminating '\0'); count of bytes in string after
// appending formatted string on output.
//[lpcbBuffer]-- Ptr to count of bytes in input string buffer on input;
// count of bytes in string buffer after appending formatted
// string on output (buffer size may increase if allocation
// was necessary).
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrAndRestrictionToString( // RETURNS: HRESULT
INLPSRestrictionlpRes,// restriction ptr
IN OUTLPSTR FAR *lppszString,// output string ptr ptr
IN OUTLPULONGlpcbString,// ptr to count of bytes in string
IN OUTLPULONGlpcbBuffer// ptr to count of bytes in buffer
)
{
HRESULThr =NOERROR;
ULONGi =0;
SAndRestriction *presAnd=NULL;

DEBUGPRIVATE("HrAndRestrictionToString()\n");

presAnd = &lpRes->res.resAnd;

if (presAnd->cRes < 2)
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<Invalid SAndRestriction>");
if (FAILED(hr))
goto cleanup;
}
else
{
hr = HrPrivateRestrictionToString(presAnd->lpRes,
lppszString,
lpcbString,
lpcbBuffer);
if (FAILED(hr))
goto cleanup;

for (i = 1; i < presAnd->cRes; i++)
{
hr = HrPrintToString(lppszString, lpcbString, lpcbBuffer, " & ");

if (FAILED(hr))
goto cleanup;

if (ISNONTERMINALRESTRICTION(presAnd->lpRes + i))
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"(");

if (FAILED(hr))
goto cleanup;

hr = HrPrivateRestrictionToString(presAnd->lpRes + i,
lppszString,
lpcbString,
lpcbBuffer);
if (FAILED(hr))
goto cleanup;

hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
")");
if (FAILED(hr))
goto cleanup;
}
else
{
hr = HrPrivateRestrictionToString(presAnd->lpRes + i,
lppszString,
lpcbString,
lpcbBuffer);
if (FAILED(hr))
goto cleanup;
}
}
}

cleanup:

RETURN(hr);
}


// $--HrContentRestrictionToString---------------------------------------------
//
// DESCRIPTION:Get a string representation of an SContentRestriction.
//
// INPUT:
//
//[lpRes]-- Ptr to content restriction to be represented as a string.
//
// INPUT/OUTPUT:
//
//[lppszString]-- Output string ptr ptr. If *lppszString == NULL, then
// a string will be allocated by this routine.
//[lpcbString]-- Ptr to count of bytes in input string on input (including
// terminating '\0'); count of bytes in string after
// appending formatted string on output.
//[lpcbBuffer]-- Ptr to count of bytes in input string buffer on input;
// count of bytes in string buffer after appending formatted
// string on output (buffer size may increase if allocation
// was necessary).
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrContentRestrictionToString( // RETURNS: HRESULT
INLPSRestrictionlpRes,// restriction ptr
IN OUTLPSTR FAR *lppszString,// output string ptr ptr
IN OUTLPULONGlpcbString,// ptr to count of bytes in string
IN OUTLPULONGlpcbBuffer// ptr to count of bytes in buffer
)
{
HRESULThr = NOERROR;
LPSTRlpszPropName =NULL;

DEBUGPRIVATE("HrContentRestrictionToString()\n");

hr = HrGetPropTagName(lpRes->res.resContent.ulPropTag,
&lpszPropName);
if (FAILED(hr))
goto cleanup;

hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"%s } ",
lpszPropName);
if (FAILED(hr))
goto cleanup;

hr = HrPropValueToString(lpRes->res.resProperty.lpProp,
lppszString,
lpcbString,
lpcbBuffer);
if (FAILED(hr))
goto cleanup;

cleanup:

MAPIFREEBUFFER(lpszPropName);

RETURN(hr);
}


// $--HrCopyRestrictionArray---------------------------------------------------
//
// DESCRIPTION: Copy an SRestriction array.
//
// INPUT:
//
//[lpResSrc]-- Ptr to restriction array to be copied.
//[lpObject]-- Ptr to an existing MAPI buffer allocated by
// MAPIAllocateBuffer(). NULL is not permitted!
//[cRes]-- Count of array elements.
//
// OUTPUT:
//
//[lpResDest]-- Ptr to destination restriction array (allocated by caller).
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
//E_OUTOFMEMORYif out of memory;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrCopyRestrictionArray(// RETURNS: HRESULT
INLPSRestrictionlpResSrc, // source restriction
INLPVOIDlpObject,// ptr to existing MAPI buffer
INULONGcRes, // # elements in array
OUTLPSRestrictionlpResDest // destination restriction
)
{
HRESULThr =NOERROR;
ULONGi;

DEBUGPRIVATE("HrCopyRestrictionArray()\n");

for (i = 0; i < cRes; i++)
{
lpResDest[i] = lpResSrc[i];

switch (lpResSrc[i].rt)
{
case RES_AND:
{
ULONGcRes =lpResSrc[i].res.resAnd.cRes;
LPSRestrictionlpRes =lpResSrc[i].res.resAnd.lpRes;

if (cRes == 0 || lpRes == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = MAPIAllocateMore(sizeof(SRestriction) * cRes,
lpObject,
(LPVOID FAR *)
&lpResDest[i].res.resAnd.lpRes);
if (FAILED(hr))
goto cleanup;

hr = HrCopyRestrictionArray(lpRes,
lpObject,
cRes,
lpResDest[i].res.resAnd.lpRes);
if (FAILED(hr))
goto cleanup;

break;
}

case RES_OR:
{
ULONGcRes =lpResSrc[i].res.resOr.cRes;
LPSRestrictionlpRes =lpResSrc[i].res.resOr.lpRes;

if (cRes == 0 || lpRes == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = MAPIAllocateMore(sizeof(SRestriction) * cRes,
lpObject,
(LPVOID FAR *)
&lpResDest[i].res.resOr.lpRes);
if (FAILED(hr))
goto cleanup;

hr = HrCopyRestrictionArray(lpRes,
lpObject,
cRes,
lpResDest[i].res.resOr.lpRes);
if (FAILED(hr))
goto cleanup;

break;
}

case RES_NOT:
{
LPSRestrictionlpRes = lpResSrc[i].res.resNot.lpRes;

if (lpRes == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = MAPIAllocateMore(sizeof(SRestriction),
lpObject,
(LPVOID FAR *)
&lpResDest[i].res.resNot.lpRes);
if (FAILED(hr))
goto cleanup;

hr = HrCopyRestrictionArray(lpRes,
lpObject,
1,
lpResDest[i].res.resNot.lpRes);
if (FAILED(hr))
goto cleanup;

break;
}

case RES_CONTENT:
{
if (lpResSrc[i].res.resContent.lpProp == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrDupPropset(1,
lpResSrc[i].res.resContent.lpProp,
lpObject,
&lpResDest[i].res.resContent.lpProp);
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

break;
}

case RES_PROPERTY:
{
if (lpResSrc[i].res.resProperty.lpProp == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrDupPropset(1,
lpResSrc[i].res.resProperty.lpProp,
lpObject,
&lpResDest[i].res.resProperty.lpProp);
if (FAILED(hr))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

break;
}

case RES_COMPAREPROPS:
case RES_BITMASK:
case RES_SIZE:
case RES_EXIST:
{
// Nothing to do.

break;
}

case RES_SUBRESTRICTION:
{
LPSRestrictionlpRes = lpResSrc[i].res.resSub.lpRes;

if (lpRes == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = MAPIAllocateMore(sizeof(SRestriction),
lpObject,
(LPVOID FAR *)
&lpResDest[i].res.resSub.lpRes);
if (FAILED(hr))
goto cleanup;

hr = HrCopyRestrictionArray(lpRes,
lpObject,
1,
lpResDest[i].res.resSub.lpRes);
if (FAILED(hr))
goto cleanup;

break;
}

case RES_COMMENT:
{
LPSRestrictionlpRes = lpResSrc[i].res.resComment.lpRes;

if (lpRes == NULL)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = MAPIAllocateMore(sizeof(SRestriction),
lpObject,
(LPVOID FAR *)
&lpResDest[i].res.resComment.lpRes);
if (FAILED(hr))
goto cleanup;

hr = HrCopyRestrictionArray(lpRes,
lpObject,
1,
lpResDest[i].res.resComment.lpRes);
if (FAILED(hr))
goto cleanup;

break;
}

default:
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}
}

cleanup:

RETURN(hr);
}


// $--HrCreateReplyMsg---------------------------------------------------------
//
// DESCRIPTION:Create a reply message for the reply action.
//
// INPUT:
//
//[lpFolder]-- Ptr to MAPI folder action applies to.
//[lpszReplyText]-- Ptr to message text.
//[lpObject]-- Ptr to an existing MAPI buffer allocated by
// MAPIAllocateBuffer(). NULL is not permitted.
//
// OUTPUT:
//
// [lpcbentryid]-- Number of bytes in message's entry identifier.
// [lppentryid]-- Ptr to message entry identifier.
//
//
// RETURNS: NOERRORif successful;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrCreateReplyMsg(// RETURNS:HRESULT
INLPMAPIFOLDERlpFolder,// ptr to MAPI folder
INLPSTRlpszReplyText,// ptr to message text
INLPVOIDlpObject,// ptr to existing MAPI buf
OUTULONG FAR *lpcbentryid, // ptr to message entry ID size
OUTLPENTRYID FAR *lppentryid// message entry ID buffer ptr
)
{
static
SizedSPropTagArray(1, PropTagArray) ={1, {PR_ENTRYID}};

SPropValueaPropVals[5] ={0};
ULONGcValues = 0;
HRESULThr =NOERROR;
LPMESSAGElpMsg =NULL;
LPSPropProblemArraylpProblems =NULL;
LPSPropValuergPropVals =NULL;
ULONGulOldStatus =0;

DEBUGPRIVATE("HrCreateReplyMsg()\n");

hr = lpFolder->CreateMessage(NULL, MAPI_ASSOCIATED, &lpMsg);

if (FAILED(hr))
goto cleanup;

aPropVals[0].ulPropTag =PR_MESSAGE_CLASS;
aPropVals[0].Value.lpszA ="IPM.Note.Rules.ReplyTemplate.Microsoft";
aPropVals[1].ulPropTag =PR_DELETE_AFTER_SUBMIT;
aPropVals[1].Value.b =FALSE;
aPropVals[2].ulPropTag =PR_ORIGINATOR_DELIVERY_REPORT_REQUESTED;
aPropVals[2].Value.b =FALSE;
aPropVals[3].ulPropTag =PR_MESSAGE_FLAGS;
aPropVals[3].Value.l =MSGFLAG_READ | MSGFLAG_UNSENT;
aPropVals[4].ulPropTag =PR_BODY;
aPropVals[4].Value.lpszA =lpszReplyText;

hr = lpMsg->SetProps(5, aPropVals, &lpProblems);

if (FAILED(hr))
goto cleanup;

if (lpProblems != NULL)
{
hr = HR_LOG(E_FAIL);
MAPIFREEBUFFER(lpProblems);
goto cleanup;
}

hr = lpMsg->SaveChanges(KEEP_OPEN_READWRITE);

if (FAILED(hr))
goto cleanup;

// Now get the entryid for the newly created msg.

hr = lpMsg->GetProps((LPSPropTagArray)&PropTagArray,
0,
&cValues,
&rgPropVals);
if (FAILED(hr))
goto cleanup;

if (cValues != 1||
rgPropVals[0].ulPropTag != PR_ENTRYID)
{
hr = HR_LOG(EDK_E_NOT_FOUND);
goto cleanup;
}

// Allocate space for entryid and store it.

*lpcbentryid = rgPropVals[0].Value.bin.cb;

hr = MAPIAllocateMore(*lpcbentryid,
lpObject,
(LPVOID FAR *)lppentryid);
if (FAILED(hr))
goto cleanup;

memcpy(*lppentryid, rgPropVals[0].Value.bin.lpb, *lpcbentryid);

cleanup:

ULRELEASE(lpMsg);

RETURN(hr);
}


// $--HrGetExplicitTag---------------------------------------------------------
//
// DESCRIPTION:Parse an explicit property tag token, returning results in
//*pti. A value for ulPropTag is returned in pti->dwValue.
//
// INPUT/OUTPUT:
//
//[pti]-- Ptr to token information structure.
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrGetExplicitTag( // RETURNS: HRESULT
IN OUTTOKENINFO *pti// ptr to token information
)
{
HRESULT hr =NOERROR;
CHARchtmp =0;
CHAR *pchDigit =NULL;
CHAR *pch =NULL;
ULONGulPropTag =0;

DEBUGPRIVATE("HrGetExplicitTag()\n");

pch = pti->pch + 1;

if (*pch != 'B' && *pch != 'b'&&
*pch != 'N' && *pch != 'n'&&
*pch != 'S' && *pch != 's'&&
*pch != 'T' && *pch != 't')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pch++;

pch = lpszSkipWhiteSpace(pch);

pchDigit = pch;

if (!isxdigit(*pch))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

while (isxdigit(*pch))
{
pch++;
}

// We temporarily terminate the hex number string, just to be absolutely
// certain that the following character is not used.

chtmp =*pch;
*pch ='\0';

errno = 0; // initialize it

ulPropTag = strtoul(pchDigit, (CHAR **)NULL, 16);

*pch = chtmp;

if (ulPropTag == ULONG_MAX && errno == ERANGE)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pch = lpszSkipWhiteSpace(pch);

if (*pch != ']')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

if (*(pti->pch + 1) == 'B' || *(pti->pch + 1) == 'b')
{
if (PROP_TYPE(ulPropTag) != PT_BOOLEAN)
{
pti->tt = InvalidTag;
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pti->tt = BooleanTag;
}
else if (*(pti->pch + 1) == 'N' || *(pti->pch + 1) == 'n')
{
if (PROP_TYPE(ulPropTag) != PT_LONG)
{
pti->tt = InvalidTag;
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pti->tt = NumericTag;
}
else if (*(pti->pch + 1) == 'S' || *(pti->pch + 1) == 's')
{
if (PROP_TYPE(ulPropTag) != PT_STRING8&&
PROP_TYPE(ulPropTag) != PT_UNICODE)
{
pti->tt = InvalidTag;
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pti->tt = StringTag;
}
else if (*(pti->pch + 1) == 'T' || *(pti->pch + 1) == 't')
{
if (PROP_TYPE(ulPropTag) != PT_SYSTIME)
{
pti->tt = InvalidTag;
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pti->tt = TimeTag;
}
else
{
// We should not have gotten past the preliminary input check, but
// we include this to prevent maintenance headaches.

hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pti->cch =pch - pti->pch + 1;
pti->dwValue =ulPropTag;

cleanup:

RETURN(hr);
}


// $--HrGetExpressionToken-----------------------------------------------------
//
// DESCRIPTION:Parse a token in a condition expression, returning results in
//*pti. The types of token that may be parsed are defined by the
//TOKENTYPE enumeration.
//
// INPUT/OUTPUT:
//
//[pti]-- Ptr to token information structure.
//
// RETURNS: NOERRORif successful;
//E_INVALIDARGif bad input;
//E_*otherwise.
//-----------------------------------------------------------------------------

HRESULT
HrGetExpressionToken( // RETURNS: HRESULT
IN OUTTOKENINFO *pti // token information ptr
)
{
HRESULThr =NOERROR;
CHAR *pch =NULL;

DEBUGPRIVATE("HrGetExpressionToken()\n");

pch = lpszSkipWhiteSpace(pti->pch);

pti->pch =pch;
pti->cch =0;
pti->tt =NotRecognized;
pti->dwValue =0;// Only valid if otherwise assigned below.

switch (*pch)
{
case '&':
pti->cch =1;
pti->tt =AndOp;
break;

case '}':
pti->cch =1;
pti->tt =ContainsOp;
break;

case '\0':
pti->cch =1;
pti->tt =EndOfString;
break;

case '=':
pti->cch =1;
pti->tt =EqualOp;
break;

case '>':
pti->cch =1;
pti->tt =GreaterThanOp;
break;

case '(':
pti->cch =1;
pti->tt =LeftParen;
break;

case '<':
pti->cch =1;
pti->tt =LessThanOp;
break;

case '#':
pti->cch =1;
pti->tt =NotEqualOp;
break;

case '!':
pti->cch =1;
pti->tt =NotOp;
break;

case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
// We try to match a time first; if this fails, we then try to
// just match a number. This order is important; a correct
// time format will always match a number format.

hr = HrGetTimeLiteral(pti);

if (FAILED(hr))
hr = HrGetNumericLiteral(pti);

break;

case '-':
case '+':
hr = HrGetNumericLiteral(pti);
break;

case 'P':
case 'p':
hr = HrGetNamedTag(pti);
break;

case '[':
hr = HrGetExplicitTag(pti);
break;

case '|':
pti->cch =1;
pti->tt =OrOp;
break;

case ')':
pti->cch =1;
pti->tt =RightParen;
break;

case '"':
hr = HrGetStringLiteral(pti);
break;

default:
hr = HR_LOG(E_INVALIDARG);
break;
}

RETURN(hr);
}


// $--HrGetFolderPath----------------------------------------------------------
//
// DESCRIPTION:Given the ENTRYID for a folder and a ptr to its store,
//get its path name.
//
// INPUT:
//
// [lpMDB]-- MAPI store ptr.
// [cbentryid]-- Count of bytes in folder's entry identifier.
// [lpentryid]-- Ptr to folder entry identifier.
//
// OUTPUT:
//
//[lppszFolderPath]-- Ptr to ptr that will be set to point at folder path
// on successful return.
//
// RETURNS: NOERROR if successful;
//E_INVALIDARGif bad input;
//E_OUTOFMEMORYif out of memory;
//EDK_E_NOT_FOUNDif specified store not found;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrGetFolderPath( // RETURNS: HRESULT
INLPMDBlpMDB,// MAPI store ptr
INULONGcbentryid,// count of bytes in folder entry ID
INLPENTRYIDlpentryid,// folder entry ID ptr
OUTLPSTR FAR *lppszFolderPath // folder path ptr ptr
)
{
static
SizedSPropTagArray(3, PropTagArray) ={3, {PR_DISPLAY_NAME,
PR_PARENT_ENTRYID,
PR_FOLDER_TYPE}};
ULONGcValues = 0;
HRESULThr =NOERROR;
LPMAPIFOLDERlpFolder =NULL;
LPSTRlpszParentFolderPath =NULL;
LPSPropValuergPropVals =NULL;
ULONGulObjType =0;

DEBUGPRIVATE("HrGetFolderPath()\n");

// Get the folder object;

hr = lpMDB->OpenEntry(cbentryid,
lpentryid,
NULL,
MAPI_DEFERRED_ERRORS,
&ulObjType,
(LPUNKNOWN FAR *)&lpFolder);
if (FAILED(hr))
goto cleanup;

if (ulObjType != MAPI_FOLDER)
{
hr = HR_LOG(EDK_E_NOT_FOUND);
goto cleanup;
}

// Get the needed properties.

hr = lpFolder->GetProps((LPSPropTagArray)&PropTagArray,
0,
&cValues,
&rgPropVals);
if (FAILED(hr))
goto cleanup;

if (cValues != 3||
rgPropVals[0].ulPropTag != PR_DISPLAY_NAME||
rgPropVals[1].ulPropTag != PR_PARENT_ENTRYID||
rgPropVals[2].ulPropTag != PR_FOLDER_TYPE)
{
hr = HR_LOG(EDK_E_NOT_FOUND);
goto cleanup;
}

if (rgPropVals[2].Value.l == FOLDER_ROOT)
{
// We protect ourselves from various possible implementations of the
// root folder name by covering all possibilities.

if (rgPropVals[0].Value.lpszA != NULL)
{
hr = MAPIAllocateBuffer(strlen(rgPropVals[0].Value.lpszA) + 1,
(LPVOID FAR *)lppszFolderPath);
if (FAILED(hr))
goto cleanup;

strcpy(*lppszFolderPath, rgPropVals[0].Value.lpszA);
}
else
{
hr = MAPIAllocateBuffer(1, (LPVOID FAR *)lppszFolderPath);

if (FAILED(hr))
goto cleanup;

*lppszFolderPath[0] = '\0';
}
}
else
{
hr = HrGetFolderPath(lpMDB,
rgPropVals[1].Value.bin.cb,
(LPENTRYID)rgPropVals[1].Value.bin.lpb,
&lpszParentFolderPath);
if (FAILED(hr))
goto cleanup;

hr = MAPIAllocateBuffer(strlen(lpszParentFolderPath) +
strlen(rgPropVals[0].Value.lpszA) + 2,
(LPVOID FAR *)lppszFolderPath);
if (FAILED(hr))
goto cleanup;

strcpy(*lppszFolderPath, lpszParentFolderPath);

if (*lppszFolderPath[0] != '\0')
strcat(*lppszFolderPath, "\\");

strcat(*lppszFolderPath, rgPropVals[0].Value.lpszA);
}

cleanup:

MAPIFREEBUFFER(lpszParentFolderPath);
MAPIFREEBUFFER(rgPropVals);
ULRELEASE(lpFolder);

RETURN(hr);
}


//$--HrGetMoveCopyArgs---------------------------------------------------------
//
// DESCRIPTION:Parse the parenthetical expression following a move or copy
//action argument. The results are stored in the ACTION struct
//passed in as an argument.
//
// INPUT:
//
//[lpSession]-- Ptr to MAPI session.
//[pszFolder]-- Target folder path for move or copy.
//[lpObject]-- Ptr to an existing MAPI buffer allocated by
// MAPIAllocateBuffer(). NULL is not permitted!
//
// INPUT/OUTPUT:
//
//[lpAction]-- Action structure to set up.
//
// RETURNS: NOERRORif successful;
//E_INVALIDARGif bad input;
//E_*otherwise.
//-----------------------------------------------------------------------------


HRESULT
HrGetMoveCopyArgs(// RETURNS: HRESULT
INLPMAPISESSIONlpSession,// ptr to MAPI session
INLPSTRpszFolder,// target folder path
INLPVOIDlpObject,// ptr to existing MAPI buffer
IN OUTLPACTIONlpAction// ACTION structure to set up
)
{
ULONGcbEIDActsFolder =0;
ULONGcbEIDActsStore =0;
HRESULT hr =NOERROR;
LPMDBlpActsStore =NULL;
LPENTRYIDlpEIDActsFolder =NULL;
LPENTRYIDlpEIDActsStore =NULL;
LPENTRYIDlpEIDTmp =NULL;
CHAR *pch =NULL;
CHARszActsFolder[MAX_PATH + 1];
CHARszActsStore[MAX_PATH + 1];

DEBUGPRIVATE("HrGetMoveCopyArgs()\n");

pch = strchr(pszFolder, '\\');

if (pch == NULL||
pch - pszFolder == 0||
pch - pszFolder + 1 > sizeof(szActsStore))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

memcpy(szActsStore, pszFolder, pch - pszFolder);

szActsStore[pch - pszFolder] = '\0';

pch++;

if (*pch == '\0'||
strlen(pch) + 1 > sizeof(szActsFolder))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

strcpy(szActsFolder, pch);

hr = HrMAPIFindStore(lpSession, szActsStore, &cbEIDActsStore, &lpEIDTmp);

if (FAILED(hr))
goto cleanup;

hr = MAPIAllocateMore(cbEIDActsStore,
lpObject,
(LPVOID FAR *)&lpEIDActsStore);
if (FAILED(hr))
goto cleanup;

memcpy(lpEIDActsStore, lpEIDTmp, cbEIDActsStore);

MAPIFREEBUFFER(lpEIDTmp);

hr = lpSession->OpenMsgStore(0,
cbEIDActsStore,
lpEIDActsStore,
NULL,
MAPI_DEFERRED_ERRORS,
&lpActsStore);
if (FAILED(hr))
goto cleanup;

hr = HrMAPIFindFolderEx(lpActsStore,
'\\',
szActsFolder,
&cbEIDActsFolder,
&lpEIDTmp);
if (FAILED(hr))
goto cleanup;

hr = MAPIAllocateMore(cbEIDActsFolder,
lpObject,
(LPVOID FAR *)&lpEIDActsFolder);
if (FAILED(hr))
goto cleanup;

memcpy(lpEIDActsFolder, lpEIDTmp, cbEIDActsFolder);

MAPIFREEBUFFER(lpEIDTmp);

lpAction->actMoveCopy.cbStoreEntryId =cbEIDActsStore;
lpAction->actMoveCopy.lpStoreEntryId =lpEIDActsStore;
lpAction->actMoveCopy.cbFldEntryId =cbEIDActsFolder;
lpAction->actMoveCopy.lpFldEntryId =lpEIDActsFolder;

cleanup:

ULRELEASE(lpActsStore);

RETURN(hr);
}


// $--HrGetNamedTag------------------------------------------------------------
//
// DESCRIPTION:Parse a named property tag token (PR_*), returning results in
//*pti. A value for ulPropTag is returned in pti->dwValue.
//
// INPUT/OUTPUT:
//
//[pti]-- Ptr to token information structure.
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrGetNamedTag( // RETURNS: HRESULT
IN OUTTOKENINFO *pti // ptr to token information
)
{
HRESULThr =NOERROR;
CHARchtmp =0;
CHAR *pch =NULL;
ULONGulPropTag =0;

DEBUGPRIVATE("HrGetNamedTag()\n");

pch = pti->pch + 1;

while (isalnum(*pch) || *pch == '_')
pch++;

// Terminate the putative PR_ string so we can look it up in the PropTbl.

chtmp =*pch;
*pch ='\0';

hr = HrGetPropTagValue(pti->pch, &ulPropTag);

*pch = chtmp;

if (FAILED(hr))
goto cleanup;

if (PROP_TYPE(ulPropTag) == PT_BOOLEAN)
{
pti->tt = BooleanTag;
}
else if (PROP_TYPE(ulPropTag) == PT_LONG)
{
pti->tt = NumericTag;
}
else if (PROP_TYPE(ulPropTag) == PT_STRING8||
PROP_TYPE(ulPropTag) == PT_UNICODE)
{
pti->tt = StringTag;
}
else if (PROP_TYPE(ulPropTag) == PT_SYSTIME)
{
pti->tt = TimeTag;
}
else
{
pti->tt = InvalidTag;

hr = HR_LOG(E_INVALIDARG);
}

pti->cch =pch - pti->pch;
pti->dwValue =ulPropTag;

cleanup:

RETURN(hr);
}


// $--HrGetNumericLiteral------------------------------------------------------
//
// DESCRIPTION:Parse a numeric literal token, returning results in
//*pti. A value for the numeric literal is returned in
//pti->dwValue.
//
// INPUT/OUTPUT:
//
//[pti]-- Ptr to token information structure.
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrGetNumericLiteral( // RETURNS: HRESULT
IN OUTTOKENINFO *pti// ptr to token info structure
)
{
HRESULThr = NOERROR;
CHARchtmp =0;
LONGlValue =0;
CHAR *pch =NULL;
ULONGulValue = 0;

DEBUGPRIVATE("HrGetNumericLiteral()\n");

pch = pti->pch + 1;

while (isdigit(*pch))
pch++;

// If there is only 1 CHAR, it better be a digit rather than a sign.

if (pch == pti->pch + 1&& !isdigit(*(pti->pch)))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

// We temporarily terminate the number string, just to be absolutely
// certain that the following character is not used.

chtmp =*pch;
*pch ='\0';

errno = 0; // initialize it

// Use strtol() if the number is negative; otherwise use strtoul().

if (*(pti->pch) == '-')
{
lValue = strtol(pti->pch, (CHAR **)NULL, 10);

*pch = chtmp;

if ((lValue == LONG_MAX || lValue == LONG_MIN) && errno == ERANGE)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pti->dwValue = lValue;
}
else
{
ulValue = strtoul(pti->pch, (CHAR **)NULL, 10);

*pch = chtmp;

if (ulValue == ULONG_MAX && errno == ERANGE)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pti->dwValue =ulValue;
}

pti->cch =pch - pti->pch;
pti->tt =NumericLiteral;

cleanup:

RETURN(hr);
}


// $--HrGetStoreName-----------------------------------------------------------
//
// DESCRIPTION:Given a ptr to a store, get its display name.
//
// INPUT:
//
// [lpMDB]-- MAPI store ptr.
//
// OUTPUT:
//
//[lppszName]-- Ptr to ptr that will be set to point at store display
// name on successful return.
//
// RETURNS: NOERROR if successful;
//E_INVALIDARGif bad input;
//E_OUTOFMEMORYif out of memory;
//EDK_E_NOT_FOUNDif specified store not found;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrGetStoreName( // RETURNS: HRESULT
INLPMDBlpMDB,// MAPI store ptr
OUTLPSTR FAR *lppszName // store name ptr ptr
)
{
static
SizedSPropTagArray(1, PropTagArray) ={1, {PR_DISPLAY_NAME}};

ULONGcValues = 0;
HRESULThr =NOERROR;
LPSPropValuelpProp = NULL;

DEBUGPRIVATE("HrGetStoreName()\n");

// Get the display name property.

hr = lpMDB->GetProps((LPSPropTagArray)&PropTagArray, 0, &cValues, &lpProp);

if (FAILED(hr))
goto cleanup;

if (cValues != 1 || lpProp->ulPropTag != PR_DISPLAY_NAME)
{
hr = HR_LOG(EDK_E_NOT_FOUND);
goto cleanup;
}

hr = MAPIAllocateBuffer(strlen(lpProp->Value.lpszA) + 1,
(LPVOID FAR *)lppszName);
if (FAILED(hr))
goto cleanup;

strcpy(*lppszName, lpProp->Value.lpszA);

cleanup:

MAPIFREEBUFFER(lpProp);

RETURN(hr);
}


// $--HrGetStringLiteral-------------------------------------------------------
//
// DESCRIPTION:Parse a string literal token, returning results in
//*pti. The user should allocate separate storage to save the
//string literal if it is to be converted to a MAPI property
//value.
// INPUT/OUTPUT:
//
//[pti]-- Ptr to token information structure.
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//
// Notes:pti->pch is left pointing at the opening quote and the value
//returned for pti->cch includes the leading and trailing quotes
//(ie. pti->cch >= 2).
//-----------------------------------------------------------------------------

HRESULT
HrGetStringLiteral(// RETURNS: HRESULT
IN OUTTOKENINFO *pti // ptr to token information
)
{
HRESULT hr =NOERROR;
CHAR *pch = NULL;

DEBUGPRIVATE("HrGetStringLiteral()\n");

pch = pti->pch + 1;

while (*pch != '"' && *pch != '\0')
pch++;

if (*pch != '"')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pti->cch =pch - pti->pch + 1;
pti->tt =StringLiteral;

cleanup:

RETURN(hr);
}


// $--HrGetTimeLiteral---------------------------------------------------------
//
// DESCRIPTION:Parse a time literal token, returning results in *pti.
//A value for the time literal is returned in pti->ftValue.
//
// INPUT/OUTPUT:
//
//[pti]-- Ptr to token information structure.
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrGetTimeLiteral( // RETURNS: HRESULT
IN OUTTOKENINFO *pti// ptr to token info structure
)
{
static CHARDateTemplate[] ="dddd-dd-dd";

CHARchtmp =0;
FILETIMEftLocal ={0};
LONGHour =0;
HRESULThr = NOERROR;
INTi =0;
LONGMinute =0;
LONGSecond =0;
CHAR *pch =pti->pch;
SYSTEMTIMEst ={0};


DEBUGPRIVATE("HrGetTimeLiteral()\n");

if (!isdigit(*pch)||
!isdigit(*(pch + 1))||
*(pch + 2) != ':')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

Hour = atol(pch);

if (Hour > 23)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

st.wHour = (WORD)Hour;

pch += 3;

if (!isdigit(*pch)||
!isdigit(*(pch + 1))||
(*(pch + 2) != ':' && !isspace(*(pch + 2))))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

Minute = atol(pch);

if (Minute > 59)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

st.wMinute = (WORD)Minute;

if (*(pch + 2) == ':')
{
pch += 3;

if (!isdigit(*pch)||
!isdigit(*(pch + 1))||
!isspace(*(pch + 2)))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

Second = atol(pch);

if (Second > 59)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

st.wSecond = (WORD)Second;
}

pch += 2;

pch = lpszSkipWhiteSpace(pch);

for (i = 0; i < sizeof(DateTemplate) - 1; i++)
{
if (DateTemplate[i] == 'd')
{
if (!isdigit(*(pch + i)))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
}
else
{
if (DateTemplate[i] != *(pch + i))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
}
}

// We let SystemTimeToFileTime() worry about range checking the date values.

st.wYear =(WORD)atol(pch);
st.wMonth =(WORD)atol(pch + 5);

// We terminate the day value just in case (insures correct day
// interpretation even if the rest of the expression is not well formed).

chtmp = *(pch + sizeof(DateTemplate) - 1);
*(pch + sizeof(DateTemplate) - 1) = '\0';

st.wDay =(WORD)atol(pch + 8);

*(pch + sizeof(DateTemplate) - 1) = chtmp;

pch += (sizeof(DateTemplate) - 1);

// Convert SYSTEMTIME to FILETIME.

if (!SystemTimeToFileTime(&st, &ftLocal))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

// We now presumably have a local FILETIME. Convert to UTC.

if (!LocalFileTimeToFileTime(&ftLocal, &pti->ftValue))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

pti->cch =pch - pti->pch;
pti->tt =TimeLiteral;

cleanup:

RETURN(hr);
}


// $--HrNotRestrictionToString-------------------------------------------------
//
// DESCRIPTION:Get a string representation of an SNotRestriction.
//
// INPUT:
//
//[lpRes]-- Ptr to not restriction to be represented as a string.
//
// INPUT/OUTPUT:
//
//[lppszString]-- Output string ptr ptr. If *lppszString == NULL, then
// a string will be allocated by this routine.
//[lpcbString]-- Ptr to count of bytes in input string on input (including
// terminating '\0'); count of bytes in string after
// appending formatted string on output.
//[lpcbBuffer]-- Ptr to count of bytes in input string buffer on input;
// count of bytes in string buffer after appending formatted
// string on output (buffer size may increase if allocation
// was necessary).
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrNotRestrictionToString(// RETURNS: HRESULT
INLPSRestrictionlpRes,// restriction ptr
IN OUTLPSTR FAR *lppszString,// output string ptr ptr
IN OUTLPULONGlpcbString,// ptr to count of bytes in string
IN OUTLPULONGlpcbBuffer// ptr to count of bytes in buffer
)
{
HRESULThr =NOERROR;
SNotRestriction *presNot=NULL;

DEBUGPRIVATE("HrNotRestrictionToString()\n");

presNot=&lpRes->res.resNot;

hr = HrPrintToString(lppszString, lpcbString, lpcbBuffer, "!");

if (FAILED(hr))
goto cleanup;

if (ISNONTERMINALRESTRICTION(presNot->lpRes))
{
hr = HrPrintToString(lppszString, lpcbString, lpcbBuffer, "(");

if (FAILED(hr))
goto cleanup;

hr = HrPrivateRestrictionToString(presNot->lpRes,
lppszString,
lpcbString,
lpcbBuffer);
if (FAILED(hr))
goto cleanup;

hr = HrPrintToString(lppszString, lpcbString, lpcbBuffer, ")");

if (FAILED(hr))
goto cleanup;
}
else
{
hr = HrPrivateRestrictionToString(presNot->lpRes,
lppszString,
lpcbString,
lpcbBuffer);
if (FAILED(hr))
goto cleanup;
}

cleanup:

RETURN(hr);
}


// $--HrOrRestrictionToString--------------------------------------------------
//
// DESCRIPTION:Get a string representation of an SOrRestriction.
//
// INPUT:
//
//[lpRes]-- Ptr to or restriction to be represented as a string.
//
// INPUT/OUTPUT:
//
//[lppszString]-- Output string ptr ptr. If *lppszString == NULL, then
// a string will be allocated by this routine.
//[lpcbString]-- Ptr to count of bytes in input string on input (including
// terminating '\0'); count of bytes in string after
// appending formatted string on output.
//[lpcbBuffer]-- Ptr to count of bytes in input string buffer on input;
// count of bytes in string buffer after appending formatted
// string on output (buffer size may increase if allocation
// was necessary).
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrOrRestrictionToString(// RETURNS: HRESULT
INLPSRestrictionlpRes,// restriction ptr
IN OUTLPSTR FAR *lppszString,// output string ptr ptr
IN OUTLPULONGlpcbString,// ptr to count of bytes in string
IN OUTLPULONGlpcbBuffer// ptr to count of bytes in buffer
)
{
HRESULThr = NOERROR;
ULONGi =0;
SOrRestriction *presOr =NULL;

DEBUGPRIVATE("HrOrRestrictionToString()\n");

presOr = &lpRes->res.resOr;

if (presOr->cRes < 2)
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<Invalid SOrRestriction>");
if (FAILED(hr))
goto cleanup;
}
else
{
hr = HrPrivateRestrictionToString(presOr->lpRes,
lppszString,
lpcbString,
lpcbBuffer);
if (FAILED(hr))
goto cleanup;

for (i = 1; i < presOr->cRes; i++)
{
hr = HrPrintToString(lppszString, lpcbString, lpcbBuffer, " | ");

if (FAILED(hr))
goto cleanup;

if (ISNONTERMINALRESTRICTION(presOr->lpRes + i))
{
hr = HrPrintToString(lppszString, lpcbString, lpcbBuffer, "(");

if (FAILED(hr))
goto cleanup;

hr = HrPrivateRestrictionToString(presOr->lpRes + i,
lppszString,
lpcbString,
lpcbBuffer);
if (FAILED(hr))
goto cleanup;

hr = HrPrintToString(lppszString, lpcbString, lpcbBuffer, ")");

if (FAILED(hr))
goto cleanup;
}
else
{
hr = HrPrivateRestrictionToString(presOr->lpRes + i,
lppszString,
lpcbString,
lpcbBuffer);
if (FAILED(hr))
goto cleanup;
}
}
}

cleanup:

RETURN(hr);
}


// $--HrParseAction------------------------------------------------------------
//
// DESCRIPTION:Parse an action string.
//
// INPUT:
//
//[lpSession]-- Ptr to MAPI session.
//
//[lpFolder]-- Ptr to MAPI folder action applies to.
//
//[lpObject]-- Ptr to an existing MAPI buffer allocated by
// MAPIAllocateBuffer(). NULL is not permitted!
//
//[pch]-- Ptr to string to be parsed.
//
// INPUT/OUTPUT:
//
//[lpAction]-- Ptr to uninitialized ACTION structure on input;
// It will be appropriately initialized on output.
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_OUTOFMEMORYif out of memory;
//E_UNEXPECTEDif there is an internal error;
// E_*otherwise.
//-----------------------------------------------------------------------------

HRESULT
HrParseAction(// RETURNS: HRESULT
INLPMAPISESSIONlpSession,// ptr to MAPI session
INLPMAPIFOLDERlpFolder,// ptr to MAPI folder
INLPVOIDlpObject,// ptr to existing MAPI buffer
INCHAR *pch, // string ptr
IN OUTLPACTIONlpAction // action ptr
)
{
static CHARszBounceKeyword[] ="bounce";
static CHARszCopyKeyword[] ="copy";
static CHARszDelegateKeyword[] ="delegate";
static CHARszDeleteKeyword[] ="delete";
static CHARszForwardKeyword[] ="forward";
static CHARszMarkReadKeyword[] ="markread";
static CHARszMoveKeyword[] ="move";
static CHARszMsgIdKeyword[] ="msgid";
static CHARszReplyKeyword[] ="reply";
static CHARszTagKeyword[] ="tag";

CHARchtmp =0;
HRESULThr =NOERROR;
LPADRBOOKlpAdrBook =NULL;
LPADRLISTlpAdrList =NULL;
CHAR *pchEnd =pch;

// Set all fields in ACTION struct to 0.

memset(lpAction, 0, sizeof(*lpAction));

// Find the start of the action keyword.

pch = lpszSkipWhiteSpace(pch);

pchEnd = lpszSkipNonWhiteSpace(pch);

// Terminate the action keyword so we can parse it.

chtmp =*pchEnd;
*pchEnd ='\0';

// Identify the action keyword.

if (!stricmp(pch, szBounceKeyword))
{
lpAction->acttype = OP_BOUNCE;
}
else if (!stricmp(pch, szCopyKeyword))
{
lpAction->acttype = OP_COPY;
}
else if (!stricmp(pch, szDelegateKeyword))
{
lpAction->acttype = OP_DELEGATE;
}
else if (!stricmp(pch, szDeleteKeyword))
{
lpAction->acttype = OP_DELETE;
}
else if (!stricmp(pch, szForwardKeyword))
{
lpAction->acttype = OP_FORWARD;
}
else if (!stricmp(pch, szMarkReadKeyword))
{
lpAction->acttype = OP_MARK_AS_READ;
}
else if (!stricmp(pch, szMoveKeyword))
{
lpAction->acttype = OP_MOVE;
}
else if (!stricmp(pch, szReplyKeyword))
{
lpAction->acttype = OP_REPLY;
}
else if (!stricmp(pch, szTagKeyword))
{
lpAction->acttype = OP_TAG;
}
else
{
*pchEnd = chtmp;
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

*pchEnd = chtmp;

pch = lpszSkipWhiteSpace(pchEnd);

switch (lpAction->acttype)
{
case OP_BOUNCE:
{
pchEnd = lpszSkipNonWhiteSpace(pch);

// Terminate the bounce code so we can parse it.

chtmp =*pchEnd;
*pchEnd ='\0';

if (!strcmp(pch, "BOUNCE_MESSAGE_SIZE_TOO_LARGE"))
{
lpAction->scBounceCode = BOUNCE_MESSAGE_SIZE_TOO_LARGE;
}
else if (!strcmp(pch, "BOUNCE_FORMS_MISMATCH"))
{
lpAction->scBounceCode = BOUNCE_FORMS_MISMATCH;
}
else if (!strcmp(pch, "BOUNCE_ACCESS_DENIED"))
{
lpAction->scBounceCode = BOUNCE_ACCESS_DENIED;
}
else
{
*pchEnd = chtmp;
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

*pchEnd = chtmp;

pch = lpszSkipWhiteSpace(pchEnd);

if (*pch != '\0')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

break;
}

case OP_COPY:
case OP_MOVE:
{
hr = HrGetMoveCopyArgs(lpSession, pch, lpObject, lpAction);

if (FAILED(hr))
goto cleanup;

break;
}

case OP_DELEGATE:
case OP_FORWARD:
{
ULONGcRecips =0;
ULONGcQuotes =0;
CHAR *pQuote =pch;

// Count quotation mark pairs to arrive at a count of recipients.
// This is more reliable than counting comma's since a comma may
// be embedded in a recipient name.

while ((pQuote = strchr(pQuote, '"')) != NULL)
{
cQuotes++;
pQuote++;
}

if (cQuotes == 0 || (cQuotes & 1) != 0)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

cRecips = cQuotes >> 1;

hr = MAPIAllocateBuffer(CbNewADRLIST(cRecips), (LPVOID *)&lpAdrList);

if (FAILED(hr))
goto cleanup;

{
ULONGi = 0;
LPADRENTRYlpAdrEntry = NULL;
LPSTRlpszName = NULL;

lpAdrList->cEntries = cRecips;

// Init the ADRENTRY's prior to actually allocating the SPropValue
// arrays. This makes cleanup possible if there is an error.

for (i = 0; i < cRecips; i++)
{
lpAdrList->aEntries[i].ulReserved1 =0;
lpAdrList->aEntries[i].cValues =1;
lpAdrList->aEntries[i].rgPropVals =NULL;
}

// Now allocate and initialize the SPropValue arrays.

for (i = 0; i < cRecips; i++)
{
hr = MAPIAllocateBuffer(sizeof(SPropValue),
(LPVOID *)&lpAdrList->aEntries[i].rgPropVals);

if (FAILED(hr))
goto cleanup;

lpAdrList->aEntries[i].rgPropVals->ulPropTag = PR_DISPLAY_NAME;
lpAdrList->aEntries[i].rgPropVals->Value.lpszA = NULL;
}

lpAdrEntry = lpAdrList->aEntries;

// Parse the recipient names.

// Note that the index used below is 1-based (ie., don't use it
// for a 0-based array!).

for (i = 1; i <= cRecips; i++)
{
if (*pch != '"')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pch++;

lpszName = pch;

while (*pch != '"' && *pch != '\0')
pch++;

if (*pch == '\0')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

*pch = '\0';

hr = MAPIAllocateMore(strlen(lpszName) + 1,
lpAdrEntry->rgPropVals,
(LPVOID *)&lpAdrEntry->
rgPropVals[0].Value.lpszA);
if (FAILED(hr))
goto cleanup;

strcpy(lpAdrEntry->rgPropVals[0].Value.lpszA, lpszName);

lpAdrEntry++;

*pch = '"';

pch++;

pch = lpszSkipWhiteSpace(pch);

if (i != cRecips)
{
if (*pch != ',')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pch++;

pch = lpszSkipWhiteSpace(pch);
}
else
{
if (*pch != '\0')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
}
}

hr = lpSession->OpenAddressBook(0, NULL, AB_NO_DIALOG, &lpAdrBook);

if (FAILED(hr))
goto cleanup;

hr = lpAdrBook->ResolveName(0, 0, NULL, lpAdrList);

if (FAILED(hr))
goto cleanup;

// Now look for problems reported in the returned ADRLIST.

for (i = 0; i < cRecips; i++)
{
ULONGj;

lpAdrEntry = &lpAdrList->aEntries[i];

for (j = 0; j < lpAdrEntry->cValues; j++)
{
LPSPropValuepPropVal;

pPropVal = &lpAdrEntry->rgPropVals[j];

if (PROP_TYPE(pPropVal->ulPropTag == PT_ERROR))
{
hr = pPropVal->Value.err;

break;
}
}

if (FAILED(hr))
goto cleanup;
}

// Now reallocate the ADRLIST using linked allocation; we did
// not do this at first to avoid problems in ResolveName().

lpAction->lpadrlist = NULL;

hr = MAPIAllocateMore(CbNewADRLIST(cRecips),
lpObject,
(LPVOID *)&lpAction->lpadrlist);
if (FAILED(hr))
goto cleanup;

memcpy(lpAction->lpadrlist, lpAdrList, CbNewADRLIST(cRecips));

for (i = 0; i < cRecips; i++)
{
hr = HrDupPropset(lpAdrList->aEntries[i].cValues,
lpAdrList->aEntries[i].rgPropVals,
lpObject,
&lpAction->lpadrlist->aEntries[i].rgPropVals);

if (FAILED(hr))
goto cleanup;
}
}

break;
}

case OP_DELETE:
case OP_MARK_AS_READ:
{
if (*pch != '\0')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

break;
}

case OP_REPLY:
{
if (*pch == '"')
{
pch++;

pchEnd = pch;

while (*pchEnd != '"' && *pchEnd != '\0')
pchEnd++;

if (*pchEnd == '\0')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

*pchEnd = '\0';

// Make a message from *pch.

hr = HrCreateReplyMsg(lpFolder,
pch,
lpObject,
&lpAction->actReply.cbEntryId,
&lpAction->actReply.lpEntryId);
if (FAILED(hr))
goto cleanup;

*pchEnd = '"';

pchEnd++;
}
else
{
ULONGcNibbles =0;

pchEnd = lpszSkipNonWhiteSpace(pch);

// Terminate the keyword so we can parse it.

chtmp =*pchEnd;
*pchEnd ='\0';

if (stricmp(pch, szMsgIdKeyword))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

*pchEnd = chtmp;

pch = lpszSkipWhiteSpace(pchEnd);

if (*pch == '0' && (*(pch + 1) == 'x' || *(pch + 1) == 'X'))
pch += 2;

pchEnd = pch;

while (isxdigit(*pchEnd))
pchEnd++;

cNibbles = pchEnd - pch;

if (cNibbles == 0 || (cNibbles & 1) != 0)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

lpAction->actReply.cbEntryId = cNibbles >> 1;

// Allocate space for entryid.

hr = MAPIAllocateMore(lpAction->actReply.cbEntryId,
lpObject,
(LPVOID FAR *)&lpAction->actReply.lpEntryId);
if (FAILED(hr))
goto cleanup;

// Determine it's value and store it.

BYTEloNibble =0;
BYTEhiNibble =0;
BYTE *pByte =(BYTE *)lpAction->actReply.lpEntryId;

while (pch < pchEnd)
{
if (*pch >= '0' && *pch <= '9')
hiNibble = *pch - '0';
else if (*pch >= 'a' && *pch <= 'f')
hiNibble = *pch - ('a' - 0xa);
else
hiNibble = *pch - ('A' - 0xa);

pch++;

if (*pch >= '0' && *pch <= '9')
loNibble = *pch - '0';
else if (*pch >= 'a' && *pch <= 'f')
loNibble = *pch - ('a' - 0xa);
else
loNibble = *pch - ('A' - 0xa);

pch++;

*pByte = (hiNibble << 4) + loNibble;

pByte++;
}
}

// Finish parsing line.

pch = lpszSkipWhiteSpace(pchEnd);

if (*pch != '\0')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

break;
}

case OP_TAG:
{
TOKENINFOti ={pch, 0, NotRecognized, 0};
ULONGpt =PT_UNSPECIFIED;

if (*pch == 'P' || *pch == 'p')
hr = HrGetNamedTag(&ti);
else if (*pch == '[')
hr = HrGetExplicitTag(&ti);
else
hr = HR_LOG(E_INVALIDARG);

if (FAILED(hr))
goto cleanup;

pch = ti.pch + ti.cch;

pt = PROP_TYPE(ti.dwValue);

pch = lpszSkipWhiteSpace(pch);


if (*pch != ',')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

pch++;// Skip over the comma.

pch = lpszSkipWhiteSpace(pch);

lpAction->propTag.ulPropTag = ti.dwValue;

ti.pch = pch;
ti.cch = 0;
ti.tt =NotRecognized;
ti.dwValue =0;

if (pt == PT_BOOLEAN)
{
hr = HrGetExpressionToken(&ti);

if (FAILED(hr))
goto cleanup;

pch = ti.pch + ti.cch;

// A boolean literal is essentially a numeric literal with
// a restricted range, so...

if (ti.tt != NumericLiteral|| ti.dwValue > 1)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

lpAction->propTag.Value.b = (USHORT)ti.dwValue;
}
else if (pt == PT_LONG)
{
hr = HrGetExpressionToken(&ti);

if (FAILED(hr))
goto cleanup;

pch = ti.pch + ti.cch;

if (ti.tt != NumericLiteral)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

lpAction->propTag.Value.l = ti.dwValue;
}
else if (pt == PT_STRING8)
{
hr = HrGetExpressionToken(&ti);

if (FAILED(hr))
goto cleanup;

pch = ti.pch + ti.cch;

if (ti.tt != StringLiteral)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

hr = MAPIAllocateMore(ti.cch + 1 - 2, lpObject,
(LPVOID FAR *)&lpAction->propTag.Value.lpszA);

if (FAILED(hr))
goto cleanup;

memcpy(lpAction->propTag.Value.lpszA, ti.pch + 1, ti.cch - 2);

*(lpAction->propTag.Value.lpszA + ti.cch - 2) = '\0';
}
else if (pt == PT_SYSTIME)
{
hr = HrGetExpressionToken(&ti);

if (FAILED(hr))
goto cleanup;

pch = ti.pch + ti.cch;

if (ti.tt != TimeLiteral)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

lpAction->propTag.Value.ft = ti.ftValue;
}
else if (pt == PT_UNICODE)
{
INTcch = 0;

hr = HrGetExpressionToken(&ti);

if (FAILED(hr))
goto cleanup;

pch = ti.pch + ti.cch;

if (ti.tt != StringLiteral)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

if (ti.cch > 2)
{
cch = MultiByteToWideChar(CP_ACP,
0,
ti.pch + 1,
ti.cch - 2,
NULL,
0);

if (cch == 0)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
}

hr = MAPIAllocateMore((cch + 1) * sizeof(WCHAR), lpObject,
(LPVOID FAR *)&lpAction->propTag.Value.lpszW);

if (FAILED(hr))
goto cleanup;

if (ti.cch > 2)
{
if (!MultiByteToWideChar(CP_ACP,
0,
ti.pch + 1,
ti.cch - 2,
lpAction->propTag.Value.lpszW,
cch))
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
}

*(lpAction->propTag.Value.lpszW + cch) = 0;
}
else
{
hr = HR_LOG(E_UNEXPECTED);// There is an internal error.
goto cleanup;
}

pch = lpszSkipWhiteSpace(pch);

if (*pch != '\0')
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

break;
}

default:
{
hr = HR_LOG(E_UNEXPECTED);// There is an internal error.
goto cleanup;

break;
}
}

cleanup:

ULRELEASE(lpAdrBook);
FREEPADRLIST(lpAdrList);

return hr;
}



// $--HrParseCondition---------------------------------------------------------
//
// DESCRIPTION:Parse a restriction condition expression or parenthetical
//subexpression.
//
// INPUT:
//
//[lpObject]-- Ptr to an existing MAPI buffer allocated by
// MAPIAllocateBuffer(). NULL is not permitted!
//
// INPUT/OUTPUT:
//
//[ppch]-- Ptr to ptr to string to be parsed on input;
// ptr to ptr to first unparsed CHAR on output.
//
//[lpRes]-- Ptr to uninitialized SRestriction structure on input;
// It will be appropriately initialized and may include
// other allocated SRestrictions on output.
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
//E_OUTOFMEMORYif out of memory;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrParseCondition(// RETURNS: HRESULT
INLPVOIDlpObject,// ptr to existing MAPI buffer
IN OUTCHAR **ppch, // string
IN OUTLPSRestrictionlpRes // restriction ptr
)
{
HRESULThr =NOERROR;
LPSRestrictionlpNxtRes =lpRes;
LPSPropValuelpProp = NULL;
CHAR *pch =NULL;
TOKENINFOti ={0};

DEBUGPRIVATE("HrParseCondition()\n");

pch = *ppch;

while (TRUE)
{
ti.pch = pch;

hr = HrGetExpressionToken(&ti);

if (FAILED(hr))
goto cleanup;

pch = ti.pch + ti.cch;// Now pch points to next token.

switch (ti.tt)
{
case LeftParen:
{
hr = HrParseCondition(lpObject, &pch, lpNxtRes);

if (FAILED(hr))
goto cleanup;

ti.pch = pch;

hr = HrGetExpressionToken(&ti);

if (FAILED(hr))
goto cleanup;

pch = ti.pch + ti.cch;

if (ti.tt != RightParen)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

break;
}

case NotOp:
{
lpNxtRes->rt = RES_NOT;

lpNxtRes->res.resNot.ulReserved =0;
lpNxtRes->res.resNot.lpRes =NULL;

hr = MAPIAllocateMore(sizeof(SRestriction),
lpObject,
(LPVOID FAR *)
&lpNxtRes->res.resNot.lpRes);

if (FAILED(hr))
goto cleanup;

lpNxtRes->res.resNot.lpRes->rt = RES_UNINITIALIZED;

lpNxtRes = lpNxtRes->res.resNot.lpRes;

continue;
}

case BooleanTag:
{
TOKENINFOtiBooleanTag =ti;
TOKENINFOtiBooleanOp;
TOKENINFOtiBooleanLiteral;

tiBooleanOp.pch = pch;

hr = HrGetExpressionToken(&tiBooleanOp);

if (FAILED(hr))
goto cleanup;

pch = tiBooleanOp.pch + tiBooleanOp.cch;

tiBooleanLiteral.pch = pch;

hr = HrGetExpressionToken(&tiBooleanLiteral);

if (FAILED(hr))
goto cleanup;

pch = tiBooleanLiteral.pch + tiBooleanLiteral.cch;

// A boolean literal is essentially a numeric literal with
// a restricted range, so...

if (tiBooleanLiteral.tt != NumericLiteral||
tiBooleanLiteral.dwValue > 1)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

if (tiBooleanOp.tt != EqualOp)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

lpNxtRes->rt = RES_PROPERTY;

lpNxtRes->res.resProperty.relop =RELOP_EQ;
lpNxtRes->res.resProperty.ulPropTag =tiBooleanTag.dwValue;
lpNxtRes->res.resProperty.lpProp =NULL;

hr = MAPIAllocateMore(sizeof(SPropValue),
lpObject,
(LPVOID FAR *)&lpProp);

if (FAILED(hr))
goto cleanup;

lpProp->ulPropTag =tiBooleanTag.dwValue;
lpProp->Value.b =(USHORT)tiBooleanLiteral.dwValue;

lpNxtRes->res.resProperty.lpProp = lpProp;

break;
}

case NumericTag:
{
ULONGrelop;
TOKENINFOtiNumericTag =ti;
TOKENINFOtiNumericOp;
TOKENINFOtiNumericLiteral;

tiNumericOp.pch = pch;

hr = HrGetExpressionToken(&tiNumericOp);

if (FAILED(hr))
goto cleanup;

pch = tiNumericOp.pch + tiNumericOp.cch;

tiNumericLiteral.pch = pch;

hr = HrGetExpressionToken(&tiNumericLiteral);

if (FAILED(hr))
goto cleanup;

pch = tiNumericLiteral.pch + tiNumericLiteral.cch;

if (tiNumericLiteral.tt != NumericLiteral)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

switch (tiNumericOp.tt)
{
case NotEqualOp:
relop = RELOP_NE;
break;

case EqualOp:
relop = RELOP_EQ;
break;

case GreaterThanOp:
relop = RELOP_GT;
break;

case LessThanOp:
relop = RELOP_LT;
break;

default:
hr = HR_LOG(E_INVALIDARG);
goto cleanup;

}

lpNxtRes->rt = RES_PROPERTY;

lpNxtRes->res.resProperty.relop =relop;
lpNxtRes->res.resProperty.ulPropTag =tiNumericTag.dwValue;
lpNxtRes->res.resProperty.lpProp =NULL;

hr = MAPIAllocateMore(sizeof(SPropValue),
lpObject,
(LPVOID FAR *)&lpProp);

if (FAILED(hr))
goto cleanup;

lpProp->ulPropTag = tiNumericTag.dwValue;
lpProp->Value.l =tiNumericLiteral.dwValue;

lpNxtRes->res.resProperty.lpProp = lpProp;

break;
}

case StringTag:
{
ULONGrelop;
ULONGrt;
TOKENINFOtiStringTag =ti;
TOKENINFOtiStringOp;
TOKENINFOtiStringLiteral;

tiStringOp.pch = pch;

hr = HrGetExpressionToken(&tiStringOp);

if (FAILED(hr))
goto cleanup;

pch = tiStringOp.pch + tiStringOp.cch;

tiStringLiteral.pch = pch;

hr = HrGetExpressionToken(&tiStringLiteral);

if (FAILED(hr))
goto cleanup;

// Note that the string literal returned includes the
// opening and closing quotes (hence all the strange mods
// to pch and cch).

pch = tiStringLiteral.pch + tiStringLiteral.cch;

if (tiStringLiteral.tt != StringLiteral)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

switch (tiStringOp.tt)
{
case NotEqualOp:
relop = RELOP_NE;
rt =RES_PROPERTY;
break;

case EqualOp:
relop = RELOP_EQ;
rt =RES_PROPERTY;
break;

case ContainsOp:
rt =RES_CONTENT;
break;

default:
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

lpNxtRes->rt = rt;

if (rt == RES_PROPERTY)
{
lpNxtRes->res.resProperty.relop = relop;
lpNxtRes->res.resProperty.ulPropTag = tiStringTag.dwValue;
lpNxtRes->res.resProperty.lpProp = NULL;
}
else// rt == RES_CONTENT
{
lpNxtRes->res.resContent.ulFuzzyLevel =FL_SUBSTRING|
FL_IGNORECASE;

lpNxtRes->res.resContent.ulPropTag = tiStringTag.dwValue;
lpNxtRes->res.resContent.lpProp = NULL;
}

hr = MAPIAllocateMore(sizeof(SPropValue),
lpObject,
(LPVOID FAR *)&lpProp);

if (FAILED(hr))
goto cleanup;

if (rt == RES_PROPERTY)
{
lpNxtRes->res.resProperty.lpProp =lpProp;
}
else// rt == RES_CONTENT
{
lpNxtRes->res.resContent.lpProp =lpProp;
}

lpProp->ulPropTag =tiStringTag.dwValue;

if (PROP_TYPE(lpProp->ulPropTag) == PT_STRING8)
{
lpProp->Value.lpszA =NULL;

hr = MAPIAllocateMore(tiStringLiteral.cch + 1 - 2, lpObject,
(LPVOID FAR *)&lpProp->Value.lpszA);
if (FAILED(hr))
goto cleanup;

memcpy(lpProp->Value.lpszA,
tiStringLiteral.pch + 1,
tiStringLiteral.cch - 2);

*(lpProp->Value.lpszA + tiStringLiteral.cch - 2) = '\0';
}
else// it is UNICODE
{
INTcch = 0;

lpProp->Value.lpszW =NULL;

if (tiStringLiteral.cch > 2)
{
cch = MultiByteToWideChar(CP_ACP,
0,
tiStringLiteral.pch + 1,
tiStringLiteral.cch - 2,
NULL,
0);
if (cch == 0)
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}

hr = MAPIAllocateMore((cch + 1) * sizeof(WCHAR), lpObject,
(LPVOID FAR *)&lpProp->Value.lpszW);

if (FAILED(hr))
goto cleanup;

if (tiStringLiteral.cch > 2)
{
if (!MultiByteToWideChar(CP_ACP,
0,
tiStringLiteral.pch + 1,
tiStringLiteral.cch - 2,
lpProp->Value.lpszW,
cch))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}
}

*(lpProp->Value.lpszW + cch) = 0;
}

break;
}

case TimeTag:
{
ULONGrelop;
TOKENINFOtiTimeTag =ti;
TOKENINFOtiTimeOp;
TOKENINFOtiTimeLiteral;

tiTimeOp.pch = pch;

hr = HrGetExpressionToken(&tiTimeOp);

if (FAILED(hr))
goto cleanup;

pch = tiTimeOp.pch + tiTimeOp.cch;

tiTimeLiteral.pch = pch;

hr = HrGetExpressionToken(&tiTimeLiteral);

if (FAILED(hr))
goto cleanup;

pch = tiTimeLiteral.pch + tiTimeLiteral.cch;

if (tiTimeLiteral.tt != TimeLiteral)
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}

switch (tiTimeOp.tt)
{
case NotEqualOp:
relop = RELOP_NE;
break;

case EqualOp:
relop = RELOP_EQ;
break;

case GreaterThanOp:
relop = RELOP_GT;
break;

case LessThanOp:
relop = RELOP_LT;
break;

default:
hr = HR_LOG(E_INVALIDARG);
goto cleanup;

}

lpNxtRes->rt = RES_PROPERTY;

lpNxtRes->res.resProperty.relop =relop;
lpNxtRes->res.resProperty.ulPropTag =tiTimeTag.dwValue;
lpNxtRes->res.resProperty.lpProp =NULL;

hr = MAPIAllocateMore(sizeof(SPropValue),
lpObject,
(LPVOID FAR *)&lpProp);

if (FAILED(hr))
goto cleanup;

lpProp->ulPropTag = tiTimeTag.dwValue;
lpProp->Value.ft =tiTimeLiteral.ftValue;

lpNxtRes->res.resProperty.lpProp = lpProp;

break;
}

default:
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
}

ti.pch = pch;

hr = HrGetExpressionToken(&ti);

if (FAILED(hr))
goto cleanup;

pch = ti.pch + ti.cch;// Now pch points to next token.

switch (ti.tt)
{
case AndOp:
{
LPSRestrictionlpResArray =NULL;

hr = MAPIAllocateMore(sizeof(SRestriction) * 2,
lpObject,
(LPVOID FAR *)&lpResArray);

if (FAILED(hr))
goto cleanup;

lpResArray[0] = *lpRes;

lpResArray[1].rt = RES_UNINITIALIZED;

lpRes->rt = RES_AND;

lpRes->res.resAnd.cRes =2;
lpRes->res.resAnd.lpRes =lpResArray;

lpNxtRes = &lpResArray[1];

break;
}

case OrOp:
{
LPSRestrictionlpResArray =NULL;

hr = MAPIAllocateMore(sizeof(SRestriction) * 2,
lpObject,
(LPVOID FAR *)&lpResArray);

if (FAILED(hr))
goto cleanup;

lpResArray[0] = *lpRes;

lpResArray[1].rt = RES_UNINITIALIZED;

lpRes->rt = RES_OR;

lpRes->res.resOr.cRes =2;
lpRes->res.resOr.lpRes =lpResArray;

lpNxtRes = &lpResArray[1];

break;
}

case EndOfString:
case RightParen:
{
pch = ti.pch;// Push this token back and return to caller.

if (lpRes->rt == RES_UNINITIALIZED)
hr = HR_LOG(E_INVALIDARG);

goto cleanup;// Done!
}

default:
{
hr = HR_LOG(E_INVALIDARG);
goto cleanup;
}
}
}

cleanup:

*ppch = pch;

RETURN(hr);
}


// $--HrPrintToString----------------------------------------------------------
//
// DESCRIPTION:Write the formatted string to the end of the supplied
//output string.
//
// INPUT/OUTPUT:
//
//[lppszString]-- Output string ptr ptr. If *lppszString == NULL, then
// a string will be allocated by this routine.
//[lpcbString]-- Ptr to count of bytes in input string on input (including
// terminating '\0'); count of bytes in string after
// appending formatted string on output.
//[lpcbBuffer]-- Ptr to count of bytes in input string buffer on input;
// count of bytes in string buffer after appending formatted
// string on output (buffer size may increase if allocation
// was necessary).
//
// INPUT:
//
//[Format]-- A printf style format string.
//[...]-- Format arguments.
//
// RETURNS: NOERRORif successful;
//E_OUTOFMEMORYif out of memory;
// E_*otherwise.
//
// Notes:This routine appends the formatted string to the end of an
//existing string. If the existing string is NULL, (ie.
//*lppszString == NULL) then this routine will do the initial
//allocation. Note that *lppszString may be reallocated as
//necessary to grow the buffer, and the old copy will be released.
//The routine uses MAPIAllocateBuffer() and MAPIFreeBuffer().
//
//-----------------------------------------------------------------------------

#defineHRPRINTTOSTRING_BUFINCR1024

HRESULT
HrPrintToString(// RETURNS: HRESULT
IN OUTLPSTR FAR *lppszString,// output string ptr ptr
IN OUTLPULONGlpcbString,// ptr to count of bytes in string
IN OUTLPULONGlpcbBuffer,// ptr to count of bytes in buffer
INLPSTRFormat // message format
... // variable arguments
)
{
va_listargs;
LONGcb =0;
HRESULThr =NOERROR;
LPSTRlpszStringCopy =NULL;

DEBUGPRIVATE("HrPrintToString()\n");

if (*lppszString == NULL)
{
hr = MAPIAllocateBuffer(HRPRINTTOSTRING_BUFINCR,
(LPVOID FAR *)lppszString);
if (FAILED(hr))
goto cleanup;

*lpcbBuffer =HRPRINTTOSTRING_BUFINCR;
*lpcbString =1;
*lppszString[0] ='\0';
}

va_start(args, Format);

while (TRUE)
{
cb = _vsnprintf(*lppszString + *lpcbString - 1,
*lpcbBuffer - *lpcbString + 1,
Format,
args);

if (cb >= 0)
{
*lpcbString += cb;
break;
}

hr = MAPIAllocateBuffer(*lpcbBuffer + HRPRINTTOSTRING_BUFINCR,
(LPVOID FAR *)&lpszStringCopy);
if (FAILED(hr))
goto cleanup;

*lpcbBuffer += HRPRINTTOSTRING_BUFINCR;

memcpy(lpszStringCopy, *lppszString, *lpcbString);

MAPIFREEBUFFER(*lppszString);

*lppszString = lpszStringCopy;
}

cleanup:

va_end(args);

RETURN(hr);
}


// $--HrPrivateRestrictionToString---------------------------------------------
//
// DESCRIPTION:Get a string representation of an SRestriction.
//
// INPUT:
//
//[lpRes]-- Ptr to restriction to be represented as a string.
//
// INPUT/OUTPUT:
//
//[lppszString]-- Output string ptr ptr. If *lppszString == NULL, then
// a string will be allocated by this routine.
//[lpcbString]-- Ptr to count of bytes in input string on input (including
// terminating '\0'); count of bytes in string after
// appending formatted string on output.
//[lpcbBuffer]-- Ptr to count of bytes in input string buffer on input;
// count of bytes in string buffer after appending formatted
// string on output (buffer size may increase if allocation
// was necessary).
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrPrivateRestrictionToString(
INLPSRestrictionlpRes,// restriction ptr
IN OUTLPSTR FAR *lppszString,// output string ptr ptr
IN OUTLPULONGlpcbString,// ptr to count of bytes in string
IN OUTLPULONGlpcbBuffer// ptr to count of bytes in buffer
)
{
HRESULThr =NOERROR;

DEBUGPRIVATE("HrPrivateRestrictionToString()\n");

if (lpRes == NULL)
{
hr = HrPrintToString(lppszString, lpcbString, lpcbBuffer, "<NULL>");

if (FAILED(hr))
goto cleanup;
}
else
{
switch (lpRes->rt)
{
case RES_AND:
hr = HrAndRestrictionToString(lpRes,
lppszString,
lpcbString,
lpcbBuffer);
break;

case RES_OR:
hr = HrOrRestrictionToString(lpRes,
lppszString,
lpcbString,
lpcbBuffer);
break;

case RES_NOT:
hr = HrNotRestrictionToString(lpRes,
lppszString,
lpcbString,
lpcbBuffer);
break;

case RES_CONTENT:
hr = HrContentRestrictionToString(lpRes,
lppszString,
lpcbString,
lpcbBuffer);
break;

case RES_PROPERTY:
hr = HrPropertyRestrictionToString(lpRes,
lppszString,
lpcbString,
lpcbBuffer);
break;

case RES_COMPAREPROPS:
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<SComparePropsRestriction>");
break;

case RES_BITMASK:
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<SBitMaskRestriction>");
break;

case RES_SIZE:
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<SSizeRestriction>");
break;

case RES_EXIST:
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<SExistRestriction>");
break;

case RES_SUBRESTRICTION:
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<SSubRestriction>");
break;

case RES_COMMENT:
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<SCommentRestriction>");
break;

case RES_UNINITIALIZED:
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<Uninitialized SRestriction>");
break;

default:
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<Invalid SRestriction>");
break;
}

if (FAILED(hr))
goto cleanup;
}

cleanup:

RETURN(hr);
}


// $-HrPropertyRestrictionToString---------------------------------------------
//
// DESCRIPTION:Get a string representation of an SPropertyRestriction.
//
// INPUT:
//
//[lpRes]-- Ptr to property restriction to be represented as a
// string.
//
// INPUT/OUTPUT:
//
//[lppszString]-- Output string ptr ptr. If *lppszString == NULL, then
// a string will be allocated by this routine.
//[lpcbString]-- Ptr to count of bytes in input string on input (including
// terminating '\0'); count of bytes in string after
// appending formatted string on output.
//[lpcbBuffer]-- Ptr to count of bytes in input string buffer on input;
// count of bytes in string buffer after appending formatted
// string on output (buffer size may increase if allocation
// was necessary).
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrPropertyRestrictionToString(// RETURNS: HRESULT
INLPSRestrictionlpRes,// restriction ptr
IN OUTLPSTR FAR *lppszString,// output string ptr ptr
IN OUTLPULONGlpcbString,// ptr to count of bytes in string
IN OUTLPULONGlpcbBuffer// ptr to count of bytes in buffer
)
{
HRESULThr = NOERROR;
LPSTRlpszPropName =NULL;
LPSTRlpszRelop = NULL;

DEBUGPRIVATE("HrPropertyRestrictionToString()\n");

switch (lpRes->res.resProperty.relop)
{
case RELOP_LT:
lpszRelop = " < ";
break;

case RELOP_LE:
lpszRelop = " <= ";
break;

case RELOP_GT:
lpszRelop = " > ";
break;

case RELOP_GE:
lpszRelop = " >= ";
break;

case RELOP_EQ:
lpszRelop = " = ";
break;

case RELOP_NE:
lpszRelop = " # ";
break;

case RELOP_RE:
lpszRelop = " <RELOP_RE Operator> ";
break;

default:
lpszRelop = " <Invalid Operator> ";
break;
}

hr = HrGetPropTagName(lpRes->res.resProperty.ulPropTag,
&lpszPropName);
if (FAILED(hr))
goto cleanup;

hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"%s%s",
lpszPropName,
lpszRelop);
if (FAILED(hr))
goto cleanup;

hr = HrPropValueToString(lpRes->res.resProperty.lpProp,
lppszString,
lpcbString,
lpcbBuffer);
if (FAILED(hr))
goto cleanup;

cleanup:

MAPIFREEBUFFER(lpszPropName);

RETURN(hr);
}


// $--HrPropValueToString------------------------------------------------------
//
// DESCRIPTION:Get a string representation of an SPropValue..
//
// INPUT:
//
//[lpProp]-- Ptr to property value to be represented as a string.
//
// INPUT/OUTPUT:
//
//[lppszString]-- Output string ptr ptr. If *lppszString == NULL, then
// a string will be allocated by this routine.
//[lpcbString]-- Ptr to count of bytes in input string on input (including
// terminating '\0'); count of bytes in string after
// appending formatted string on output.
//[lpcbBuffer]-- Ptr to count of bytes in input string buffer on input;
// count of bytes in string buffer after appending formatted
// string on output (buffer size may increase if allocation
// was necessary).
//
// RETURNS: NOERRORif successful;
// E_INVALIDARGif bad input;
// E_FAILotherwise.
//-----------------------------------------------------------------------------

HRESULT
HrPropValueToString( // RETURNS: VOID
INLPSPropValuelpProp,// property value ptr
IN OUTLPSTR FAR *lppszString,// output string ptr ptr
IN OUTLPULONGlpcbString,// ptr to count of bytes in string
IN OUTLPULONGlpcbBuffer// ptr to count of bytes in buffer
)
{
HRESULThr =NOERROR;

DEBUGPRIVATE("HrPropValueToString()\n");

if (lpProp == NULL)
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<Undefined SPropValue>");
if (FAILED(hr))
goto cleanup;
}
else
{
ULONGulPropTag =0;

ulPropTag = lpProp->ulPropTag;

switch (PROP_TYPE(ulPropTag))
{
case PT_I2:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"%#hx",
lpProp->Value.i);
break;
}

case PT_LONG:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"%#x",
lpProp->Value.ul);
break;
}

case PT_R4:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_R4 value dump not supported>");
break;
}

case PT_DOUBLE:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_DOUBLE value dump not supported>");
break;
}

case PT_BOOLEAN:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
lpProp->Value.b ? "TRUE" : "FALSE");
break;
}

case PT_CURRENCY:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_CURRENCY value dump not supported>");
break;
}

case PT_APPTIME:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_APPTIME value dump not supported>");
break;
}

case PT_SYSTIME: 
{
SYSTEMTIMEst ={0};
FILETIMEftLocal ={0};

// Convert UTC FILETIME to Local FILETIME.

if (!FileTimeToLocalFileTime(&lpProp->Value.ft, &ftLocal))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

// Convert Local FILETIME to SYSTEMTIME.

if (!FileTimeToSystemTime(&ftLocal, &st))
{
hr = HR_LOG(E_FAIL);
goto cleanup;
}

hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"%02hu:%02hu:%02hu %04hu-%02hu-%02hu",
st.wHour,
st.wMinute,
st.wSecond,
st.wYear,
st.wMonth,
st.wDay);
break;
}

case PT_STRING8:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"\"%s\"",
lpProp->Value.lpszA);
break;
}

case PT_BINARY:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_BINARY value dump not supported>");
break;
}

case PT_UNICODE:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"\"%S\"",
lpProp->Value.lpszW);
break;
}

case PT_CLSID:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_CLSID value dump not supported>");
break;
}

case PT_I8:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_I8 value dump not supported>");
break;
}

case PT_MV_I2:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_I2 value dump not supported>");
break;
}

case PT_MV_LONG:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_LONG value dump not supported>");
break;
}

case PT_MV_R4:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_R4 value dump not supported>");
break;
}

case PT_MV_DOUBLE:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_DOUBLE value dump not supported>");
break;
}

case PT_MV_CURRENCY:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_CURRENCY value dump not supported>");
break;
}

case PT_MV_APPTIME:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_APPTIME value dump not supported>");
break;
}

case PT_MV_SYSTIME:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_SYSTIME value dump not supported>");
break;
}

case PT_MV_BINARY:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_BINARY value dump not supported>");
break;
}

case PT_MV_STRING8:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_STRING8 value dump not supported>");
break;
}

case PT_MV_UNICODE:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_UNICODE value dump not supported>");
break;
}

case PT_MV_CLSID:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_CLSID value dump not supported>");
break;
}

case PT_MV_I8:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_MV_I8 value dump not supported>");
break;
}

case PT_ERROR:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"%x",
lpProp->Value.err);
break;
}

case PT_NULL:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_NULL value dump not supported>");
break;
}

case PT_OBJECT:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<PT_OBJECT value dump not supported>");
break;
}

default:
{
hr = HrPrintToString(lppszString,
lpcbString,
lpcbBuffer,
"<Undefined property type value!>");
}
}

if (FAILED(hr))
goto cleanup;
}

cleanup:

RETURN(hr);
}


// $--lpszSkipNonWhiteSpace----------------------------------------------------
//
// DESCRIPTION: Find first whitespace CHAR or '\0' in *pch.
//
// INPUT:
//
//[pch]-- Ptr to string to be examined for whitespace CHAR.
//
// RETURNS: Ptr to first whitespace CHAR or '\0' in *pch.
//
//-----------------------------------------------------------------------------

inline
CHAR *
lpszSkipNonWhiteSpace(// RETURNS CHAR *
INCHAR *pch // input string
)
{
while (!isspace(*pch) && *pch != '\0')
pch++;

return pch;
}


// $--lpszSkipWhiteSpace-------------------------------------------------------
//
// DESCRIPTION: Find first nonwhitespace CHAR in *pch.
//
// INPUT:
//
//[pch]-- Ptr to string to be examined for nonwhitespace CHAR.
//
// RETURNS: Ptr to first nonwhitespace CHAR in *pch.
//
//-----------------------------------------------------------------------------

inline
CHAR *
lpszSkipWhiteSpace(// RETURNS CHAR *
INCHAR *pch // input string
)
{
while (isspace(*pch))
pch++;

return pch;
}


// NOTE NOTE NOTE - Code below here does not yet conform to coding standards
//because we do not yet know if it will be left in the
//product. It will be appropriately munged when and if
//necessary.

HRESULT
HrCountProps(
INint cprop,
INLPSPropValuergprop,
OUTULONG FAR *pcb
)
{
LPSPropValuepprop;
ULONGcb = 0;
ULONGcbMV;
intiValue;

if ((rgprop && !cprop) || IsBadReadPtr(rgprop, cprop*sizeof(SPropValue)))
return E_INVALIDARG;

for (pprop = rgprop; cprop--; ++pprop)
{
ULONGulType = PROP_TYPE(pprop->ulPropTag);

//Check for valid PROP_TYPE and count memory consumed.

cb += sizeof(SPropValue);

switch (PROP_TYPE(pprop->ulPropTag))
{
case PT_UNSPECIFIED:
case PT_OBJECT:
default:
{
return E_INVALIDARG;
}

case PT_I2:
case PT_LONG:
case PT_R4:
case PT_APPTIME:
case PT_DOUBLE:
case PT_BOOLEAN:
case PT_CURRENCY:
case PT_SYSTIME:
case PT_CLSID:
case PT_I8:
case PT_ERROR:
case PT_NULL:
{
break;
}

case PT_BINARY:
{
cb += pprop->Value.bin.cb;

if (pprop->Value.bin.cb&&
IsBadReadPtr(pprop->Value.bin.lpb,
(UINT)(pprop->Value.bin.cb)))
{
goto badProp;
}

break;
}

case PT_STRING8:
{
if (IsBadStringPtrA(pprop->Value.lpszA, INFINITE))
goto badProp;

cb += (lstrlenA( pprop->Value.lpszA ) + 1) * sizeof(CHAR);

break;
}

case PT_UNICODE:
{
if (IsBadStringPtrW(pprop->Value.lpszW, INFINITE))
goto badProp;

cb += (lstrlenW(pprop->Value.lpszW) + 1) * sizeof(WCHAR);

break;
}

//Note!MVxxx.cValues may NOT be zero (DCR 2789).

case PT_MV_I2:
{
if (!(cbMV = pprop->Value.MVi.cValues * sizeof(short int)) ||
IsBadReadPtr(pprop->Value.MVi.lpi, (UINT)cbMV))
{
goto badProp;
}

cb += cbMV;

break;
}

case PT_MV_LONG:
{
if (!(cbMV = pprop->Value.MVl.cValues * sizeof(LONG)) ||
IsBadReadPtr(pprop->Value.MVl.lpl, (UINT)cbMV))
{
goto badProp;
}

cb += cbMV;

break;
}

case PT_MV_R4:
{
if (!(cbMV = pprop->Value.MVflt.cValues * sizeof(float)) ||
IsBadReadPtr(pprop->Value.MVflt.lpflt, (UINT)cbMV))
{
goto badProp;
}

cb += cbMV;

break;
}

case PT_MV_APPTIME:
{
if (!(cbMV = pprop->Value.MVat.cValues * sizeof(double)) ||
IsBadReadPtr(pprop->Value.MVat.lpat, (UINT)cbMV))
{
goto badProp;
}

cb += cbMV;

break;
}

case PT_MV_DOUBLE:
{
if (!(cbMV = pprop->Value.MVdbl.cValues * sizeof(double)) ||
IsBadReadPtr(pprop->Value.MVdbl.lpdbl, (UINT)cbMV))
{
goto badProp;
}

cb += cbMV;

break;
}

case PT_MV_CURRENCY:
{
if (!(cbMV = pprop->Value.MVcur.cValues * sizeof(CURRENCY)) ||
IsBadReadPtr(pprop->Value.MVcur.lpcur, (UINT)cbMV))
{
goto badProp;
}

cb += cbMV;

break;
}

case PT_MV_SYSTIME:
{
if (!(cbMV = pprop->Value.MVft.cValues * sizeof(FILETIME)) ||
IsBadReadPtr(pprop->Value.MVft.lpft, (UINT)cbMV))
{
goto badProp;
}

cb += cbMV;

break;
}

case PT_MV_CLSID:
{
if (!(cbMV = pprop->Value.MVguid.cValues * sizeof(GUID)) ||
IsBadReadPtr(pprop->Value.MVguid.lpguid, (UINT)cbMV))
{
goto badProp;
}

cb += cbMV;

break;
}

case PT_MV_I8:
{
if (!(cbMV =
pprop->Value.MVli.cValues * sizeof(LARGE_INTEGER)) ||
IsBadReadPtr(pprop->Value.MVli.lpli, (UINT)cbMV))
{
goto badProp;
}

cb += cbMV;

break;
}

case PT_MV_BINARY:
{
if (!(cbMV = pprop->Value.MVbin.cValues * sizeof(SBinary)) ||
IsBadReadPtr(pprop->Value.MVbin.lpbin, (UINT)cbMV))
{
goto badProp;
}

for (iValue = 0;
(ULONG)iValue < pprop->Value.MVbin.cValues;
iValue++)
{
if (IsBadReadPtr(pprop->Value.MVbin.lpbin[iValue].lpb,
(UINT)pprop->Value.MVbin.lpbin[iValue].cb))
{
goto badProp;
}

cb += pprop->Value.MVbin.lpbin[iValue].cb;
}

cb += cbMV;

break;
}

case PT_MV_STRING8:
{
if (!(cbMV = pprop->Value.MVszA.cValues * sizeof(LPVOID)) ||
IsBadReadPtr(pprop->Value.MVszA.lppszA, (UINT)cbMV))
{
goto badProp;
}

for (iValue = 0;
(ULONG)iValue < pprop->Value.MVszA.cValues;
iValue++)
{
if (IsBadStringPtrA(pprop->Value.MVszA.lppszA[iValue],
INFINITE))
{
goto badProp;
}

cb += lstrlenA(pprop->Value.MVszA.lppszA[iValue]) + 1;
}

cb += cbMV;

break;
}

case PT_MV_UNICODE:
{
if (!(cbMV = pprop->Value.MVszW.cValues * sizeof(LPVOID)) ||
IsBadReadPtr(pprop->Value.MVszW.lppszW, (UINT)cbMV))
{
goto badProp;
}

for (iValue = 0;
(ULONG)iValue < pprop->Value.MVszW.cValues;
iValue++)
{
if (IsBadStringPtrW(pprop->Value.MVszW.lppszW[iValue],
INFINITE))
{
goto badProp;
}

cb +=(lstrlenW(pprop->Value.MVszW.lppszW[iValue]) + 1) *
sizeof(WCHAR);
}

cb += cbMV;

break;
}
}
}

if (pcb)
*pcb = cb;

return NOERROR;

badProp:

return E_INVALIDARG;
}


HRESULT
HrCopyProps(
INintcprop,
INLPSPropValuergprop,
OUTLPVOIDpvDst,
OUTULONG FAR * pcb
)
{
LPSPropValuepprop;
LPSPropValueppropDst;
ULONGcb;
LPBYTEpb;
UINTcbT;
intiValue;

cb = cprop * sizeof(SPropValue);

memcpy(pvDst, rgprop, (UINT)cb);

pb = (LPBYTE)pvDst + cb;

for (pprop = rgprop, ppropDst = (LPSPropValue)pvDst;
cprop--;
++pprop, ++ppropDst)
{
//Tricky: common code after the switch increments pb and cb
//by the amount copied. If no increment is necessary, the case
//uses 'continue' rather than 'break' to exit the switch, thus
//skipping the increment -- AND any other code which may be
//added after the switch.

switch (PROP_TYPE(pprop->ulPropTag))
{
default:
return E_INVALIDARG;

case PT_I2:
case PT_LONG:
case PT_R4:
case PT_APPTIME:
case PT_DOUBLE:
case PT_BOOLEAN:
case PT_CURRENCY:
case PT_SYSTIME:
case PT_CLSID:
case PT_I8:
case PT_ERROR:
case PT_NULL:
continue;//nothing to add

case PT_BINARY:
ppropDst->Value.bin.lpb = pb;
cbT = (UINT)pprop->Value.bin.cb;
memcpy(pb, pprop->Value.bin.lpb, cbT);
break;

case PT_STRING8:
ppropDst->Value.lpszA = (LPSTR)pb;
cbT = lstrlenA( pprop->Value.lpszA ) + 1;
memcpy(pb, pprop->Value.lpszA, cbT);
break;

case PT_UNICODE:
ppropDst->Value.lpszW = (LPWSTR)pb;
cbT = (lstrlenW( pprop->Value.lpszW ) + 1) * sizeof(WCHAR);
memcpy(pb, pprop->Value.lpszW, cbT);
break;


case PT_MV_I2:
ppropDst->Value.MVi.lpi = (short int FAR *)pb;
cbT = (UINT)pprop->Value.MVi.cValues * sizeof(short int);
memcpy(pb, pprop->Value.MVi.lpi, cbT);
break;

case PT_MV_LONG:
ppropDst->Value.MVl.lpl = (LONG FAR *)pb;
cbT = (UINT)pprop->Value.MVl.cValues * sizeof(LONG);
memcpy(pb, pprop->Value.MVl.lpl, cbT);
break;

case PT_MV_R4:
ppropDst->Value.MVflt.lpflt = (float FAR *)pb;
cbT = (UINT)pprop->Value.MVflt.cValues * sizeof(float);
memcpy(pb, pprop->Value.MVflt.lpflt, cbT);
break;

case PT_MV_APPTIME:
ppropDst->Value.MVat.lpat = (double FAR *)pb;
cbT = (UINT)pprop->Value.MVat.cValues * sizeof(double);
memcpy(pb, pprop->Value.MVat.lpat, cbT);
break;

case PT_MV_DOUBLE:
ppropDst->Value.MVdbl.lpdbl = (double FAR *)pb;
cbT = (UINT)pprop->Value.MVdbl.cValues * sizeof(double);
memcpy(pb, pprop->Value.MVdbl.lpdbl, cbT);
break;

case PT_MV_CURRENCY:
ppropDst->Value.MVcur.lpcur = (CURRENCY FAR *)pb;
cbT = (UINT)pprop->Value.MVcur.cValues * sizeof(CURRENCY);
memcpy(pb, pprop->Value.MVcur.lpcur, cbT);
break;

case PT_MV_SYSTIME:
ppropDst->Value.MVft.lpft = (FILETIME FAR *)pb;
cbT = (UINT)pprop->Value.MVft.cValues * sizeof(FILETIME);
memcpy(pb, pprop->Value.MVft.lpft, cbT);
break;

case PT_MV_CLSID:
ppropDst->Value.MVguid.lpguid = (GUID FAR *)pb;
cbT = (UINT)pprop->Value.MVguid.cValues * sizeof(GUID);
memcpy(pb, pprop->Value.MVguid.lpguid, cbT);
break;

case PT_MV_I8:
ppropDst->Value.MVli.lpli = (LARGE_INTEGER FAR *)pb;
cbT = (UINT)pprop->Value.MVli.cValues * sizeof(LARGE_INTEGER);
memcpy(pb, pprop->Value.MVli.lpli, cbT);
break;

case PT_MV_BINARY:
ppropDst->Value.MVbin.lpbin = (SBinary *) pb;
pb += pprop->Value.MVbin.cValues * sizeof(SBinary);
for (iValue = 0;
(ULONG)iValue < pprop->Value.MVbin.cValues;
iValue++)
{
ppropDst->Value.MVbin.lpbin[iValue].lpb = pb;
cbT = (UINT)pprop->Value.MVbin.lpbin[iValue].cb;
memcpy(pb, pprop->Value.MVbin.lpbin[iValue].lpb, cbT);
cb += cbT;
pb += cbT;
}
continue;//already updated, don't do it again

case PT_MV_STRING8:
ppropDst->Value.MVszA.lppszA = (LPSTR *) pb;
pb += pprop->Value.MVszA.cValues * sizeof(LPSTR);
for (iValue = 0;
(ULONG)iValue < pprop->Value.MVszA.cValues;
iValue++)
{
ppropDst->Value.MVszA.lppszA[iValue] = (LPSTR)pb;
cbT = lstrlenA(pprop->Value.MVszA.lppszA[iValue]) + 1;
memcpy(pb, pprop->Value.MVszA.lppszA[iValue], cbT);
pb += cbT;
cb += cbT;
}
continue;//already updated, don't do it again

case PT_MV_UNICODE:
ppropDst->Value.MVszW.lppszW = (LPWSTR *) pb;
pb += pprop->Value.MVszW.cValues * sizeof(LPWSTR);
for (iValue = 0;
(ULONG)iValue < pprop->Value.MVszW.cValues;
iValue++)
{
ppropDst->Value.MVszW.lppszW[iValue] = (LPWSTR)pb;
cbT = (lstrlenW(pprop->Value.MVszW.lppszW[iValue]) + 1)
* sizeof(WCHAR);
memcpy(pb, pprop->Value.MVszW.lppszW[iValue], cbT);
pb += cbT;
cb += cbT;
}
continue;//already updated, don't do it again
}

//Advance ptr and total count by the amount copied
pb += cbT;
cb += cbT;
}

if (pcb)
*pcb = cb;

return NOERROR;
}

/*
*Wrapper function to just duplicate a property value array
*into a single block of MAPI memory.
*/

HRESULT
HrDupPropset(
INint cprop,
INLPSPropValuergprop,
INLPVOIDlpObject,
OUTLPSPropValue FAR *prgprop
)
{
ULONGcb;
HRESULThr;

//Find out how much memory we need

if (hr = HrCountProps(cprop, rgprop, &cb))
goto ret;

//Obtain memory

if (lpObject != NULL)
hr = MAPIAllocateMore(cb, lpObject, (LPVOID FAR *)prgprop);
else
hr = MAPIAllocateBuffer(cb, (LPVOID FAR *)prgprop);

if (hr)
goto ret;

//Copy the properties

if (hr = HrCopyProps(cprop, rgprop, *prgprop, &cb))
goto ret;

ret:
return hr;
}