/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

	oleutil.cpp

Abstract:

	Provides Useful functions for dealing with OLE.

Author:

	Magnus Hedlund (MagnusH)		--

Revision History:

--*/

#include "stdafx.h"
#include "iadm.h"
#include "oleutil.h"
#include "cmultisz.h"
#include "resource.h"

//$-------------------------------------------------------------------
//
//	UpdateChangedMask
//
//	Description:
//
//		Marks a field as changed in the given bit vector
//
//	Parameters:
//
//		pbvChangedProps - points to the bit vector
//		dwBitMask - Bit to turn on. (must have only one bit on)
//
//--------------------------------------------------------------------

static void UpdateChangedMask ( DWORD * pbvChangedProps, DWORD dwBitMask )
{
	if ( pbvChangedProps == NULL ) {
		// Legal, means that the caller doesn't want change tracking.

		return;
	}

	_ASSERT ( dwBitMask != 0 );

	*pbvChangedProps |= dwBitMask;
}

HRESULT LongArrayToVariantArray ( SAFEARRAY * psaLongs, SAFEARRAY ** ppsaVariants )
{
	_ASSERT ( psaLongs );

	HRESULT		hr	= NOERROR;
	long		lLBound	= 0;
	long		lUBound	= 0;
	long		i;
	SAFEARRAYBOUND	bounds;
	SAFEARRAY *	psaVariants;

	*ppsaVariants = NULL;

	_ASSERT ( SafeArrayGetDim ( psaLongs ) == 1 );

	SafeArrayGetLBound ( psaLongs, 1, &lLBound );
	SafeArrayGetUBound ( psaLongs, 1, &lUBound );

	bounds.lLbound = lLBound;
	bounds.cElements = lUBound - lLBound + 1;

	psaVariants = SafeArrayCreate ( VT_VARIANT, 1, &bounds );

	for ( i = lLBound; i <= lUBound; i++ ) {
		VARIANT		var;
		long		lTemp;

		VariantInit ( &var );

		hr = SafeArrayGetElement ( psaLongs, &i, &lTemp );
		if ( FAILED(hr) ) {
			goto Exit;
		}

		V_VT (&var) = VT_I4;
		V_I4 (&var) = lTemp;

		hr = SafeArrayPutElement ( psaVariants, &i, &var );
		if ( FAILED(hr) ) {
			goto Exit;
		}

		VariantClear ( &var );
	}

	*ppsaVariants	= psaVariants;
Exit:
	return hr;
}

HRESULT StringArrayToVariantArray ( SAFEARRAY * psaStrings, SAFEARRAY ** ppsaVariants )
{
	_ASSERT ( psaStrings );

	HRESULT		hr	= NOERROR;
	long		lLBound	= 0;
	long		lUBound	= 0;
	long		i;
	SAFEARRAYBOUND	bounds;
	SAFEARRAY *	psaVariants;

	*ppsaVariants = NULL;

	_ASSERT ( SafeArrayGetDim ( psaStrings ) == 1 );

	SafeArrayGetLBound ( psaStrings, 1, &lLBound );
	SafeArrayGetUBound ( psaStrings, 1, &lUBound );

	bounds.lLbound = lLBound;
	bounds.cElements = lUBound - lLBound + 1;

	psaVariants = SafeArrayCreate ( VT_VARIANT, 1, &bounds );

	for ( i = lLBound; i <= lUBound; i++ ) {
		VARIANT		var;
		CComBSTR	strTemp;

		VariantInit ( &var );

		hr = SafeArrayGetElement ( psaStrings, &i, &strTemp );
		if ( FAILED(hr) ) {
			goto Exit;
		}

		V_VT (&var) = VT_BSTR;
		V_BSTR (&var) = ::SysAllocString ( strTemp );

		hr = SafeArrayPutElement ( psaVariants, &i, &var );
		if ( FAILED(hr) ) {
			goto Exit;
		}

		VariantClear ( &var );
	}

	*ppsaVariants	= psaVariants;
Exit:
	return hr;
}

HRESULT VariantArrayToStringArray ( SAFEARRAY * psaVariants, SAFEARRAY ** ppsaStrings )
{
	_ASSERT ( psaVariants );

	HRESULT		hr	= NOERROR;
	long		lLBound	= 0;
	long		lUBound	= 0;
	long		i;
	SAFEARRAYBOUND	bounds;
	SAFEARRAY *	psaStrings;

	_ASSERT ( SafeArrayGetDim ( psaVariants ) == 1 );
	
	*ppsaStrings = NULL;

	SafeArrayGetLBound ( psaVariants, 1, &lLBound );
	SafeArrayGetUBound ( psaVariants, 1, &lUBound );

	bounds.lLbound = lLBound;
	bounds.cElements = lUBound - lLBound + 1;

	psaStrings = SafeArrayCreate ( VT_BSTR, 1, &bounds );

	for ( i = lLBound; i <= lUBound; i++ ) {
		VARIANT		var;
		CComBSTR	strTemp;

		VariantInit ( &var );

		hr = SafeArrayGetElement ( psaVariants, &i, &var );
		if ( FAILED(hr) ) {
			goto Exit;
		}

		strTemp = V_BSTR (&var);

		hr = SafeArrayPutElement ( psaStrings, &i, strTemp );
		if ( FAILED(hr) ) {
			goto Exit;
		}

		VariantClear (&var);
	}

	*ppsaStrings = psaStrings;
Exit:
	return hr;
}

//$-------------------------------------------------------------------
//
//	StdPropertyGet < BSTR, long, DWORD, DATE >
//
//	Description:
//
//		Performs a default Property Get on a BSTR, long, DWORD or 
//		Ole DATE.
//
//	Parameters:
//
//		Property	- The property to get.
//		pOut		- The resulting copy.
//
//	Returns:
//
//		E_POINTER		- invalid arguments
//		E_OUTOFMEMORY	- Not enough memory to copy
//		NOERROR			- success.
//
//--------------------------------------------------------------------

HRESULT StdPropertyGet ( const BSTR strProperty, BSTR * pstrOut )
{
	TraceQuietEnter ( "StdPropertyGet <BSTR>" );

	_ASSERT ( pstrOut != NULL );
	_ASSERT ( IS_VALID_OUT_PARAM ( pstrOut ) );

	if ( pstrOut == NULL ) {
		FatalTrace ( 0, "Bad return pointer" );
		return E_POINTER;
	}

	*pstrOut = NULL;

	if ( strProperty == NULL ) {

		// If the property is NULL, use a blank string:
		*pstrOut = ::SysAllocString ( _T("") );
	}
	else {
		_ASSERT ( IS_VALID_STRING ( strProperty ) );

		// Copy the property into the result:
		*pstrOut = ::SysAllocString ( strProperty );
	}

	if ( *pstrOut == NULL ) {

		// Allocation failed.
		FatalTrace ( 0, "Out of memory" );

		return E_OUTOFMEMORY;
	}

	return NOERROR;
}

HRESULT StdPropertyGet ( long lProperty, long * plOut )
{
	TraceQuietEnter ( "StdPropertyGet <long>" );

	_ASSERT ( plOut != NULL );
	_ASSERT ( IS_VALID_OUT_PARAM ( plOut ) );

	if ( plOut == NULL ) {
		FatalTrace ( 0, "Bad return pointer" );

		return E_POINTER;
	}

	*plOut = lProperty;
	return NOERROR;
}

HRESULT StdPropertyGet ( DATE dateProperty, DATE * pdateOut )
{
	TraceQuietEnter ( "StdPropertyGet <DATE>" );

	_ASSERT ( pdateOut != NULL );
	_ASSERT ( IS_VALID_OUT_PARAM ( pdateOut ) );

	if ( pdateOut == NULL ) {
		FatalTrace ( 0, "Bad return pointer" );

		return E_POINTER;
	}
	
	*pdateOut = dateProperty;
	return NOERROR;
}

HRESULT StdPropertyGet ( const CMultiSz * pmszProperty, SAFEARRAY ** ppsaStrings )
{
	TraceFunctEnter ( "StdPropertyGet <MULTI_SZ>" );

	_ASSERT ( pmszProperty );
	_ASSERT ( IS_VALID_OUT_PARAM ( ppsaStrings ) );

	HRESULT		hr	= NOERROR;

	*ppsaStrings = pmszProperty->ToSafeArray ( );

	if ( *ppsaStrings == NULL ) {
		hr = E_OUTOFMEMORY;
		goto Exit;
	}

Exit:
	TraceFunctLeave ();
	return hr;
}

HRESULT	StdPropertyGetBit ( DWORD bvBitVector, DWORD dwBit, BOOL * pfOut )
{
	_ASSERT ( IS_VALID_OUT_PARAM ( pfOut ) );

	if ( !pfOut ) {
		return E_POINTER;
	}

	*pfOut	= GetBitFlag ( bvBitVector, dwBit );

	return NOERROR;
}

//$-------------------------------------------------------------------
//
//	StdPropertyPut <BSTR, long, DWORD or DATE>
//
//	Description:
//
//		Performs a default Property Put on a BSTR, long, DWORD or
//		Ole date.
//
//	Parameters:
//
//		pProperty	- The property to put.
//		New			- The new value.
//		pbvChangedProps [optional] - Bit Vector which holds which
//				properties have changed.
//		dwBitMask [optional] - This property's bitmask for the 
//				changed bit vector.
//
//	Returns:
//
//		E_POINTER - invalid arguments
//		E_OUTOFMEMORY - Not enough memory to copy
//		NOERROR - success.
//
//--------------------------------------------------------------------

HRESULT StdPropertyPut ( 
	BSTR * pstrProperty, 
	const BSTR strNew, 
	DWORD * pbvChangedProps, // = NULL 
	DWORD dwBitMask // = 0 
	)
{
	TraceQuietEnter ( "StdPropertyPut <BSTR>" );

	// Validate Parameters:
	_ASSERT ( pstrProperty != NULL );
	_ASSERT ( IS_VALID_OUT_PARAM ( pstrProperty ) );

	_ASSERT ( strNew != NULL );
	_ASSERT ( IS_VALID_STRING ( strNew ) );

	if ( pstrProperty == NULL ) {
		FatalTrace ( 0, "Bad property pointer" );
		return E_POINTER;
	}

	if ( strNew == NULL ) {
		FatalTrace ( 0, "Bad pointer" );
		return E_POINTER;
	}

	HRESULT	hr	= NOERROR;
	BSTR	strCopy = NULL;

	// Copy the new string:
	strCopy = ::SysAllocString ( strNew );

	if ( strCopy == NULL ) {
		hr = E_OUTOFMEMORY;

		FatalTrace ( 0, "Out of memory" );
		goto Error;
	}

	// Update the changed bit, if necessary:
	if ( *pstrProperty && lstrcmp ( *pstrProperty, strCopy ) != 0 ) {
		UpdateChangedMask ( pbvChangedProps, dwBitMask );
	}

	// Replace the old property with the new one.
	SAFE_FREE_BSTR ( *pstrProperty );

	*pstrProperty = strCopy;

Error:
	return hr;
}

HRESULT StdPropertyPut ( 
	long * plProperty, 
	long lNew, 
	DWORD * pbvChangedProps, // = NULL 
	DWORD dwBitMask // = 0
	)
{
	TraceQuietEnter ( "StdPropertyPut <long>" );

	_ASSERT ( plProperty != NULL );
	_ASSERT ( IS_VALID_OUT_PARAM ( plProperty ) );

	if ( plProperty == NULL ) {
		FatalTrace ( 0, "Bad pointer" );
		return E_POINTER;
	}

	if ( *plProperty != lNew ) {
		UpdateChangedMask ( pbvChangedProps, dwBitMask );
	}

	*plProperty = lNew;
	return NOERROR;
}

HRESULT StdPropertyPut ( 
	DATE * pdateProperty, 
	DATE dateNew, 
	DWORD * pbvChangedProps, // = NULL
	DWORD dwBitMask // = 0
	)
{
	TraceQuietEnter ( "StdPropertyPut <DATE>" );

	_ASSERT ( pdateProperty != NULL );
	_ASSERT ( IS_VALID_OUT_PARAM ( pdateProperty ) );

	if ( pdateProperty == NULL ) {
		FatalTrace ( 0, "Bad pointer" );
		return E_POINTER;
	}

	if ( *pdateProperty != dateNew ) {
		UpdateChangedMask ( pbvChangedProps, dwBitMask );
	}

	*pdateProperty = dateNew;
	return NOERROR;
}

HRESULT StdPropertyPut ( CMultiSz * pmszProperty, SAFEARRAY * psaStrings, DWORD * pbvChangedProps, DWORD dwBitMask )
{
	TraceFunctEnter ( "StdPropertyPut <MULTI_SZ>" );

	_ASSERT ( IS_VALID_IN_PARAM ( psaStrings ) );
	_ASSERT ( IS_VALID_OUT_PARAM ( pmszProperty ) );

	if ( psaStrings == NULL ) {
		FatalTrace ( 0, "Bad return pointer" );

		TraceFunctLeave ();
		return E_POINTER;
	}

	HRESULT		hr	= NOERROR;

	pmszProperty->FromSafeArray ( psaStrings );

	if ( !*pmszProperty ) {
		hr = E_OUTOFMEMORY;
		goto Exit;
	}

	// Don't want to deal with comparing these properties:
	UpdateChangedMask ( pbvChangedProps, dwBitMask );

Exit:
	TraceFunctLeave ();
	return hr;
}

HRESULT	StdPropertyPutBit ( DWORD * pbvBitVector, DWORD dwBit, BOOL fIn )
{
	_ASSERT ( IS_VALID_OUT_PARAM ( pbvBitVector ) );
	_ASSERT ( dwBit );

	SetBitFlag ( pbvBitVector, dwBit, fIn );

	return NOERROR;
}

//$-------------------------------------------------------------------
//
//	PV_MaxChars
//
//	Description:
//
//		Validates a string to make sure it's not too long.
//
//	Parameters:
//
//		strProperty - the string to check
//		nMaxChars - the maximum number of characters in the string,
//			not including the NULL terminator.
//
//	Returns:
//
//		FALSE if the string is too long.
//
//--------------------------------------------------------------------

BOOL PV_MaxChars ( const BSTR strProperty, DWORD nMaxChars )
{
	TraceQuietEnter ( "PV_MaxChars" );

	_ASSERT ( strProperty != NULL );
	_ASSERT ( IS_VALID_STRING ( strProperty ) );

	_ASSERT ( nMaxChars > 0 );

	if ( strProperty == NULL ) {
		// This error should be caught somewhere else.
		return TRUE;
	}

	if ( (DWORD) lstrlen ( strProperty ) > nMaxChars ) {
		ErrorTrace ( 0, "String too long" );
		return FALSE;
	}

	return TRUE;
}

//$-------------------------------------------------------------------
//
//	PV_MinMax <int, dword>
//
//	Description:
//
//		Makes sure a property is within a given range.
//
//	Parameters:
//
//		nProperty - the value to test
//		nMin - The minimum value the property could have
//		nMax - The maximum value the property could have
//
//	Returns:
//
//		TRUE if the property is in the range (inclusive).
//
//--------------------------------------------------------------------

BOOL PV_MinMax ( int nProperty, int nMin, int nMax )
{
	TraceQuietEnter ( "PV_MinMax" );

	_ASSERT ( nMin <= nMax );

	if ( nProperty < nMin || nProperty > nMax ) {
		ErrorTrace ( 0, "Integer out of range" );
		return FALSE;
	}
	return TRUE;
}

BOOL PV_MinMax ( DWORD dwProperty, DWORD dwMin, DWORD dwMax )
{
	TraceQuietEnter ( "PV_MinMax" );

	_ASSERT ( dwMin <= dwMax );

	if ( dwProperty < dwMin || dwProperty > dwMax ) {

		ErrorTrace ( 0, "Dword out of range" );
		return FALSE;
	}
	return TRUE;
}

BOOL PV_Boolean		( BOOL fProperty )
{
	TraceQuietEnter ( "PV_Boolean" );

	if ( fProperty != TRUE && fProperty != FALSE ) {

		ErrorTrace ( 0, "Boolean property is not true or false" );
		return FALSE;
	}

	return TRUE;
}

//$-------------------------------------------------------------------
//
//	StdPropertyGetIDispatch
//
//	Description:
//
//		Gets a IDispatch pointer for the given cLSID
//
//	Parameters:
//
//		clsid		- OLE CLSID of the object
//		ppIDipsatch	- the IDispatch pointer to that object.
//
//	Returns:
//
//		E_POINTER	- invalid argument
//		NOERROR		- Success
//		Others - defined by CoCreateInstance.
//
//--------------------------------------------------------------------

HRESULT StdPropertyGetIDispatch ( 
	REFCLSID clsid, 
	IDispatch ** ppIDispatch 
	)
{
	TraceFunctEnter ( "StdPropertyGetIDispatch" );

	CComPtr<IDispatch>	pNewIDispatch;
	HRESULT				hr = NOERROR;

	_ASSERT ( ppIDispatch );

	if ( ppIDispatch == NULL ) {
		FatalTrace ( 0, "Bad return pointer" );
		TraceFunctLeave ();
		return E_POINTER;
	}

	*ppIDispatch = NULL;

	hr = ::CoCreateInstance ( 
		clsid,
		NULL, 
		CLSCTX_ALL, 
		IID_IDispatch,
		(void **) &pNewIDispatch
		);

	if ( FAILED (hr) ) {
		DebugTraceX ( 0, "CoCreate(IDispatch) failed %x", hr );
		FatalTrace ( 0, "Failed to create IDispatch" );
		goto Exit;
	}

	*ppIDispatch = pNewIDispatch;
	pNewIDispatch->AddRef ();

Exit:
	TraceFunctLeave ();
	return hr;

	// Destructor releases pNewIDispatch
}

//$-------------------------------------------------------------------
//
//	InetAddressToString
//
//	Description:
//
//		Converts a DWORD with an ip address to a string in the form
//		"xxx.xxx.xxx.xxx"
//
//	Parameters:
//
//		dwAddress	- The address to convert
//		wszAddress	- The resulting string
//		cAddress	- The maximum size of the resulting string
//
//	Returns:
//
//		TRUE if succeeded, FALSE otherwise.
//
//--------------------------------------------------------------------

BOOL InetAddressToString ( DWORD dwAddress, LPWSTR wszAddress, DWORD cAddress )
{
	TraceFunctEnter ( "InetAddressToString" );

	_ASSERT ( wszAddress );

	if ( wszAddress == NULL ) {
		FatalTrace ( 0, "Bad pointer" );
		TraceFunctLeave ();
		return FALSE;
	}

	struct in_addr	addr;
	LPSTR			szAnsiAddress;
	DWORD			cchCopied;

	addr.s_addr = dwAddress;

	szAnsiAddress = inet_ntoa ( addr );

	if ( szAnsiAddress == NULL ) {
		ErrorTraceX ( 0, "inet_ntoa failed: %x", GetLastError() );
		TraceFunctLeave ();
		return FALSE;
	}

	cchCopied = MultiByteToWideChar ( 
		CP_ACP, 
		MB_PRECOMPOSED,
		szAnsiAddress,
		-1,
		wszAddress,
		cAddress
		);

	if ( cchCopied == 0 ) {
		ErrorTraceX ( 0, "MultiByteToWideChar failed: %x", GetLastError() );
		TraceFunctLeave ();
		return FALSE;
	}

	TraceFunctLeave ();
	return TRUE;
}

//$-------------------------------------------------------------------
//
//	StringToInetAddress
//
//	Description:
//
//		Converts a string in the form "xxx.xxx.xxx.xxx" to a DWORD
//		IP Address.
//
//	Parameters:
//
//		wszAddress	- The string to convert
//		pdwAddress	- The resulting address
//
//	Returns:
//
//		TRUE if succeeded, FALSE otherwise.
//
//--------------------------------------------------------------------

BOOL StringToInetAddress ( LPCWSTR wszAddress, DWORD * pdwAddress )
{
	TraceFunctEnter ( "StringToInetAddress" );

	_ASSERT ( wszAddress );
	_ASSERT ( pdwAddress );

	if ( wszAddress == NULL ) {
		FatalTrace ( 0, "Bad pointer" );
		TraceFunctLeave ();
		return FALSE;
	}

	if ( pdwAddress == NULL ) {
		FatalTrace ( 0, "Bad return pointer" );
		TraceFunctLeave ();
		return FALSE;
	}

	char	szAnsiAddress[100];
	DWORD	cchCopied;

	*pdwAddress = 0;

	cchCopied = WideCharToMultiByte ( 
		CP_ACP, 
		0, 
		wszAddress, 
		-1, 
		szAnsiAddress, 
		sizeof ( szAnsiAddress ),
		NULL,
		NULL
		);

	if ( cchCopied == 0 ) {
		ErrorTraceX ( 0, "MultiByteToWideChar failed: %x", GetLastError() );
		TraceFunctLeave ();
		return FALSE;
	}

	*pdwAddress = inet_addr ( szAnsiAddress );

	if ( !*pdwAddress ) {
		ErrorTraceX ( 0, "inet_addr failed: %x", GetLastError () );
	}

	TraceFunctLeave ();
	return TRUE;
}