/**********************************************************************/
/**                       Microsoft Windows/NT                       **/
/**                Copyright(c) Microsoft Corporation, 1997 - 1999 **/
/**********************************************************************/

/*
    spddb.h

    FILE HISTORY:
        
*/

#include "stdafx.h"
#include "DynamLnk.h"
#include "spddb.h"
#include "spdutil.h"

#include "security.h"
#include "lm.h"
#include "service.h"

#define AVG_PREFERRED_ENUM_COUNT       40

#define DEFAULT_SECURITY_PKG    _T("negotiate")
#define NT_SUCCESS(Status)      ((NTSTATUS)(Status) >= 0)
#define STATUS_SUCCESS          ((NTSTATUS)0x00000000L)

// internal functions
BOOL    IsUserAdmin(LPCTSTR pszMachine, PSID    AccountSid);
BOOL    LookupAliasFromRid(LPWSTR TargetComputer, DWORD Rid, LPWSTR Name, PDWORD cchName);
DWORD   ValidateDomainAccount(IN CString Machine, IN CString UserName, IN CString Domain, OUT PSID * AccountSid);
NTSTATUS ValidatePassword(IN LPCWSTR UserName, IN LPCWSTR Domain, IN LPCWSTR Password);
DWORD   GetCurrentUser(CString & strAccount);


template <class T>
void
FreeItemsAndEmptyArray (
    T& array)
{
	for (int i = 0; i < array.GetSize(); i++)
	{
		delete array.GetAt(i);
	}
	array.RemoveAll();
}

DWORD GetCurrentUser(CString & strAccount)
{
    LPBYTE pBuf;

    NET_API_STATUS status = NetWkstaUserGetInfo(NULL, 1, &pBuf);
    if (status == NERR_Success)
    {
        strAccount.Empty();

        WKSTA_USER_INFO_1 * pwkstaUserInfo = (WKSTA_USER_INFO_1 *) pBuf;
 
        strAccount = pwkstaUserInfo->wkui1_logon_domain;
        strAccount += _T("\\");
        strAccount += pwkstaUserInfo->wkui1_username;

        NetApiBufferFree(pBuf);
    }

    return (DWORD) status;
}

/*!--------------------------------------------------------------------------
    IsAdmin
        Connect to the remote machine as administrator with user-supplied
        credentials to see if the user has admin priviledges

        Returns
            TRUE - the user has admin rights
            FALSE - if user doesn't
    Author: EricDav, KennT
 ---------------------------------------------------------------------------*/
DWORD IsAdmin(LPCTSTR szMachineName, LPCTSTR szAccount, LPCTSTR szPassword, BOOL * pIsAdmin)
{
    CString         stAccount;
    CString         stDomain;
    CString         stUser;
    CString         stMachineName;
    DWORD           dwStatus;
    BOOL            fIsAdmin = FALSE;

    // get the current user info
    if (szAccount == NULL)
    {
        GetCurrentUser(stAccount);
    }
    else
    {
        stAccount = szAccount;
    }
    
    // separate the user and domain
    int nPos = stAccount.Find(_T("\\"));
    stDomain = stAccount.Left(nPos);
    stUser = stAccount.Right(stAccount.GetLength() - nPos - 1);

    // build the machine string
    stMachineName = szMachineName;
    if ( stMachineName.Left(2) != TEXT( "\\\\" ) )
    {
        stMachineName = TEXT( "\\\\" ) + stMachineName;
    }

    // validate the domain account and get the sid 
    PSID connectSid;

    dwStatus = ValidateDomainAccount( stMachineName, stUser, stDomain, &connectSid );
    if ( dwStatus != ERROR_SUCCESS  ) 
    {
        goto Error;
    }

    // if a password was supplied, is it correct?
    if (szPassword)
    {
        dwStatus = ValidatePassword( stUser, stDomain, szPassword );

        if ( dwStatus != SEC_E_OK ) 
        {
            switch ( dwStatus ) 
            {
                case SEC_E_LOGON_DENIED:
                    dwStatus = ERROR_INVALID_PASSWORD;
                    break;

                case SEC_E_INVALID_HANDLE:
                    dwStatus = ERROR_INTERNAL_ERROR;
                    break;

                default:
                    dwStatus = ERROR_INTERNAL_ERROR;
                    break;
            } // end of switch

            goto Error;

        } // Did ValidatePassword succeed?
    }

    // now check the machine to see if this account has admin access
    fIsAdmin = IsUserAdmin( stMachineName, connectSid );

Error:
    if (pIsAdmin)
        *pIsAdmin = fIsAdmin;

    return dwStatus;
}


BOOL
IsUserAdmin(LPCTSTR pszMachine,
            PSID    AccountSid)

/*++

Routine Description:

    Determine if the specified account is a member of the local admin's group

Arguments:

    AccountSid - pointer to service account Sid

Return Value:

    True if member

--*/

{
    NET_API_STATUS status;
    DWORD count;
    WCHAR adminGroupName[UNLEN+1];
    DWORD cchName = UNLEN;
    PLOCALGROUP_MEMBERS_INFO_0 grpMemberInfo;
    PLOCALGROUP_MEMBERS_INFO_0 pInfo;
    DWORD entriesRead;
    DWORD totalEntries;
    DWORD_PTR resumeHandle = NULL;
    DWORD bufferSize = 128;
    BOOL foundEntry = FALSE;

    // get the name of the admin's group

    if (!LookupAliasFromRid(NULL,
                            DOMAIN_ALIAS_RID_ADMINS,
                            adminGroupName,
                            &cchName)) {
        return(FALSE);
    }

    // get the Sids of the members of the admin's group

    do 
    {
        status = NetLocalGroupGetMembers(pszMachine,
                                         adminGroupName,
                                         0,             // level 0 - just the Sid
                                         (LPBYTE *)&grpMemberInfo,
                                         bufferSize,
                                         &entriesRead,
                                         &totalEntries,
                                         &resumeHandle);

        bufferSize *= 2;
        if ( status == ERROR_MORE_DATA ) 
        {
            // we got some of the data but I want it all; free this buffer and
            // reset the context handle for the API

            NetApiBufferFree( grpMemberInfo );
            resumeHandle = NULL;
        }
    } while ( status == NERR_BufTooSmall || status == ERROR_MORE_DATA );

    if ( status == NERR_Success ) 
    {
        // loop through the members of the admin group, comparing the supplied
        // Sid to that of the group members' Sids

        for ( count = 0, pInfo = grpMemberInfo; count < totalEntries; ++count, ++pInfo ) 
        {
            if ( EqualSid( AccountSid, pInfo->lgrmi0_sid )) 
            {
                foundEntry = TRUE;
                break;
            }
        }

        NetApiBufferFree( grpMemberInfo );
    }

    return foundEntry;
}

//
//
//

BOOL
LookupAliasFromRid(
    LPWSTR TargetComputer,
    DWORD Rid,
    LPWSTR Name,
    PDWORD cchName
    )
{
    SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
    SID_NAME_USE snu;
    PSID pSid;
    WCHAR DomainName[DNLEN+1];
    DWORD cchDomainName = DNLEN;
    BOOL bSuccess = FALSE;

    //
    // Sid is the same regardless of machine, since the well-known
    // BUILTIN domain is referenced.
    //

    if(AllocateAndInitializeSid(&sia,
                                2,
                                SECURITY_BUILTIN_DOMAIN_RID,
                                Rid,
                                0, 0, 0, 0, 0, 0,
                                &pSid)) {

        bSuccess = LookupAccountSidW(TargetComputer,
                                     pSid,
                                     Name,
                                     cchName,
                                     DomainName,
                                     &cchDomainName,
                                     &snu);

        FreeSid(pSid);
    }

    return bSuccess;
} // LookupAliasFromRid

DWORD
ValidateDomainAccount(
    IN CString Machine,
    IN CString UserName,
    IN CString Domain,
    OUT PSID * AccountSid
    )

/*++

Routine Description:

    For the given credentials, look up the account SID for the specified
    domain. As a side effect, the Sid is stored in theData->m_Sid.

Arguments:

    pointers to strings that describe the user name, domain name, and password

    AccountSid - address of pointer that receives the SID for this user

Return Value:

    TRUE if everything validated ok.

--*/

{
    DWORD dwStatus = ERROR_SUCCESS;
    DWORD dwSidSize = 128;
    DWORD dwDomainNameSize = 128;
    LPWSTR pwszDomainName;
    SID_NAME_USE SidType;
    CString domainAccount;
    PSID accountSid;

    domainAccount = Domain + _T("\\") + UserName;

    do {
        // Attempt to allocate a buffer for the SID. Note that apparently in the
        // absence of any error theData->m_Sid is freed only when theData goes
        // out of scope.

        accountSid = LocalAlloc( LMEM_FIXED, dwSidSize );
        pwszDomainName = (LPWSTR) LocalAlloc( LMEM_FIXED, dwDomainNameSize * sizeof(WCHAR) );

        // Was space allocated for the SID and domain name successfully?

        if ( accountSid == NULL || pwszDomainName == NULL ) {
            if ( accountSid != NULL ) {
                LocalFree( accountSid );
            }

            if ( pwszDomainName != NULL ) {
                LocalFree( pwszDomainName );
            }

            //FATALERR( IDS_ERR_NOT_ENOUGH_MEMORY, GetLastError() );    // no return
            break;
        }

        // Attempt to Retrieve the SID and domain name. If LookupAccountName failes
        // because of insufficient buffer size(s) dwSidSize and dwDomainNameSize
        // will be set correctly for the next attempt.

        if ( !LookupAccountName( Machine,
                                 domainAccount,
                                 accountSid,
                                 &dwSidSize,
                                 pwszDomainName,
                                 &dwDomainNameSize,
                                 &SidType ))
        {
            // free the Sid buffer and find out why we failed
            LocalFree( accountSid );

            dwStatus = GetLastError();
        }

        // domain name isn't needed at any time
        LocalFree( pwszDomainName );
        pwszDomainName = NULL;

    } while ( dwStatus == ERROR_INSUFFICIENT_BUFFER );

    if ( dwStatus == ERROR_SUCCESS ) {
        *AccountSid = accountSid;
    }

    return dwStatus;
} // ValidateDomainAccount

NTSTATUS
ValidatePassword(
    IN LPCWSTR UserName,
    IN LPCWSTR Domain,
    IN LPCWSTR Password
    )
/*++

Routine Description:

    Uses SSPI to validate the specified password

Arguments:

    UserName - Supplies the user name

    Domain - Supplies the user's domain

    Password - Supplies the password

Return Value:

    TRUE if the password is valid.

    FALSE otherwise.

--*/

{
    SECURITY_STATUS SecStatus;
    SECURITY_STATUS AcceptStatus;
    SECURITY_STATUS InitStatus;
    CredHandle ClientCredHandle;
    CredHandle ServerCredHandle;
    BOOL ClientCredAllocated = FALSE;
    BOOL ServerCredAllocated = FALSE;
    CtxtHandle ClientContextHandle;
    CtxtHandle ServerContextHandle;
    TimeStamp Lifetime;
    ULONG ContextAttributes;
    PSecPkgInfo PackageInfo = NULL;
    ULONG ClientFlags;
    ULONG ServerFlags;
    SEC_WINNT_AUTH_IDENTITY_W AuthIdentity;

    SecBufferDesc NegotiateDesc;
    SecBuffer NegotiateBuffer;

    SecBufferDesc ChallengeDesc;
    SecBuffer ChallengeBuffer;

    SecBufferDesc AuthenticateDesc;
    SecBuffer AuthenticateBuffer;

    SecBufferDesc *pChallengeDesc      = NULL;
    CtxtHandle *  pClientContextHandle = NULL;
    CtxtHandle *  pServerContextHandle = NULL;

    AuthIdentity.User = (LPWSTR)UserName;
    AuthIdentity.UserLength = lstrlenW(UserName);
    AuthIdentity.Domain = (LPWSTR)Domain;
    AuthIdentity.DomainLength = lstrlenW(Domain);
    AuthIdentity.Password = (LPWSTR)Password;
    AuthIdentity.PasswordLength = lstrlenW(Password);
    AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

    NegotiateBuffer.pvBuffer = NULL;
    ChallengeBuffer.pvBuffer = NULL;
    AuthenticateBuffer.pvBuffer = NULL;

    //
    // Get info about the security packages.
    //

    SecStatus = QuerySecurityPackageInfo( DEFAULT_SECURITY_PKG, &PackageInfo );

    if ( SecStatus != STATUS_SUCCESS ) {
        goto error_exit;
    }

    //
    // Acquire a credential handle for the server side
    //
    SecStatus = AcquireCredentialsHandle(
                    NULL,
                    DEFAULT_SECURITY_PKG,
                    SECPKG_CRED_INBOUND,
                    NULL,
                    &AuthIdentity,
                    NULL,
                    NULL,
                    &ServerCredHandle,
                    &Lifetime );

    if ( SecStatus != STATUS_SUCCESS ) {
        goto error_exit;
    }
    ServerCredAllocated = TRUE;

    //
    // Acquire a credential handle for the client side
    //

    SecStatus = AcquireCredentialsHandle(
                    NULL,           // New principal
                    DEFAULT_SECURITY_PKG,
                    SECPKG_CRED_OUTBOUND,
                    NULL,
                    &AuthIdentity,
                    NULL,
                    NULL,
                    &ClientCredHandle,
                    &Lifetime );

    if ( SecStatus != STATUS_SUCCESS ) {
        goto error_exit;
    }
    ClientCredAllocated = TRUE;

    NegotiateBuffer.pvBuffer = LocalAlloc( 0, PackageInfo->cbMaxToken ); // [CHKCHK] check or allocate this earlier //
    if ( NegotiateBuffer.pvBuffer == NULL ) {
        SecStatus = SEC_E_INSUFFICIENT_MEMORY;
        goto error_exit;
    }

    ChallengeBuffer.pvBuffer = LocalAlloc( 0, PackageInfo->cbMaxToken ); // [CHKCHK]
    if ( ChallengeBuffer.pvBuffer == NULL ) {
        SecStatus = SEC_E_INSUFFICIENT_MEMORY;
        goto error_exit;
    }

    do {

        //
        // Get the NegotiateMessage (ClientSide)
        //

        NegotiateDesc.ulVersion = 0;
        NegotiateDesc.cBuffers = 1;
        NegotiateDesc.pBuffers = &NegotiateBuffer;

        NegotiateBuffer.BufferType = SECBUFFER_TOKEN;
        NegotiateBuffer.cbBuffer = PackageInfo->cbMaxToken;

        ClientFlags = 0; // ISC_REQ_MUTUAL_AUTH | ISC_REQ_REPLAY_DETECT; // [CHKCHK] 0

        InitStatus = InitializeSecurityContext(
                         &ClientCredHandle,
                         pClientContextHandle, // (NULL on the first pass, partially formed ctx on the next)
                         NULL,                 // [CHKCHK] szTargetName
                         ClientFlags,
                         0,                    // Reserved 1
                         SECURITY_NATIVE_DREP,
                         pChallengeDesc,       // (NULL on the first pass)
                         0,                    // Reserved 2
                         &ClientContextHandle,
                         &NegotiateDesc,
                         &ContextAttributes,
                         &Lifetime );

        // BUGBUG - the following call to NT_SUCCESS should be replaced with something.

        if ( !NT_SUCCESS(InitStatus) ) {
            SecStatus = InitStatus;
            goto error_exit;
        }

        // ValidateBuffer( &NegotiateDesc ) // [CHKCHK]

        pClientContextHandle = &ClientContextHandle;

        //
        // Get the ChallengeMessage (ServerSide)
        //

        NegotiateBuffer.BufferType |= SECBUFFER_READONLY;
        ChallengeDesc.ulVersion = 0;
        ChallengeDesc.cBuffers = 1;
        ChallengeDesc.pBuffers = &ChallengeBuffer;

        ChallengeBuffer.cbBuffer = PackageInfo->cbMaxToken;
        ChallengeBuffer.BufferType = SECBUFFER_TOKEN;

        ServerFlags = ASC_REQ_ALLOW_NON_USER_LOGONS; // ASC_REQ_EXTENDED_ERROR; [CHKCHK]

        AcceptStatus = AcceptSecurityContext(
                        &ServerCredHandle,
                        pServerContextHandle,   // (NULL on the first pass)
                        &NegotiateDesc,
                        ServerFlags,
                        SECURITY_NATIVE_DREP,
                        &ServerContextHandle,
                        &ChallengeDesc,
                        &ContextAttributes,
                        &Lifetime );


        // BUGBUG - the following call to NT_SUCCESS should be replaced with something.

        if ( !NT_SUCCESS(AcceptStatus) ) {
            SecStatus = AcceptStatus;
            goto error_exit;
        }

        // ValidateBuffer( &NegotiateDesc ) // [CHKCHK]

        pChallengeDesc = &ChallengeDesc;
        pServerContextHandle = &ServerContextHandle;


    } while ( AcceptStatus == SEC_I_CONTINUE_NEEDED ); // || InitStatus == SEC_I_CONTINUE_NEEDED );

error_exit:
    if (ServerCredAllocated) {
        FreeCredentialsHandle( &ServerCredHandle );
    }
    if (ClientCredAllocated) {
        FreeCredentialsHandle( &ClientCredHandle );
    }

    //
    // Final Cleanup
    //

    if ( NegotiateBuffer.pvBuffer != NULL ) {
        (VOID) LocalFree( NegotiateBuffer.pvBuffer );
    }

    if ( ChallengeBuffer.pvBuffer != NULL ) {
        (VOID) LocalFree( ChallengeBuffer.pvBuffer );
    }

    if ( AuthenticateBuffer.pvBuffer != NULL ) {
        (VOID) LocalFree( AuthenticateBuffer.pvBuffer );
    }
    return(SecStatus);
} // ValidatePassword


DEBUG_DECLARE_INSTANCE_COUNTER(CSpdInfo);

CSpdInfo::CSpdInfo() :
	  m_cRef(1)
{
        m_Init=0;
        m_Active=0;
	DEBUG_INCREMENT_INSTANCE_COUNTER(CSpdInfo);
}

CSpdInfo::~CSpdInfo()
{
	DEBUG_DECREMENT_INSTANCE_COUNTER(CSpdInfo);
	CSingleLock cLock(&m_csData);
	
	cLock.Lock();

	//Convert the data to our internal data structure
	FreeItemsAndEmptyArray(m_arrayFilters);
	FreeItemsAndEmptyArray(m_arraySpecificFilters);
	FreeItemsAndEmptyArray(m_arrayMmFilters);
	FreeItemsAndEmptyArray(m_arrayMmSpecificFilters);
	FreeItemsAndEmptyArray(m_arrayMmPolicies);
	FreeItemsAndEmptyArray(m_arrMmAuthMethods);
	FreeItemsAndEmptyArray(m_arrayMmSAs);
	FreeItemsAndEmptyArray(m_arrayQmSAs);
	FreeItemsAndEmptyArray(m_arrayQmPolicies);

	cLock.Unlock();

}

// Although this object is not a COM Interface, we want to be able to
// take advantage of recounting, so we have basic addref/release/QI support
IMPLEMENT_ADDREF_RELEASE(CSpdInfo)

IMPLEMENT_SIMPLE_QUERYINTERFACE(CSpdInfo, ISpdInfo)

HRESULT CSpdInfo::SetComputerName(LPTSTR pszName)
{
	m_stMachineName = pszName;
	return S_OK;
}

HRESULT CSpdInfo::GetComputerName(CString * pstName)
{
	Assert(pstName);

	if (NULL == pstName)
		return E_INVALIDARG;

	
	*pstName = m_stMachineName;

	return S_OK;
	
}


//Call the SPD to enum policies and put it to our array
HRESULT CSpdInfo::InternalEnumMmAuthMethods(
						CMmAuthMethodsArray * pArray,
						DWORD dwPreferredNum /* = 0 by default get all entries*/)
{
	Assert(pArray);

	HRESULT		 hr = hrOK;
	PMM_AUTH_METHODS   pAuths = NULL;

	DWORD	dwNumAuths = 0;
	DWORD	dwTemp = 0;
	DWORD	dwResumeHandle = 0;
	
	FreeItemsAndEmptyArray(*pArray);

	do
	{
		dwTemp = 0;
		CWRg(::EnumMMAuthMethods(
					(LPTSTR)(LPCTSTR)m_stMachineName,
					&pAuths,
					dwPreferredNum,
                    &dwTemp,
                    &dwResumeHandle
                    ));
		
		
		pArray->SetSize(dwNumAuths + dwTemp);
		for (int i = 0; i < (int)dwTemp; i++)
		{
			CMmAuthMethods * pAuth = new CMmAuthMethods;
			if (NULL == pAuth)
			{
				hr = E_OUTOFMEMORY;
				break;
			}

			*pAuth = *(pAuths + i);
			(*pArray)[dwNumAuths + i] = pAuth;
		}
		dwNumAuths += dwTemp;

		if (pAuths)
		{
			SPDApiBufferFree(pAuths);
		}
		
	}while (TRUE);  
	// it will automatically break out from the loop when SPD returns ERROR_NO_DATA

Error:
	//this particular error is because we don't have any data. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
	
}


HRESULT CSpdInfo::EnumMmAuthMethods()
{
	HRESULT hr = hrOK;
	int i;

	CSingleLock cLock(&m_csData);

	CMmAuthMethodsArray arrayTemp;
	CORg(InternalEnumMmAuthMethods(
					  &arrayTemp
                      ));

	cLock.Lock();
	FreeItemsAndEmptyArray(m_arrMmAuthMethods);
	m_arrMmAuthMethods.Copy(arrayTemp);

Error:
	//this particular error is because we don't have any MM policies. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
}

//Call the SPD to enum all Main mode SAs
HRESULT CSpdInfo::InternalEnumMmSAs(
						CMmSAArray * pArray)
{
	HRESULT			hr = hrOK;
	
	PIPSEC_MM_SA	pSAs = NULL;
	DWORD			dwNumEntries = 10;
	DWORD			dwTotal = 0;
	DWORD			dwEnumHandle = 0;
        IPSEC_MM_SA MMTemplate;

	int				i;

	FreeItemsAndEmptyArray(*pArray);
        memset(&MMTemplate,0,sizeof(IPSEC_MM_SA));

	DWORD		dwNumSAs = 0;
	do 
	{
		dwNumEntries = 10; //we request 10 (the Max#) SAs each time
		CWRg(::IPSecEnumMMSAs(
							(LPTSTR)(LPCTSTR)m_stMachineName,
							&MMTemplate,
							&pSAs,
							&dwNumEntries,
							&dwTotal,
							&dwEnumHandle,
							0			//dwFlags
							));

		pArray->SetSize(dwNumSAs + dwNumEntries);
		for (i = 0; i < (int)dwNumEntries; i++)
		{
			CMmSA * psa = new CMmSA;
			if (NULL == psa)
			{
				hr = E_OUTOFMEMORY;
				break;
			}

			*psa = *(pSAs + i);
			LoadMiscMmSAInfo(psa);
			(*pArray)[dwNumSAs + i] = psa;
		}
		dwNumSAs += dwNumEntries;

		if (pSAs)
		{
			SPDApiBufferFree(pSAs);
		}
	}while (dwTotal);
	

Error:
	//this particular error is because we don't have any data. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
}

HRESULT CSpdInfo::EnumMmSAs()
{
	HRESULT hr = hrOK;
	int i;

	DWORD	dwCurrentIndexType = 0;
	DWORD	dwCurrentSortOption = 0;

	CSingleLock cLock(&m_csData);
	
	CMmSAArray arrayTemp;
	CORg(InternalEnumMmSAs(
					  &arrayTemp
                      ));

	cLock.Lock();
	FreeItemsAndEmptyArray(m_arrayMmSAs);
	m_arrayMmSAs.Copy(arrayTemp);
	
	//remember the original IndexType and Sort options
	dwCurrentIndexType = m_IndexMgrMmSAs.GetCurrentIndexType();
	dwCurrentSortOption = m_IndexMgrMmSAs.GetCurrentSortOption();

	m_IndexMgrMmSAs.Reset();
	for (i = 0; i < (int)m_arrayMmSAs.GetSize(); i++)
	{
		m_IndexMgrMmSAs.AddItem(m_arrayMmSAs.GetAt(i));
	}
	m_IndexMgrMmSAs.Sort(dwCurrentIndexType, dwCurrentSortOption);


Error:
	//this particular error is because we don't have any MM policies. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;

}

//Call the SPD to enum all Quick mode SAs
HRESULT CSpdInfo::InternalEnumQmSAs(
						CQmSAArray * pArray)
{
	HRESULT			hr = hrOK;
	
	PIPSEC_QM_SA	pSAs = NULL;
	DWORD			dwNumEntries = 10;
	DWORD			dwTotal = 0;
	DWORD			dwResumeHandle = 0;

	int				i;

	FreeItemsAndEmptyArray(*pArray);

	DWORD		dwNumSAs = 0;
	do 
	{
		CWRg(::EnumQMSAs(
							(LPTSTR)(LPCTSTR)m_stMachineName,
							NULL,		//pMMTemplate
							&pSAs,
							0,			//We prefer to get all
							&dwNumEntries,
							&dwTotal,
							&dwResumeHandle,
							0			//dwFlags
							));

		pArray->SetSize(dwNumSAs + dwNumEntries);
		for (i = 0; i < (int)dwNumEntries; i++)
		{
			Assert(pSAs);
			CQmSA * psa = new CQmSA;
			if (NULL == psa)
			{
				hr = E_OUTOFMEMORY;
				break;
			}

			*psa = *(pSAs + i);
			LoadMiscQmSAInfo(psa);
			(*pArray)[dwNumSAs + i] = psa;
		}
		dwNumSAs += dwNumEntries;

		if (pSAs)
		{
			SPDApiBufferFree(pSAs);
		}
	}while (TRUE);
	

Error:
	//this particular error is because we don't have any data. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
}

HRESULT CSpdInfo::EnumQmSAs()
{
	HRESULT hr = hrOK;
	int i;

	DWORD	dwCurrentIndexType = 0;
	DWORD	dwCurrentSortOption = 0;

	CSingleLock cLock(&m_csData);

	CQmSAArray arrayTemp;
	CORg(InternalEnumQmSAs(
					  &arrayTemp
                      ));

	cLock.Lock();
	FreeItemsAndEmptyArray(m_arrayQmSAs);
	m_arrayQmSAs.Copy(arrayTemp);

	//remember the original IndexType and Sort options
	dwCurrentIndexType = m_IndexMgrQmSAs.GetCurrentIndexType();
	dwCurrentSortOption = m_IndexMgrQmSAs.GetCurrentSortOption();

	m_IndexMgrQmSAs.Reset();
	for (i = 0; i < (int)m_arrayQmSAs.GetSize(); i++)
	{
		m_IndexMgrQmSAs.AddItem(m_arrayQmSAs.GetAt(i));
	}
	m_IndexMgrQmSAs.Sort(dwCurrentIndexType, dwCurrentSortOption);

Error:

	return hr;

}

//Call the SPD to enum filters and put it to our array
HRESULT CSpdInfo::InternalEnumMmFilters(
						DWORD dwLevel,
						GUID guid,
						CMmFilterInfoArray * pArray,
						DWORD dwPreferredNum /* = 0 by default get all entries*/)
{
	Assert(pArray);

	HRESULT		 hr = hrOK;
	PMM_FILTER   pFilters = NULL;

	DWORD dwNumFilters = 0;
	DWORD dwTemp = 0;
	DWORD	dwResumeHandle = 0;
	
	FreeItemsAndEmptyArray(*pArray);

	do
	{
		dwTemp = 0;
		CWRg(::EnumMMFilters(
					(LPTSTR)(LPCTSTR)m_stMachineName,
					dwLevel,
					guid,
					&pFilters,
					dwPreferredNum,
                    &dwTemp,
                    &dwResumeHandle
                    ));
		
		
		pArray->SetSize(dwNumFilters + dwTemp);
		for (int i = 0; i < (int)dwTemp; i++)
		{
			CMmFilterInfo * pFltr = new CMmFilterInfo;
			if (NULL == pFltr)
			{
				hr = E_OUTOFMEMORY;
				break;
			}

			*pFltr = *(pFilters + i);
			LoadMiscMmFilterInfo(pFltr);
			(*pArray)[dwNumFilters + i] = pFltr;
		}
		dwNumFilters += dwTemp;

		if (pFilters)
		{
			SPDApiBufferFree(pFilters);
		}
		
	}while (TRUE);  
	// it will automatically break out from the loop when SPD returns ERROR_NO_DATA

Error:
	//this particular error is because we don't have any data. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
	
}

HRESULT CSpdInfo::EnumMmFilters()
{
	HRESULT hr = S_OK;
	int i;

	DWORD	dwCurrentIndexType = 0;
	DWORD	dwCurrentSortOption = 0;

	CSingleLock cLock(&m_csData);
	
	//TODO we should either read all filters in one short 
	//     or create some assync way to query filters
	GUID   guid;
	ZeroMemory(&guid, sizeof(guid));
	CMmFilterInfoArray arrayTempGeneric;
	CMmFilterInfoArray arrayTempSpecific;
	CORg(InternalEnumMmFilters(
					ENUM_GENERIC_FILTERS,
					guid,
					&arrayTempGeneric
					));

	//Load the specific filters
	CORg(InternalEnumMmFilters(
					ENUM_SPECIFIC_FILTERS,
					guid,
					&arrayTempSpecific
                    ));

	cLock.Lock();
	
	FreeItemsAndEmptyArray(m_arrayMmFilters);
	m_arrayMmFilters.Copy(arrayTempGeneric);

	FreeItemsAndEmptyArray(m_arrayMmSpecificFilters);
	m_arrayMmSpecificFilters.Copy(arrayTempSpecific);

	//remember the original IndexType and Sort options
	dwCurrentIndexType = m_IndexMgrMmFilters.GetCurrentIndexType();
	dwCurrentSortOption = m_IndexMgrMmFilters.GetCurrentSortOption();

	m_IndexMgrMmFilters.Reset();
	for (i = 0; i < m_arrayMmFilters.GetSize(); i++)
	{
		m_IndexMgrMmFilters.AddItem(m_arrayMmFilters.GetAt(i));
	}
	m_IndexMgrMmFilters.SortMmFilters(dwCurrentIndexType, dwCurrentSortOption);

	//Now work on the specific filters
	//remember the original IndexType and Sort options
	dwCurrentIndexType = m_IndexMgrMmSpecificFilters.GetCurrentIndexType();
	dwCurrentSortOption = m_IndexMgrMmSpecificFilters.GetCurrentSortOption();
	m_IndexMgrMmSpecificFilters.Reset();
	for (i = 0; i < m_arrayMmSpecificFilters.GetSize(); i++)
	{
		m_IndexMgrMmSpecificFilters.AddItem(m_arrayMmSpecificFilters.GetAt(i));
	}
	m_IndexMgrMmSpecificFilters.SortMmFilters(dwCurrentIndexType, dwCurrentSortOption);

	
Error:

	return hr;

}

//Call the SPD to enum filters and put it to our array
HRESULT CSpdInfo::InternalEnumTransportFilters(
						DWORD dwLevel,
						GUID guid,
						CFilterInfoArray * pArray,
						DWORD dwPreferredNum /* = 0 by default get all entries*/)
{
	Assert(pArray);

	HRESULT				hr = hrOK;
	PTRANSPORT_FILTER   pFilters = NULL;

	DWORD	dwNumFilters = 0;
	DWORD	dwTemp = 0;
	DWORD	dwResumeHandle = 0;
	
	FreeItemsAndEmptyArray(*pArray);

	do
	{
		dwTemp = 0;
		CWRg(::EnumTransportFilters(
					(LPTSTR)(LPCTSTR)m_stMachineName,
					dwLevel,
					guid,
					&pFilters,
					dwPreferredNum,
                    &dwTemp,
                    &dwResumeHandle
                    ));
		
		
		pArray->SetSize(dwNumFilters + dwTemp);
		for (int i = 0; i < (int)dwTemp; i++)
		{
			CFilterInfo * pFltr = new CFilterInfo;
			if (NULL == pFltr)
			{
				hr = E_OUTOFMEMORY;
				break;
			}

			*pFltr = *(pFilters + i);
			LoadMiscFilterInfo(pFltr);
			(*pArray)[dwNumFilters + i] = pFltr;
		}
		dwNumFilters += dwTemp;

		if (pFilters)
		{
			SPDApiBufferFree(pFilters);
		}
		
	}while (TRUE);  
	// it will automatically break out from the loop when SPD returns ERROR_NO_DATA

Error:
	//this particular error is because we don't have any data. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
	
}

HRESULT CSpdInfo::EnumQmFilters()
{
	HRESULT hr = S_OK;
	int i;

	DWORD	dwCurrentIndexType = 0;
	DWORD	dwCurrentSortOption = 0;

	CFilterInfoArray arrayTransportFilters;
	CFilterInfoArray arrayTunnelFilters;
	
	CFilterInfoArray arraySpTransportFilters;
	CFilterInfoArray arraySpTunnelFilters;
	
	CSingleLock cLock(&m_csData);

	GUID   guid;
	ZeroMemory(&guid, sizeof(guid));
	CORg(InternalEnumTransportFilters(
					ENUM_GENERIC_FILTERS,
					guid,
					&arrayTransportFilters
                    ));

	CORg(InternalEnumTunnelFilters(
					ENUM_GENERIC_FILTERS,
					guid,
					&arrayTunnelFilters
					));

	//We are done with the generic filters.
	//Load the specific filters

	CORg(InternalEnumTransportFilters(
					ENUM_SPECIFIC_FILTERS,
					guid,
					&arraySpTransportFilters
                    ));

	CORg(InternalEnumTunnelFilters(
					ENUM_SPECIFIC_FILTERS,
					guid,
					&arraySpTunnelFilters
					));

	//Update the internal data now

	cLock.Lock();

	FreeItemsAndEmptyArray(m_arrayFilters);
	m_arrayFilters.Copy(arrayTransportFilters);
	m_arrayFilters.Append(arrayTunnelFilters);

	//remember the original IndexType and Sort options
	dwCurrentIndexType = m_IndexMgrFilters.GetCurrentIndexType();
	dwCurrentSortOption = m_IndexMgrFilters.GetCurrentSortOption();

	m_IndexMgrFilters.Reset();
	for (i = 0; i < (int)m_arrayFilters.GetSize(); i++)
	{
		m_IndexMgrFilters.AddItem(m_arrayFilters.GetAt(i));
	}
	m_IndexMgrFilters.SortFilters(dwCurrentIndexType, dwCurrentSortOption);


	FreeItemsAndEmptyArray(m_arraySpecificFilters);
	m_arraySpecificFilters.Copy(arraySpTransportFilters);
	m_arraySpecificFilters.Append(arraySpTunnelFilters);

	//remember the original IndexType and Sort options
	dwCurrentIndexType = m_IndexMgrSpecificFilters.GetCurrentIndexType();
	dwCurrentSortOption = m_IndexMgrSpecificFilters.GetCurrentSortOption();
	
	m_IndexMgrSpecificFilters.Reset();
	for (i = 0; i < (int)m_arraySpecificFilters.GetSize(); i++)
	{
		m_IndexMgrSpecificFilters.AddItem(m_arraySpecificFilters.GetAt(i));
	}
	m_IndexMgrSpecificFilters.SortFilters(dwCurrentIndexType, dwCurrentSortOption);

	cLock.Unlock();

Error:
	//this particular error is because we don't have any MM policies. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
}

//Call the SPD to enum filters and put it to our array
HRESULT CSpdInfo::InternalEnumTunnelFilters(
						DWORD dwLevel,
						GUID guid,
						CFilterInfoArray * pArray,
						DWORD dwPreferredNum /* = 0 by default get all entries*/)
{
	Assert(pArray);

	HRESULT				hr = hrOK;
	FreeItemsAndEmptyArray(*pArray);

	
	PTUNNEL_FILTER   pFilters = NULL;

	DWORD	dwNumFilters = 0;
	DWORD	dwTemp = 0;
	DWORD	dwResumeHandle = 0;

	do
	{
		dwTemp = 0;
		CWRg(::EnumTunnelFilters(
					(LPTSTR)(LPCTSTR)m_stMachineName,
					dwLevel,
					guid,
					&pFilters,
					dwPreferredNum,
                    &dwTemp,
                    &dwResumeHandle
                    ));
		
		
		pArray->SetSize(dwNumFilters + dwTemp);
		for (int i = 0; i < (int)dwTemp; i++)
		{
			CFilterInfo * pFltr = new CFilterInfo;
			if (NULL == pFltr)
			{
				hr = E_OUTOFMEMORY;
				break;
			}

			*pFltr = *(pFilters + i);
			LoadMiscFilterInfo(pFltr);
			(*pArray)[dwNumFilters + i] = pFltr;
		}
		dwNumFilters += dwTemp;

		if (pFilters)
		{
			SPDApiBufferFree(pFilters);
		}
		
	}while (TRUE);  
	// it will automatically break out from the loop when SPD returns ERROR_NO_DATA

Error:
	//this particular error is because we don't have any data. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
	
}

HRESULT CSpdInfo::EnumSpecificFilters
(
	GUID * pFilterGuid, 
	CFilterInfoArray * parraySpecificFilters,
	FILTER_TYPE fltrType
)
{
	Assert (pFilterGuid);
	Assert (parraySpecificFilters);

	HRESULT hr = hrOK;

	int		i;

	if (FILTER_TYPE_TUNNEL == fltrType)
	{
		CORg(InternalEnumTunnelFilters(
					ENUM_SELECT_SPECIFIC_FILTERS,
					*pFilterGuid,
					parraySpecificFilters
					));
	}
	else if (FILTER_TYPE_TRANSPORT == fltrType)
	{
		CORg(InternalEnumTransportFilters(
					ENUM_SELECT_SPECIFIC_FILTERS,
					*pFilterGuid,
					parraySpecificFilters
                    ));
	}

Error:
	//this particular error is because we don't have any MM policies. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
}

//Convert internal filter data to external spd data structure
//NOTE: the routine only convert severl parameters that are 
//needed for searching match filters
void CSpdInfo::ConvertToExternalFilterData
(
	CFilterInfo * pfltrIn, 
	TRANSPORT_FILTER * pfltrOut
)
{
		ZeroMemory (pfltrOut, sizeof(*pfltrOut));
		//pfltrOut->bCreateMirror = pfltrIn->m_bCreateMirror;
		pfltrOut->DesAddr = pfltrIn->m_DesAddr;
		pfltrOut->DesPort = pfltrIn->m_DesPort;
		pfltrOut->dwDirection = pfltrIn->m_dwDirection;
		//pfltrOut->dwWeight = pfltrIn->m_dwWeight;
		//pfltrOut->FilterFlag = pfltrIn->;
		//pfltrOut->gFilterID = pfltrIn->m_guidFltr;
		//pfltrOut->gPolicyID = pfltrIn->m_guidPolicyID;
		//pfltrOut->InterfaceType = pfltrIn->m_InterfaceType;
		pfltrOut->Protocol = pfltrIn->m_Protocol;
		
		pfltrOut->SrcAddr = pfltrIn->m_SrcAddr;
		pfltrOut->SrcPort = pfltrIn->m_SrcPort;

}

HRESULT CSpdInfo::GetMatchFilters
(
	CFilterInfo * pfltrSearchCondition, 
	DWORD dwPreferredNum, 
	CFilterInfoArray * parrFilters
)
{
	HRESULT hr = S_OK;

	Assert (pfltrSearchCondition);
	Assert (parrFilters);

	PTRANSPORT_FILTER pMatchedFilters = NULL;
	PIPSEC_QM_POLICY pMatchedPolicies = NULL;
	DWORD dwNumMatches = 0;
	DWORD dwResumeHandle = 0;
	DWORD i = 0;

	TRANSPORT_FILTER SpdFltr;
	
	ConvertToExternalFilterData(pfltrSearchCondition, &SpdFltr);
	CWRg(::MatchTransportFilter (
				(LPTSTR)((LPCTSTR)m_stMachineName),
				&SpdFltr,
				0,					//Don't return default policy if no match
				&pMatchedFilters,
				&pMatchedPolicies,
				dwPreferredNum,					//enum all //BUGBUG should be 0 instead of 1000
				&dwNumMatches,
				&dwResumeHandle
				));

	//Todo check whether we really got all.
	
	parrFilters->SetSize(dwNumMatches);
	for (i = 0; i < dwNumMatches; i++)
	{
		CFilterInfo * pFltr = new CFilterInfo;
		*pFltr = *(pMatchedFilters + i);
		LoadMiscFilterInfo(pFltr);
		(*parrFilters)[i] = pFltr;
	}

	if (pMatchedFilters)
		SPDApiBufferFree(pMatchedFilters);

	if (pMatchedPolicies)
		SPDApiBufferFree(pMatchedPolicies);

Error:
	//this particular error is because we don't have any data. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
}


//Convert internal filter data to external spd data structure
//NOTE: the routine only convert severl parameters that are 
//needed for searching match filters
void CSpdInfo::ConvertToExternalMMFilterData
(
	CMmFilterInfo * pfltrIn, 
	MM_FILTER * pfltrOut
)
{
		ZeroMemory (pfltrOut, sizeof(*pfltrOut));
		//pfltrOut->bCreateMirror = pfltrIn->m_bCreateMirror;
		pfltrOut->DesAddr = pfltrIn->m_DesAddr;
		pfltrOut->dwDirection = pfltrIn->m_dwDirection;
		//pfltrOut->dwWeight = pfltrIn->m_dwWeight;
		//pfltrOut->gFilterID = pfltrIn->m_guidFltr;
		//pfltrOut->gPolicyID = pfltrIn->m_guidPolicyID;
		//pfltrOut->InterfaceType = pfltrIn->m_InterfaceType;
		pfltrOut->SrcAddr = pfltrIn->m_SrcAddr;
}

HRESULT CSpdInfo::GetMatchMMFilters
(
	CMmFilterInfo * pfltrSearchCondition, 
	DWORD dwPreferredNum, 
	CMmFilterInfoArray * parrFilters
)
{
	HRESULT hr = S_OK;

	Assert (pfltrSearchCondition);
	Assert (parrFilters);

	PMM_FILTER pMatchedFilters = NULL;
	PIPSEC_MM_POLICY pMatchedPolicies = NULL;
	PMM_AUTH_METHODS pMatchedAuths = NULL;
	DWORD dwNumMatches = 0;
	DWORD dwResumeHandle = 0;
	DWORD i = 0;

	MM_FILTER SpdFltr;
	
	ConvertToExternalMMFilterData(pfltrSearchCondition, &SpdFltr);

	CWRg(::MatchMMFilter (
				(LPTSTR)((LPCTSTR)m_stMachineName),
				&SpdFltr,
				0,					//Don't return default policy if no match
				&pMatchedFilters,
				&pMatchedPolicies,
				&pMatchedAuths,
				dwPreferredNum,					//enum all //TODO BUGBUG should be 0 instead of 1000
				&dwNumMatches,
				&dwResumeHandle
				));

	//Todo check whether we really got all.
	
	parrFilters->SetSize(dwNumMatches);
	for (i = 0; i < dwNumMatches; i++)
	{
		CMmFilterInfo * pFltr = new CMmFilterInfo;
		*pFltr = *(pMatchedFilters + i);
		LoadMiscMmFilterInfo(pFltr);
		
		(*parrFilters)[i] = pFltr;
	}

	if (pMatchedFilters)
		SPDApiBufferFree(pMatchedFilters);

	if (pMatchedPolicies)
		SPDApiBufferFree(pMatchedPolicies);

	if (pMatchedAuths)
		SPDApiBufferFree(pMatchedAuths);

Error:
	//this particular error is because we don't have any data. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
}

HRESULT CSpdInfo::EnumMmSpecificFilters
(
	GUID * pGenFilterGuid, 
	CMmFilterInfoArray * parraySpecificFilters
)
{
	Assert (pGenFilterGuid);
	Assert (parraySpecificFilters);

	HRESULT hr = hrOK;

    DWORD	dwNumFilters = 0;
    DWORD	dwResumeHandle = 0;
	int		i;

	parraySpecificFilters->RemoveAll();
	
	CORg(InternalEnumMmFilters(
					ENUM_SELECT_SPECIFIC_FILTERS,
					*pGenFilterGuid,
					parraySpecificFilters
                    ));

Error:
	//this particular error is because we don't have any MM policies. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
}


//Call the SPD to enum policies and put it to our array
HRESULT CSpdInfo::InternalEnumMmPolicies(
						CMmPolicyInfoArray * pArray,
						DWORD dwPreferredNum /* = 0 by default get all entries*/)
{
	Assert(pArray);

	HRESULT		 hr = hrOK;
	PIPSEC_MM_POLICY   pPolicies = NULL;

	DWORD	dwNumPolicies = 0;
	DWORD	dwTemp = 0;
	DWORD	dwResumeHandle = 0;
	
	FreeItemsAndEmptyArray(*pArray);

	do
	{
		dwTemp = 0;
		CWRg(::EnumMMPolicies(
					(LPTSTR)(LPCTSTR)m_stMachineName,
					&pPolicies,
					dwPreferredNum,
                    &dwTemp,
                    &dwResumeHandle
                    ));
		
		
		pArray->SetSize(dwNumPolicies + dwTemp);
		for (int i = 0; i < (int)dwTemp; i++)
		{
			CMmPolicyInfo * pPol = new CMmPolicyInfo;
			if (NULL == pPol)
			{
				hr = E_OUTOFMEMORY;
				break;
			}

			*pPol = *(pPolicies + i);
			(*pArray)[dwNumPolicies + i] = pPol;
		}
		dwNumPolicies += dwTemp;

		if (pPolicies)
		{
			SPDApiBufferFree(pPolicies);
		}
		
	}while (TRUE);  
	// it will automatically break out from the loop when SPD returns ERROR_NO_DATA

Error:
	//this particular error is because we don't have any data. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
	
}

HRESULT CSpdInfo::EnumMmPolicies()
{
	HRESULT hr = hrOK;

	CSingleLock cLock(&m_csData);

	DWORD dwCurrentIndexType;
	DWORD dwCurrentSortOption;
	
	int i;

	CMmPolicyInfoArray arrayTemp;

	CORg(InternalEnumMmPolicies(
				&arrayTemp,
				0				//enum all policies
				));

	cLock.Lock();
	FreeItemsAndEmptyArray(m_arrayMmPolicies);
	m_arrayMmPolicies.Copy(arrayTemp);

	//remember the original IndexType and Sort options
	dwCurrentIndexType = m_IndexMgrMmPolicies.GetCurrentIndexType();
	dwCurrentSortOption = m_IndexMgrMmPolicies.GetCurrentSortOption();

	m_IndexMgrMmPolicies.Reset();
	for (i = 0; i < (int)m_arrayMmPolicies.GetSize(); i++)
	{
		m_IndexMgrMmPolicies.AddItem(m_arrayMmPolicies.GetAt(i));
	}
	m_IndexMgrMmPolicies.Sort(dwCurrentIndexType, dwCurrentSortOption);
	
Error:
	//this particular error is because we don't have any MM policies. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
}


//Call the SPD to enum policies and put it to our array
HRESULT CSpdInfo::InternalEnumQmPolicies(
						CQmPolicyInfoArray * pArray,
						DWORD dwPreferredNum /* = 0 by default get all entries*/)
{
	Assert(pArray);

	HRESULT		 hr = hrOK;
	PIPSEC_QM_POLICY   pPolicies = NULL;

	DWORD	dwNumPolicies = 0;
	DWORD	dwTemp = 0;
	DWORD	dwResumeHandle = 0;
	
	FreeItemsAndEmptyArray(*pArray);

	do
	{
		dwTemp = 0;
		CWRg(::EnumQMPolicies(
					(LPTSTR)(LPCTSTR)m_stMachineName,
					&pPolicies,
					dwPreferredNum,
                    &dwTemp,
                    &dwResumeHandle
                    ));
		
		
		pArray->SetSize(dwNumPolicies + dwTemp);
		for (int i = 0; i < (int)dwTemp; i++)
		{
			CQmPolicyInfo * pPol = new CQmPolicyInfo;
			if (NULL == pPol)
			{
				hr = E_OUTOFMEMORY;
				break;
			}

			*pPol = *(pPolicies + i);
			(*pArray)[dwNumPolicies + i] = pPol;
		}
		dwNumPolicies += dwTemp;

		if (pPolicies)
		{
			SPDApiBufferFree(pPolicies);
		}
		
	}while (TRUE);  
	// it will automatically break out from the loop when SPD returns ERROR_NO_DATA

Error:
	//this particular error is because we don't have any data. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
	
}

HRESULT CSpdInfo::EnumQmPolicies()
{
	HRESULT hr = hrOK;

	CSingleLock cLock(&m_csData);

	DWORD dwCurrentIndexType;
	DWORD dwCurrentSortOption;
	
	int i;

	CQmPolicyInfoArray arrayTemp;
	CORg(InternalEnumQmPolicies(
				&arrayTemp
				));

	cLock.Lock();
	FreeItemsAndEmptyArray(m_arrayQmPolicies);
	m_arrayQmPolicies.Copy(arrayTemp);

	//remember the original IndexType and Sort options
	dwCurrentIndexType = m_IndexMgrQmPolicies.GetCurrentIndexType();
	dwCurrentSortOption = m_IndexMgrQmPolicies.GetCurrentSortOption();

	m_IndexMgrQmPolicies.Reset();
	for (i = 0; i < (int)m_arrayQmPolicies.GetSize(); i++)
	{
		m_IndexMgrQmPolicies.AddItem(m_arrayQmPolicies.GetAt(i));
	}
	m_IndexMgrQmPolicies.Sort(dwCurrentIndexType, dwCurrentSortOption);

	
Error:
	//this particular error is because we don't have any QM policies. Ignore it
	if (HRESULT_FROM_WIN32(ERROR_NO_DATA) == hr)
		hr = hrOK;

	return hr;
}

DWORD CSpdInfo::GetMmAuthMethodsCount()
{
	CSingleLock cLock(&m_csData);
	cLock.Lock();
	
	return (DWORD)m_arrMmAuthMethods.GetSize();
}

DWORD CSpdInfo::GetMmSACount()
{
	CSingleLock cLock(&m_csData);
	cLock.Lock();
	
	return (DWORD)m_arrayMmSAs.GetSize();
}

DWORD CSpdInfo::GetMmPolicyCount()
{
	CSingleLock cLock(&m_csData);
	cLock.Lock();
	
	return (DWORD)m_arrayMmPolicies.GetSize();
}

DWORD CSpdInfo::GetQmSACount()
{
	CSingleLock cLock(&m_csData);
	cLock.Lock();
	
	return (DWORD)m_arrayQmSAs.GetSize();
}

DWORD CSpdInfo::GetQmPolicyCount()
{
	CSingleLock cLock(&m_csData);
	cLock.Lock();
	
	return (DWORD)m_arrayQmPolicies.GetSize();
}


DWORD CSpdInfo::GetQmFilterCountOfCurrentViewType()
{
	CSingleLock cLock(&m_csData);
	cLock.Lock();

	return (DWORD)(m_IndexMgrFilters.GetItemCount());
}

DWORD CSpdInfo::GetQmSpFilterCountOfCurrentViewType()
{
	CSingleLock cLock(&m_csData);
	cLock.Lock();

	return (DWORD)(m_IndexMgrSpecificFilters.GetItemCount());
}

DWORD CSpdInfo::GetMmFilterCount()
{
	CSingleLock cLock(&m_csData);
	cLock.Lock();

	return (DWORD)(m_arrayMmFilters.GetSize());
}

DWORD CSpdInfo::GetMmSpecificFilterCount()
{
	CSingleLock cLock(&m_csData);
	cLock.Lock();

	return (DWORD)(m_arrayMmSpecificFilters.GetSize());
}

HRESULT CSpdInfo::GetFilterInfo(int iIndex, CFilterInfo * pFilter)
{
	HRESULT hr = S_OK;

	Assert(pFilter);
	if (NULL == pFilter)
		return E_INVALIDARG;

	CSingleLock cLock(&m_csData);
	cLock.Lock();

	if (iIndex < m_arrayFilters.GetSize())
	{
		CFilterInfo * pFltrData = (CFilterInfo*)m_IndexMgrFilters.GetItemData(iIndex);
		Assert(pFltrData);
		*pFilter = *pFltrData;
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

	return hr;
}

HRESULT CSpdInfo::GetSpecificFilterInfo(int iIndex, CFilterInfo * pFilter)
{
	HRESULT hr = S_OK;
	
	Assert(pFilter);
	if (NULL == pFilter)
		return E_INVALIDARG;

	CSingleLock cLock(&m_csData);
	cLock.Lock();

	if (iIndex < m_arraySpecificFilters.GetSize())
	{
		CFilterInfo * pFltrData = (CFilterInfo*)m_IndexMgrSpecificFilters.GetItemData(iIndex);
		Assert(pFltrData);
		*pFilter = *pFltrData;
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

	return hr;
}

HRESULT CSpdInfo::GetMmFilterInfo(int iIndex, CMmFilterInfo * pFltr)
{
	HRESULT hr = S_OK;

	Assert(pFltr);
	if (NULL == pFltr)
		return E_INVALIDARG;

	CSingleLock cLock(&m_csData);
	cLock.Lock();

	if (iIndex < m_arrayMmFilters.GetSize())
	{
		CMmFilterInfo * pFltrData = (CMmFilterInfo*)m_IndexMgrMmFilters.GetItemData(iIndex);
		Assert(pFltrData);

		*pFltr = *pFltrData;
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

	return hr;
}

HRESULT CSpdInfo::GetMmSpecificFilterInfo
(
	int iIndex, 
	CMmFilterInfo * pFltr
)
{
	HRESULT hr = S_OK;

	Assert(pFltr);
	if (NULL == pFltr)
		return E_INVALIDARG;

	CSingleLock cLock(&m_csData);
	cLock.Lock();

	if (iIndex < m_arrayMmSpecificFilters.GetSize())
	{
		CMmFilterInfo * pFltrData = (CMmFilterInfo*)m_IndexMgrMmSpecificFilters.GetItemData(iIndex);
		Assert(pFltrData);
		*pFltr = *pFltrData;
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

	return hr;
}

HRESULT CSpdInfo::GetMmPolicyInfo(int iIndex, CMmPolicyInfo * pMmPolicy)
{
	HRESULT hr = hrOK;

	Assert(pMmPolicy);
	if (NULL == pMmPolicy)
		return E_INVALIDARG;

	CSingleLock cLock(&m_csData);
	cLock.Lock();

	if (iIndex < m_arrayMmPolicies.GetSize())
	{
		CMmPolicyInfo * pPolicy = (CMmPolicyInfo*) m_IndexMgrMmPolicies.GetItemData(iIndex);
		*pMmPolicy = *pPolicy;
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

	return hr;
}

HRESULT CSpdInfo::GetMmAuthMethodsInfo(int iIndex, CMmAuthMethods * pMmAuth)
{
	HRESULT hr = hrOK;
	CSingleLock cLock(&m_csData);
	cLock.Lock();

	if (iIndex < m_arrMmAuthMethods.GetSize())
	{
		*pMmAuth = *m_arrMmAuthMethods[iIndex];
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

	return hr;
}

HRESULT CSpdInfo::GetMmSAInfo(int iIndex, CMmSA * pSA)
{
	HRESULT hr = hrOK;
	CSingleLock cLock(&m_csData);
	cLock.Lock();

	Assert(pSA);
	if (NULL == pSA)
		return E_INVALIDARG;

	if (iIndex < m_arrayMmSAs.GetSize())
	{
		CMmSA * pSATemp = (CMmSA*) m_IndexMgrMmSAs.GetItemData(iIndex);
		*pSA = *pSATemp;
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

	return hr;	
}

HRESULT CSpdInfo::GetQmSAInfo(int iIndex, CQmSA * pSA)
{
	HRESULT hr = hrOK;
	CSingleLock cLock(&m_csData);
	cLock.Lock();

	Assert(pSA);
	if (NULL == pSA)
		return E_INVALIDARG;

	if (iIndex < m_arrayQmSAs.GetSize())
	{
		CQmSA * pSATemp = (CQmSA*) m_IndexMgrQmSAs.GetItemData(iIndex);
		*pSA = *pSATemp;
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

	return hr;	
}

HRESULT CSpdInfo::GetQmPolicyInfo(int iIndex, CQmPolicyInfo * pQmPolicy)
{
	HRESULT hr = hrOK;
	
	if (NULL == pQmPolicy)
		return E_INVALIDARG;

	CSingleLock cLock(&m_csData);
	cLock.Lock();

	if (iIndex < m_arrayQmPolicies.GetSize())
	{
		CQmPolicyInfo * pPolicy = (CQmPolicyInfo*) m_IndexMgrQmPolicies.GetItemData(iIndex);
		*pQmPolicy = *pPolicy;
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

	return hr;
}

HRESULT CSpdInfo::GetQmPolicyNameByGuid(GUID guid, CString * pst)
{
	HRESULT hr = hrOK;

	if (NULL == pst)
		return E_INVALIDARG;

	pst->Empty();

	PIPSEC_QM_POLICY pPolicy = NULL;
	CWRg(::GetQMPolicyByID(
				(LPTSTR)(LPCTSTR) m_stMachineName, 
				guid,
				&pPolicy));
	
	if (pPolicy)
	{
		*pst = pPolicy->pszPolicyName;
		SPDApiBufferFree(pPolicy);
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

Error:

	return hr;
}

HRESULT CSpdInfo::GetMmPolicyNameByGuid(GUID guid, CString * pst)
{
	HRESULT hr = hrOK;

	if (NULL == pst)
		return E_INVALIDARG;

	pst->Empty();

	PIPSEC_MM_POLICY pPolicy = NULL;
	CWRg(::GetMMPolicyByID(
				(LPTSTR)(LPCTSTR) m_stMachineName, 
				guid,
				&pPolicy));
	
	if (pPolicy)
	{
		*pst = pPolicy->pszPolicyName;
		SPDApiBufferFree(pPolicy);
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

Error:

	return hr;
}

HRESULT CSpdInfo::GetMmAuthMethodsInfoByGuid(GUID guid, CMmAuthMethods * pMmAuth)
{
	HRESULT hr = hrOK;

	PMM_AUTH_METHODS pSpdAuth = NULL;

	CWRg(::GetMMAuthMethods(
				(LPTSTR)(LPCTSTR) m_stMachineName,
				guid,
				&pSpdAuth));
	if (pSpdAuth)
	{
		*pMmAuth = *pSpdAuth;
		SPDApiBufferFree(pSpdAuth);
	}
	else
	{
		hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
	}

Error:

	return hr;
}

HRESULT CSpdInfo::SortFilters(DWORD dwIndexType, DWORD dwSortOptions)
{
	return m_IndexMgrFilters.SortFilters(dwIndexType, dwSortOptions);
}

HRESULT CSpdInfo::SortSpecificFilters(DWORD dwIndexType, DWORD dwSortOptions)
{
	return m_IndexMgrSpecificFilters.SortFilters(dwIndexType, dwSortOptions);
}

HRESULT CSpdInfo::SortMmFilters(DWORD dwIndexType, DWORD dwSortOptions)
{
	return m_IndexMgrMmFilters.SortMmFilters(dwIndexType, dwSortOptions);
}

HRESULT CSpdInfo::SortMmSpecificFilters(DWORD dwIndexType, DWORD dwSortOptions)
{
	return m_IndexMgrMmSpecificFilters.SortMmFilters(dwIndexType, dwSortOptions);
}

HRESULT CSpdInfo::SortMmPolicies(DWORD dwIndexType, DWORD dwSortOptions)
{
	return m_IndexMgrMmPolicies.Sort(dwIndexType, dwSortOptions);
}

HRESULT CSpdInfo::SortQmPolicies(DWORD dwIndexType, DWORD dwSortOptions)
{
	return m_IndexMgrQmPolicies.Sort(dwIndexType, dwSortOptions);
}

HRESULT CSpdInfo::SortMmSAs(DWORD dwIndexType, DWORD dwSortOptions)
{
	return m_IndexMgrMmSAs.Sort(dwIndexType, dwSortOptions);
}

HRESULT CSpdInfo::SortQmSAs(DWORD dwIndexType, DWORD dwSortOptions)
{
	return m_IndexMgrQmSAs.Sort(dwIndexType, dwSortOptions);
}

HRESULT CSpdInfo::EnumQmSAsFromMmSA(const CMmSA & MmSA, CQmSAArray * parrayQmSAs)
{
	HRESULT hr = hrOK;

	if (NULL == parrayQmSAs)
		return E_INVALIDARG;

	FreeItemsAndEmptyArray(*parrayQmSAs);

	for (int i = 0; i < m_arrayQmSAs.GetSize(); i++)
	{
		if (0 == memcmp(&MmSA.m_MMSpi, &(m_arrayQmSAs[i]->m_MMSpi), sizeof(MmSA.m_MMSpi)))
		{
			CQmSA * pSA = new CQmSA;
			
			if (NULL == pSA)
			{
				FreeItemsAndEmptyArray(*parrayQmSAs);
				hr = E_OUTOFMEMORY;
				break;
			}

			*pSA = *(m_arrayQmSAs[i]);
			parrayQmSAs->Add(pSA);
		}
	}

	return hr;
}

HRESULT CSpdInfo::LoadStatistics()
{
	HRESULT hr;
	PIPSEC_STATISTICS pIpsecStats = NULL;

	IKE_STATISTICS ikeStats;
	ZeroMemory(&ikeStats, sizeof(ikeStats));

	CWRg(::IPSecQueryIKEStatistics((LPTSTR)(LPCTSTR)m_stMachineName,
								&ikeStats));
	m_IkeStats = ikeStats;

	CWRg(::QueryIPSecStatistics((LPTSTR)(LPCTSTR)m_stMachineName,
								&pIpsecStats));

	Assert(pIpsecStats);
	m_IpsecStats = *pIpsecStats;

	if (pIpsecStats)
	{
		SPDApiBufferFree(pIpsecStats);
	}

Error:
	return hr;
}

// Get the current cached statistics
void CSpdInfo::GetLoadedStatistics(CIkeStatistics * pIkeStats, CIpsecStatistics * pIpsecStats)
{
	if (pIkeStats)
	{
		*pIkeStats = m_IkeStats;
	}

	if (pIpsecStats)
	{
		*pIpsecStats = m_IpsecStats;
	}
}

void CSpdInfo::ChangeQmFilterViewType(FILTER_TYPE FltrType)
{
	CSingleLock cLock(&m_csData);
	cLock.Lock();

	DWORD dwCurrentIndexType = m_IndexMgrFilters.GetCurrentIndexType();
	DWORD dwCurrentSortOption = m_IndexMgrFilters.GetCurrentSortOption();

	m_IndexMgrFilters.Reset();
	for (int i = 0; i < m_arrayFilters.GetSize(); i++)
	{
		if (FILTER_TYPE_ANY == FltrType ||
			FltrType == m_arrayFilters[i]->m_FilterType)
		{
			m_IndexMgrFilters.AddItem(m_arrayFilters.GetAt(i));
		}
	}

	m_IndexMgrFilters.SortFilters(dwCurrentIndexType, dwCurrentSortOption);

}

void CSpdInfo::ChangeQmSpFilterViewType(FILTER_TYPE FltrType)
{
	CSingleLock cLock(&m_csData);
	cLock.Lock();

	DWORD dwCurrentIndexType = m_IndexMgrSpecificFilters.GetCurrentIndexType();
	DWORD dwCurrentSortOption = m_IndexMgrSpecificFilters.GetCurrentSortOption();

	m_IndexMgrSpecificFilters.Reset();
	for (int i = 0; i < m_arraySpecificFilters.GetSize(); i++)
	{
		if (FILTER_TYPE_ANY == FltrType ||
			FltrType == m_arraySpecificFilters[i]->m_FilterType)
		{
			m_IndexMgrSpecificFilters.AddItem(m_arraySpecificFilters.GetAt(i));
		}
	}

	m_IndexMgrSpecificFilters.SortFilters(dwCurrentIndexType, dwCurrentSortOption);

}

HRESULT CSpdInfo::LoadMiscMmSAInfo(CMmSA * pSA)
{
	Assert(pSA);
	return GetMmPolicyNameByGuid(pSA->m_guidPolicy, &pSA->m_stPolicyName);
}

HRESULT CSpdInfo::LoadMiscQmSAInfo(CQmSA * pSA)
{
	Assert(pSA);
	return GetQmPolicyNameByGuid(pSA->m_guidPolicy, &pSA->m_stPolicyName);
}

HRESULT CSpdInfo::LoadMiscFilterInfo(CFilterInfo * pFltr)
{
	Assert(pFltr);
	return GetQmPolicyNameByGuid(pFltr->m_guidPolicyID, &pFltr->m_stPolicyName);
}

HRESULT CSpdInfo::LoadMiscMmFilterInfo(CMmFilterInfo * pFltr)
{
	Assert(pFltr);

	HRESULT hr = S_OK;
	HRESULT hrTemp;

	CMmAuthMethods auth;

	hrTemp = GetMmAuthMethodsInfoByGuid(pFltr->m_guidAuthID, &auth);
	if (FAILED(hrTemp))
		hr = hrTemp;
	pFltr->m_stAuthDescription = auth.m_stDescription;

	hrTemp = GetMmPolicyNameByGuid(pFltr->m_guidPolicyID, &pFltr->m_stPolicyName);
	if (FAILED(hrTemp))
		hr = hrTemp;

	return hr;
}

STDMETHODIMP
CSpdInfo::Destroy()
{
	//$REVIEW this routine get called when doing auto-refresh
	//We don't need to clean up anything at this time.
	//Each array (Filter, SA, policy...) will get cleaned up when calling the
	//corresponding enum function.

	return S_OK;
}



DWORD
CSpdInfo::GetInitInfo()
{
    CSingleLock cLock(&m_csData);
    cLock.Lock();
        
    return m_Init;
}


void
CSpdInfo::SetInitInfo(DWORD dwInitInfo)
{
    CSingleLock cLock(&m_csData);
    cLock.Lock();
        
    m_Init=dwInitInfo;
}

DWORD
CSpdInfo::GetActiveInfo()
{
    CSingleLock cLock(&m_csData);
    cLock.Lock();
        
    return m_Active;
}


void
CSpdInfo::SetActiveInfo(DWORD dwActiveInfo)
{
    CSingleLock cLock(&m_csData);
    cLock.Lock();
        
    m_Active=dwActiveInfo;
}


/*!--------------------------------------------------------------------------
    CreateSpdInfo
        Helper to create the SpdInfo object.
 ---------------------------------------------------------------------------*/
HRESULT 
CreateSpdInfo(ISpdInfo ** ppSpdInfo)
{
    AFX_MANAGE_STATE(AfxGetModuleState());
    
    SPISpdInfo     spSpdInfo;
    ISpdInfo *     pSpdInfo = NULL;
    HRESULT         hr = hrOK;

    COM_PROTECT_TRY
    {
        pSpdInfo = new CSpdInfo;

        // Do this so that it will get freed on error
        spSpdInfo = pSpdInfo;

        *ppSpdInfo = spSpdInfo.Transfer();

    }
    COM_PROTECT_CATCH

    return hr;
}


//
//  FUNCTIONS: MIDL_user_allocate and MIDL_user_free
//
//  PURPOSE: Used by stubs to allocate and free memory
//           in standard RPC calls. Not used when
//           [enable_allocate] is specified in the .acf.
//
//
//  PARAMETERS:
//    See documentations.
//
//  RETURN VALUE:
//    Exceptions on error.  This is not required,
//    you can use -error allocation on the midl.exe
//    command line instead.
//
//
void * __RPC_USER MIDL_user_allocate(size_t size)
{
    return(HeapAlloc(GetProcessHeap(), HEAP_GENERATE_EXCEPTIONS, size));
}

void __RPC_USER MIDL_user_free( void *pointer)
{
    HeapFree(GetProcessHeap(), 0, pointer);
}