/*
 *	MAPI 1.0 property handling routines
 *
 *
 *	MAPIUTIL.C -
 *
 *		Useful routines for manipulating and comparing property values continued
 *	The difference between this file and proputil.c is that this file doesn't require
 *	any c-runtimes.
 */

#include <_apipch.h>



#ifndef MB_SETFOREGROUND
#define MB_SETFOREGROUND 0
#endif

STDAPI_(BOOL)
FEqualNames(LPMAPINAMEID lpName1, LPMAPINAMEID lpName2)
{
	AssertSz(lpName1 && !IsBadReadPtr(lpName1, sizeof(MAPINAMEID)),
			 TEXT("lpName1 fails address check"));
			
	AssertSz(lpName2 && !IsBadReadPtr(lpName2, sizeof(MAPINAMEID)),
			 TEXT("lpName2 fails address check"));
	//
	//  Same ptr case - optimization
	if (lpName1 == lpName2)
		return TRUE;

	if (memcmp(lpName1->lpguid, lpName2->lpguid, sizeof(GUID)))
		return FALSE;

	if (lpName1->ulKind == lpName2->ulKind)
	{
		if (lpName1->ulKind == MNID_STRING)
		{
			if (!lstrcmpW(lpName1->Kind.lpwstrName,
						  lpName2->Kind.lpwstrName))
			{
				return TRUE;
			}

		} else
		{
			if (lpName1->Kind.lID == lpName2->Kind.lID)
			{
				return TRUE;
			}
		}
	}

	return FALSE;
}



/*
 *	IsBadBoundedStringPtr
 *	
 *	Like IsBadStringPtr, but guarantees in addition that there is a
 *	valid string which will fit in a buffer of cchMax characters.
 */
BOOL WINAPI EXPORT_16
IsBadBoundedStringPtr(const void FAR *lpsz, UINT cchMax)
{
	if (IsBadStringPtr(lpsz, (UINT) -1) || ((UINT) lstrlenA(lpsz) >= cchMax))
		return TRUE;

	return FALSE;
}

/*
 *	For now, internal to HrQueryAllRows.
 *	
 *	Merges prows with *pprowsDst, reallocating *pprowsDst if
 *	necessary. Destroys the container portion of prows (but not the
 *	individual rows it contains).
 */
HRESULT	// STDAPI
HrMergeRowSets(LPSRowSet prows, LPSRowSet FAR *pprowsDst)
{
	SCODE		sc = S_OK;
	LPSRowSet	prowsT;
	UINT		crowsSrc;
	UINT		crowsDst;

	Assert(!IsBadWritePtr(pprowsDst, sizeof(LPSRowSet)));
	Assert(prows);

	if (!*pprowsDst || (*pprowsDst)->cRows == 0)
	{
		//	This is easy. But check this case first, because if the
		//	table is completely empty we want to return this.
		FreeBufferAndNull(pprowsDst);    // correct, no '&'
		*pprowsDst = prows;
		prows = NULL;		//	don't free it!
		goto ret;
	}

	if (prows->cRows == 0)
	{
		//	This is easy too
		goto ret;
	}

	//	OK, now we know there are rows in both rowsets.
	//	We have to do a real merge.

	SideAssert(crowsSrc = (UINT) prows->cRows);
	crowsDst = (UINT) (*pprowsDst)->cRows;	//	handle 0

	if (FAILED(sc = MAPIAllocateBuffer(CbNewSRowSet(crowsSrc + crowsDst),
			&prowsT)))
		goto ret;
	if (crowsDst)
		CopyMemory(prowsT->aRow, (*pprowsDst)->aRow, crowsDst*sizeof(SRow));
	CopyMemory(&prowsT->aRow[crowsDst], prows->aRow, crowsSrc*sizeof(SRow));
	prowsT->cRows = crowsSrc + crowsDst;
	FreeBufferAndNull(pprowsDst);    // correct, no '&'
	*pprowsDst = prowsT;

ret:
	FreeBufferAndNull(&prows);

	DebugTraceSc(HrMergeRowSets, sc);
	return ResultFromScode(sc);

}

/*
 -	HrQueryAllRows
 -	
 *	Purpose:
 *		Retrieves all rows from an IMAPITable interface up to a set
 *		maximum. It will optionally set the column set, sort order,
 *		and restriction on the table before querying.
 *	
 *		If the table is empty, an SRowSet with zero rows is
 *		returned (just like QueryRows).
 *	
 *		The seek position of the table is undefined both before and
 *		after this call.
 *	
 *		If the function fails with an error other than
 *		MAPI_E_NOT_ENOUGH_MEMORY, extended error information is
 *		available through the table interface.
 *	
 *	Arguments:
 *		ptable		in		the table interface to query
 *		ptaga		in		if not NULL, column set for the table
 *		pres		in		if not NULL, restriction to be applied
 *		psos		in		if not NULL, sort order to be applied
 *		crowsMax	in		if nonzero, limits the number of rows
 *							to be returned.
 *		pprows		out		all rows of the table
 *	
 *	Returns:
 *		HRESULT. Extended error information normally is in the
 *		table.
 *	
 *	Side effects:
 *		Seek position of table is undefined.
 *	
 *	Errors:
 *		MAPI_E_TABLE_TOO_BIG if the table contains more than
 *		cRowsMax rows.
 */
STDAPI
HrQueryAllRows(LPMAPITABLE ptable,
	LPSPropTagArray ptaga, LPSRestriction pres, LPSSortOrderSet psos,
	LONG crowsMax, LPSRowSet FAR *pprows)
{
	HRESULT		hr;
	LPSRowSet	prows = NULL;
	UINT		crows = 0;
	LPSRowSet	prowsT;
	UINT		crowsT;

#if !defined(DOS)
// Why have we commented out the check for PARAMETER_VALIDATION? --gfb
//#ifdef	PARAMETER_VALIDATION
	if (FBadUnknown(ptable))
	{
		DebugTraceArg(HrQueryAllRows,  TEXT("ptable fails address check"));
		goto badArg;
	}
	if (ptaga && FBadColumnSet(ptaga))
	{
		DebugTraceArg(HrQueryAllRows,  TEXT("ptaga fails address check"));
		goto badArg;
	}
	if (pres && FBadRestriction(pres))
	{
		DebugTraceArg(HrQueryAllRows,  TEXT("pres fails address check"));
		goto badArg;
	}
	if (psos && FBadSortOrderSet(psos))
	{
		DebugTraceArg(HrQueryAllRows,  TEXT("psos fails address check"));
		goto badArg;
	}
	if (IsBadWritePtr(pprows, sizeof(LPSRowSet)))
	{
		DebugTraceArg(HrQueryAllRows,  TEXT("pprows fails address check"));
		goto badArg;
	}
//#endif
#endif

	*pprows = NULL;

	//	Set up the table, if the corresponding setup parameter
	//	is present.

	if (ptaga &&
		HR_FAILED(hr = ptable->lpVtbl->SetColumns(ptable, ptaga, TBL_BATCH)))
		goto ret;

	if (pres &&
		HR_FAILED(hr = ptable->lpVtbl->Restrict(ptable, pres, TBL_BATCH)))
		goto ret;

	if (psos &&
		HR_FAILED(hr = ptable->lpVtbl->SortTable(ptable, psos, TBL_BATCH)))
		goto ret;

	//	Set position to beginning of the table.

	if (HR_FAILED(hr = ptable->lpVtbl->SeekRow(ptable, BOOKMARK_BEGINNING,
			0, NULL)))
		goto ret;

	if (crowsMax == 0)
		crowsMax = LONG_MAX;

	for (;;)
	{
		prowsT = NULL;

		//	Retrieve some rows. Ask for the limit.
		hr = ptable->lpVtbl->QueryRows(ptable, crowsMax, 0, &prowsT);
		if (HR_FAILED(hr))
		{
			//	Note: the failure may actually have happened during
			//	one of the setup calls, since we set TBL_BATCH.
			goto ret;
		}
		Assert(prowsT->cRows <= UINT_MAX);
		crowsT = (UINT) prowsT->cRows;

		//	Did we get more rows than caller can handle?

		if ((LONG) (crowsT + (prows ? prows->cRows : 0)) > crowsMax)
		{
			hr = ResultFromScode(MAPI_E_TABLE_TOO_BIG);
			FreeProws(prowsT);
			goto ret;
		}

		//	Add the rows just retrieved into the set we're building.
		//	Note: this handles boundary conditions including either
		//	row set is empty.
		if (HR_FAILED(hr = HrMergeRowSets(prowsT, &prows)))
			goto ret;
		//	NOTE: the merge destroys prowsT.

		//	Did we hit the end of the table?
		//	Unfortunately, we have to ask twice before we know.
		if (crowsT == 0)
			break;

	}

	*pprows = prows;

ret:
	if (HR_FAILED(hr))
		FreeProws(prows);

	DebugTraceResult(HrGetAllRows, hr);
	return hr;

#if !defined(DOS)
badArg:
#endif
	return ResultFromScode(MAPI_E_INVALID_PARAMETER);
}


#ifdef WIN16 // Imported inline function
/*
 *	IListedPropID
 *
 *  Purpose
 *		If a tag with ID == PROP_ID(ulPropTag) is listed in lptaga then
 *		the index of tag is returned.  If the tag is not in lptaga then
 *		-1 is returned.
 *
 *	Arguments
 *		ulPropTag	Property tag to locate.
 *		lptaga		Property tag array to search.
 *
 *	Returns		TRUE or FALSE
 */
LONG
IListedPropID( ULONG			ulPropTag,
			   LPSPropTagArray	lptaga)
{
	ULONG FAR	*lpulPTag;

	/* No tag is contained in a NULL list of tags.
	 */
    if (!lptaga)
	{
		return -1;
	}

	/* Mutate ulPropTag to just a PROP_ID.
	 */
    ulPropTag = PROP_ID(ulPropTag);

	for ( lpulPTag = lptaga->aulPropTag + lptaga->cValues
		; --lpulPTag >= lptaga->aulPropTag
		; )
	{
		/* Compare PROP_ID's.
		 */
		if (PROP_ID(*lpulPTag) == ulPropTag)
		{
			return (lpulPTag - lptaga->aulPropTag);
		}
	}

	return -1;
}

/*
 *	FListedPropID
 *
 *  Purpose
 *		Determine if a tag with ID == PROP_ID(ulPropTag) is listed in lptaga.
 *
 *	Arguments
 *		ulPropTag	Property tag to locate.
 *		lptaga		Property tag array to search.
 *
 *	Returns		TRUE or FALSE
 */
BOOL
FListedPropID( ULONG			ulPropTag,
			   LPSPropTagArray	lptaga)
{
	ULONG FAR	*lpulPTag;

	/* No tag is contained in a NULL list of tags.
	 */
    if (!lptaga)
	{
		return FALSE;
	}

	/* Mutate ulPropTag to just a PROP_ID.
	 */
    ulPropTag = PROP_ID(ulPropTag);

	for ( lpulPTag = lptaga->aulPropTag + lptaga->cValues
		; --lpulPTag >= lptaga->aulPropTag
		; )
	{
		/* Compare PROP_ID's.
		 */
		if (PROP_ID(*lpulPTag) == ulPropTag)
		{
			return TRUE;
		}
	}

	return FALSE;
}

/*
 *	FListedPropTAG
 *
 *  Purpose
 *		Determine if a the given ulPropTag is listed in lptaga.
 *
 *	Arguments
 *		ulPropTag	Property tag to locate.
 *		lptaga		Property tag array to search.
 *
 *	Returns		TRUE or FALSE
 */
BOOL
FListedPropTAG( ULONG			ulPropTag,
				LPSPropTagArray	lptaga)
{
	ULONG FAR	*lpulPTag;

	/* No tag is contained in a NULL list of tags.
	 */
    if (!lptaga)
	{
		return FALSE;
	}

	/* Compare the entire prop tag to be sure both ID and TYPE match
	 */
	for ( lpulPTag = lptaga->aulPropTag + lptaga->cValues
		; --lpulPTag >= lptaga->aulPropTag
		; )
	{
		/* Compare PROP_ID's.
		 */
		if (PROP_ID(*lpulPTag) == ulPropTag)
		{
			return TRUE;
		}
	}

	return FALSE;
}


/*
 *	AddProblem
 *
 *  Purpose
 *		Adds a problem to the next available entry of a pre-allocated problem
 *		array.
 *		The pre-allocated problem array must be big enough to have another
 *		problem added.  The caller is responsible for making sure this is
 *		true.
 *
 *	Arguments
 *		lpProblems	Pointer to pre-allocated probelem array.
 *		ulIndex		Index into prop tag/value array of the problem property.
 *		ulPropTag	Prop tag of property which had the problem.
 *		scode		Error code to list for the property.
 *
 *	Returns		TRUE or FALSE
 */
VOID
AddProblem( LPSPropProblemArray	lpProblems,
			ULONG				ulIndex,
			ULONG				ulPropTag,
			SCODE				scode)
{
	if (lpProblems)
	{
		Assert( !IsBadWritePtr( lpProblems->aProblem + lpProblems->cProblem
			  , sizeof(SPropProblem)));
		lpProblems->aProblem[lpProblems->cProblem].ulIndex = ulIndex;
		lpProblems->aProblem[lpProblems->cProblem].ulPropTag = ulPropTag;
		lpProblems->aProblem[lpProblems->cProblem].scode = scode;
		lpProblems->cProblem++;
	}
}

BOOL
FIsExcludedIID( LPCIID lpiidToCheck, LPCIID rgiidExclude, ULONG ciidExclude)
{
	/* Check the obvious (no exclusions).
	 */
	if (!ciidExclude || !rgiidExclude)
	{
		return FALSE;
	}

	/* Check each iid in the list of exclusions.
	 */
	for (; ciidExclude; rgiidExclude++, ciidExclude--)
	{
//		if (IsEqualGUID( lpiidToCheck, rgiidExclude))
		if (!memcmp( lpiidToCheck, rgiidExclude, sizeof(MAPIUID)))
		{
			return TRUE;
		}
	}

	return FALSE;
}


/*
 *	Error/Warning Alert Message Boxes
 */
int			AlertIdsCtx( HWND hwnd,
						 HINSTANCE hinst,
						 UINT idsMsg,
						 LPSTR szComponent,
						 ULONG ulContext,
						 ULONG ulLow,
						 UINT fuStyle);

int
AlertIds(HWND hwnd, HINSTANCE hinst, UINT idsMsg, UINT fuStyle)
{
	return AlertIdsCtx(hwnd, hinst, idsMsg, NULL, 0, 0, fuStyle);
}

int			AlertSzCtx( HWND hwnd,
						LPSTR szMsg,
						LPSTR szComponent,
						ULONG ulContext,
						ULONG ulLow,
						UINT fuStyle);

int
AlertSz(HWND hwnd, LPSTR szMsg, UINT fuStyle)
{
	return AlertSzCtx(hwnd, szMsg, NULL, 0, 0, fuStyle);
}
#endif // WIN16