/*

Copyright (c) 1998-1999  Microsoft Corporation

Module Name:
    CMDhcp.cpp

Abstract:
    Implementation of CMdhcp.

Author:

*/

#include "stdafx.h"

#include <winsock2.h>

#include "mdhcp.h"
#include "CMDhcp.h"
#include "lease.h"
#include "local.h"

// template for collections
#include "collect.h"

// From rendezvous control code:
// sets the first bit to indicate error
// sets the win32 facility code
// this is used instead of the HRESULT_FROM_WIN32 macro
// because that clears the customer flag
inline long
HRESULT_FROM_ERROR_CODE(IN long ErrorCode)
{
    return ( 0x80070000 | (0xa000ffff & ErrorCode) );
}



/////////////////////////////////////////////////////////////////////////////
// Helper functions.
/////////////////////////////////////////////////////////////////////////////

HRESULT CMDhcp::CreateWrappers(
    DWORD                 dwScopeCount, // the number of scopes we were given
    MCAST_SCOPE_ENTRY   * pScopeList,   // array of scope structs
    IMcastScope       *** pppWrappers,  // here we will put an array of if ptrs
    BOOL                  fLocal        // true = scopes are locally generated
    )
{
    LOG((MSP_TRACE, "CMDhcp::CreateWrappers enter"));

    HRESULT hr;

    // Allocate the array of interface pointers.
    typedef IMcastScope * ScopeIfPtr;
    *pppWrappers = new ScopeIfPtr[dwScopeCount];

    if ( (*pppWrappers) == NULL )
    {
        LOG((MSP_ERROR,
            "can't create allocate array of interface pointers"));
        return E_OUTOFMEMORY;
    }

    // For each scope in the list of scopes returned by the C API
    for (DWORD i = 0; i < dwScopeCount; i++)
    {
        // create the com object.
        CComObject<CMDhcpScope> * pMDhcpScope;
        hr = CComObject<CMDhcpScope>::CreateInstance(&pMDhcpScope);

        if ( (FAILED(hr)) || (NULL == pMDhcpScope) )
        {
            LOG((MSP_ERROR, "can't create MDhcpScope Object (%d/%d): %08x",
                i, dwScopeCount, hr));
            // get rid of all previously created COM objects
            for (DWORD j = 0; j < i; j++) (*pppWrappers)[j]->Release();
            delete (*pppWrappers);

            return hr;
        }

        // Get the IMcastScope interface.
        hr = pMDhcpScope->_InternalQueryInterface(
            IID_IMcastScope,
            (void **) (& (*pppWrappers)[i])
            );

        if ( FAILED(hr) )
        {
            LOG((MSP_ERROR, "CreateWrappers:QueryInterface (%d/%d) failed: %08x",
                i, dwScopeCount, hr));

            // get rid of all previously created COM objects
            for (DWORD j = 0; j < i; j++) (*pppWrappers)[j]->Release();
            delete (*pppWrappers);

            delete pMDhcpScope; // don't know if it addrefed or not

            return hr;
        }

        // Set the object's info based on the struct. From now on the
        // object will be read-only.
        hr = pMDhcpScope->Initialize(pScopeList[i], fLocal);

        if ( FAILED(hr) )
        {
            LOG((MSP_ERROR, "CreateWrappers:Initialize (%d/%d) failed: %08x",
                i, dwScopeCount, hr));
            // get rid of all previously created COM objects
            for (DWORD j = 0; j < i; j++) (*pppWrappers)[j]->Release();
            delete (*pppWrappers);

            pMDhcpScope->Release(); // we know it addrefed in the QI
            return hr;
        }
    }

    LOG((MSP_TRACE, "CMDhcp::CreateWrappers exit"));
    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// Get a list of scopes from the C API.

HRESULT CMDhcp::GetScopeList(
    DWORD              * pdwScopeCount,
    MCAST_SCOPE_ENTRY ** ppScopeList,
    BOOL               * pfLocal
    )
{
    LOG((MSP_TRACE, "CMDhcp::GetScopeList enter"));

    _ASSERTE( ! IsBadWritePtr(pdwScopeCount, sizeof(DWORD) ) );
    _ASSERTE( ! IsBadWritePtr(ppScopeList, sizeof(MCAST_SCOPE_ENTRY *) ) );

    HRESULT hr;

    DWORD dwScopeLen = 0;   // size in bytes of returned scopes structure
    DWORD dwCode;           // return code

    *pfLocal = FALSE; // try mdhcp first

    dwCode = LocalEnumerateScopes(NULL, // only want to know how many we have
                                  &dwScopeLen,    // # of bytes should be zero
                                  pdwScopeCount,  // # of scopes placed here
                                  pfLocal);

    // This must succeed for us to continue.
    if (dwCode != ERROR_SUCCESS)
    {
        hr = HRESULT_FROM_ERROR_CODE(dwCode);
        LOG((MSP_ERROR, "GetScopeList: First C API call failed "
                "(code: %d hresult: %08x)", dwCode, hr));
        return hr;
    }

    do
    {
        // If there are no scopes to choose from, let's not enumerate them.
        // We also need at least the length fields from the first
        // UNICODE_STRING.
        if ( (dwScopeLen < sizeof(MCAST_SCOPE_ENTRY)) || (*pdwScopeCount < 1) )
        {
            LOG((MSP_ERROR, "GetScopeList: don't have enough scopes (%d;%d)",
                    dwScopeLen, *pdwScopeCount));
            return E_FAIL;
        }

        // Now that we know how many there are, allocate an array to hold the
        // scope structs returned by the C method.

        // The API acts very strangely here. We have to give it dwScopeLen
        // bytes as one big chunk. The first dwScopeCount * sizeof(MCAST_SCOPE_ENTRY)
        // bytes contain dwScopeCount MCAST_SCOPE_ENTRY structures. Each of these
        // structures has a pointer to a wide string. The first of these points
        // to the first byte after all the MCAST_SCOPE_ENTRY structures! In this way
        // they avoid doing so many mallocs. We therefore have to
        // copy each string in the COM wrapper for each scope, and then delete
        // this buffer (ppScopeList) all at once after all the wrapping is complete.

        *ppScopeList = (MCAST_SCOPE_ENTRY *) new CHAR[dwScopeLen];

        if (*ppScopeList == NULL)
        {
            LOG((MSP_ERROR, "GetScopeList: not enough memory to allocate scope"
                    " list (size = %d)", dwScopeLen));
            return E_OUTOFMEMORY;
        }

        // *pdwScopeCount still specifies the number of scopes we can get.

        // Now ask for all the scopes.
        dwCode = LocalEnumerateScopes(*ppScopeList,
                                      &dwScopeLen,
                                      pdwScopeCount,
                                      pfLocal);


        // If things changed in this bried time, just try again.
        if (dwCode == ERROR_MORE_DATA)
        {
            LOG((MSP_INFO, "GetScopeList: got more scopes than we were told "
                    "existed (we though there were %d) -- retrying",
                    *pdwScopeCount));
            delete (*ppScopeList);
        }
    }
    while (dwCode == ERROR_MORE_DATA);

    if (dwCode != ERROR_SUCCESS)
    {
        hr = HRESULT_FROM_ERROR_CODE(dwCode);
        LOG((MSP_ERROR, "GetScopeList: Second C API call failed "
                "(code: %d hresult: %08x)", dwCode, hr));
        delete (*ppScopeList);
        return hr;
    }

    LOG((MSP_TRACE, "CMDhcp::GetScopeList exit"));
    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// This is a private helper method that creates a CMDhcpLeaseInfo object and
// uses it to wrap a lease info structure and request ID into an
// IMcastLeaseInfo interface.

HRESULT CMDhcp::WrapMDhcpLeaseInfo(
    BOOL                fGotTtl,
    long                lTtl,
    BOOL                fLocal,
    MCAST_LEASE_INFO  * pLeaseInfo,
    MCAST_CLIENT_UID  * pRequestID,
    IMcastLeaseInfo  ** ppInterface
    )
{
    LOG((MSP_TRACE, "CMDhcp::WrapMDhcpLeaseInfo enter"));

    // We don't check pLeaseInfo or pRequestID -- they'll be comprehensively
    // checked in the Wrap call below.

    if ( IsBadWritePtr(ppInterface, sizeof(IMcastLeaseInfo *) ) )
    {
        LOG((MSP_ERROR, "WrapMDhcpLeaseInfo: invalid pointer: %x",
            ppInterface));
        
        return E_POINTER;
    }

    HRESULT hr;

    // create the com object.
    CComObject<CMDhcpLeaseInfo> * pMDhcpLeaseInfo;
    hr = CComObject<CMDhcpLeaseInfo>::CreateInstance(&pMDhcpLeaseInfo);

    if ( (FAILED(hr)) || (pMDhcpLeaseInfo == NULL) )
    {
        LOG((MSP_ERROR, "can't create MDhcpLeaseInfo Object."));
        
        return hr;
    }

    // Get the IMcastLeaseInfo interface.
    hr = pMDhcpLeaseInfo->_InternalQueryInterface(
        IID_IMcastLeaseInfo,
        (void **)ppInterface
        );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "WrapMDhcpLeaseInfo:QueryInterface failed: %x", hr));
        
        delete pMDhcpLeaseInfo;

        return hr;
    }

    // Wrap the object in the interface.
    hr = pMDhcpLeaseInfo->Wrap(pLeaseInfo, pRequestID, fGotTtl, lTtl);

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "WrapMDhcpLeaseInfo:Wrap failed: %x", hr));
        
        (*ppInterface)->Release();

        return hr;
    }

    hr = pMDhcpLeaseInfo->SetLocal(fLocal);

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "WrapMDhcpLeaseInfo: SetLocal failed: %x", hr));
        
        (*ppInterface)->Release();

        return hr;
    }

    LOG((MSP_TRACE, "CMDhcp::WrapMDhcpLeaseInfo exit"));
    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// This is a private helper method that munges the arguments into structs
// at the beginning of a Request call.

HRESULT CMDhcp::PrepareArgumentsRequest(
    IN   IMcastScope           * pScope,
    IN   DATE                    LeaseStartTime,
    IN   DATE                    LeaseStopTime,
    IN   long                    lNumAddresses,
    OUT  MCAST_CLIENT_UID      * pRequestIDStruct,
    OUT  MCAST_SCOPE_CTX       * pScopeCtxStruct,
    OUT  MCAST_LEASE_INFO     ** ppLeaseStruct,
    OUT  BOOL                  * pfLocal,
    OUT  long                  * plTtl
    )
{
    LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsRequest enter"));

    _ASSERTE ( ! IsBadReadPtr(pScope, sizeof(IMcastScope) ) );

    _ASSERTE ( ! IsBadWritePtr(pRequestIDStruct, sizeof(MCAST_CLIENT_UID) ) );
    _ASSERTE ( ! IsBadWritePtr(pScopeCtxStruct, sizeof(MCAST_SCOPE_CTX) ) );
    _ASSERTE ( ! IsBadWritePtr(ppLeaseStruct, sizeof(MCAST_LEASE_INFO *) ) );
    _ASSERTE ( ! IsBadWritePtr(pfLocal, sizeof(BOOL) ) );
    _ASSERTE ( ! IsBadWritePtr(plTtl, sizeof(long) ) );

    HRESULT hr;

    //
    // The start time must be less than the stop time.
    //

    if ( LeaseStartTime > LeaseStopTime )
    {
        LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - "
            "start time is greater than stop time - exit E_INVALIDARG"));

        return E_INVALIDARG;
    }

    //
    // lNumAddresses must be stuffed into a WORD for the C API -- check to see if
    // it's in range.
    //

    if ( ( lNumAddresses < 0 ) || ( lNumAddresses > USHRT_MAX ) )
    {
        LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - "
            "invalid number of addresses - exit E_INVALIDARG"));

        return E_INVALIDARG;
    }

    //
    // dynamic_cast to get an object pointer from the passed-in interface
    // pointer. This will cause an exception if the user tries to use their
    // own implementation of IMcastScope, which is quite unlikely.
    //

    CMDhcpScope * pCScope = dynamic_cast<CMDhcpScope *>(pScope);

    if (pCScope == NULL)
    {
        LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - "
            "Unsupported CMDhcpScope object"));

        return E_POINTER;
    }

    //
    // Find out if this scope uses local alloc.
    //

    hr = pCScope->GetLocal(pfLocal);

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest: "
            "GetLocal failed %08x", hr));

        return hr;
    }

    //
    // Find out the ttl to stuff in leases from this scope.
    //

    hr = pCScope->get_TTL( plTtl );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest: "
            "get_TTL failed %08x", hr));

        return hr;
    }

    //
    // Get the normal scope info.
    // ScopeID is stored in network byte order but the get_ method
    // returns it in host byte order for the benefit of apps.
    //

    long lScopeID;

    hr = pScope->get_ScopeID( &lScopeID );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - "
           "can't get scope ID from scope object - exit 0x%08x", hr));

        return hr;
    }

    pScopeCtxStruct->ScopeID.IpAddrV4 = htonl(lScopeID);




    hr = pScope->get_ServerID(
                     (long *) &(pScopeCtxStruct->ServerID.IpAddrV4) );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - "
           "can't get server ID from scope object - exit 0x%08x", hr));

        return hr;
    }

    hr = pScope->get_InterfaceID(
                     (long *) &(pScopeCtxStruct->Interface.IpAddrV4) );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest - "
           "can't get interface ID from scope object - exit 0x%08x", hr));

        return hr;
    }

    //
    // Allocate space for the client UID.
    //

    pRequestIDStruct->ClientUIDLength = MCAST_CLIENT_ID_LEN;
    pRequestIDStruct->ClientUID = new BYTE[ MCAST_CLIENT_ID_LEN ];

    if ( pRequestIDStruct->ClientUID == NULL )
    {
        LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest: out of memory in "
           "buffer allocation"));
        return E_OUTOFMEMORY;
    }

    //
    // Generate a random client UID.
    //

    DWORD dwResult = McastGenUID( pRequestIDStruct );

    if ( dwResult != ERROR_SUCCESS )
    {
        hr = HRESULT_FROM_ERROR_CODE( dwResult );

        LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsRequest: "
            "McastGenUID failed (dw = %d; hr = 0x%08x)", dwResult, hr));

        return hr;
    }

    LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsRequest: before MCAST_LEASE_INFO "
        "alloc; we are asking for %d addresses", lNumAddresses));

    //
    // Allocate the lease info structure.
    // The caller will delete it after the API call.
    // This is a REQUEST, so we do not specify any particular addresses
    // in the array -- we do not need space for them.
    //
    //

    (*ppLeaseStruct) = new MCAST_LEASE_INFO;

    if ( (*ppLeaseStruct) == NULL )
    {
        LOG((MSP_ERROR, "CMDhcp::PrepareArgumentsRequest: out of memory in "
           "MCAST_LEASE_INFO allocation"));
        delete (pRequestIDStruct->ClientUID);
        return E_OUTOFMEMORY;
    }

    //
    // Fill in the times.
    //

    hr = DateToLeaseTime(LeaseStartTime,
                         &((*ppLeaseStruct)->LeaseStartTime));

    if ( FAILED(hr) )
    {
        delete (pRequestIDStruct->ClientUID);
        delete (*ppLeaseStruct);
        return hr;
    }

    hr = DateToLeaseTime(LeaseStopTime,
                         &((*ppLeaseStruct)->LeaseEndTime));

    if ( FAILED(hr) )
    {
        delete (pRequestIDStruct->ClientUID);
        delete (*ppLeaseStruct);
        return hr;
    }

    //
    // Fill in the address info fields.
    //

    (*ppLeaseStruct)->ServerAddress.IpAddrV4 = 0;

    (*ppLeaseStruct)->AddrCount = (WORD) lNumAddresses; // checked above
    
    //
    // This is a REQUEST, so we do not specify any particular addresses
    // in the array -- we make the array NULL.
    //

    (*ppLeaseStruct)->pAddrBuf = NULL;

    LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsRequest exit"));

    return S_OK;
}

/////////////////////////////////////////////////////////////////////////////
// This is a private helper method that munges the arguments into structs
// at the beginning of a Renew or Release call.

HRESULT CMDhcp::PrepareArgumentsNonRequest(
    IN   IMcastLeaseInfo       * pLease,
    OUT  MCAST_CLIENT_UID      * pRequestIDStruct,
    OUT  MCAST_LEASE_INFO     ** ppLeaseStruct,
    OUT  BOOL                  * pfLocal,
    OUT  BOOL                  * pfGotTtl,
    OUT  long                  * plTtl
    )
{
    LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsNonRequest enter"));

    if ( IsBadReadPtr(pLease, sizeof(IMcastLeaseInfo) ) )
    {
        LOG((MSP_ERROR, "PrepareArgumentsNonRequest: bad pLease pointer argument"));

        return E_POINTER;
    }

    _ASSERTE ( ! IsBadWritePtr(pRequestIDStruct, sizeof(MCAST_CLIENT_UID) ) );
    _ASSERTE ( ! IsBadWritePtr(ppLeaseStruct, sizeof(MCAST_LEASE_INFO *) ) );
    _ASSERTE ( ! IsBadWritePtr(pfLocal, sizeof(BOOL) ) );
    _ASSERTE ( ! IsBadWritePtr(pfGotTtl, sizeof(BOOL) ) );
    _ASSERTE ( ! IsBadWritePtr(plTtl, sizeof(long) ) );

    HRESULT hr;

    // We approach things in a completely different way here, compared
    // to the other PrepareArguments method -- we use
    // dynamic_cast to get an object pointer from the passed-in interface
    // pointer. This will cause an exception if the user tries to use their
    // own implementation of IMcastRequestID, which is quite unlikely.

    CMDhcpLeaseInfo * pCLease = dynamic_cast<CMDhcpLeaseInfo *>(pLease);

    if (pCLease == NULL)
    {
        LOG((MSP_ERROR, "PrepareArgumentsNonRequest: Unsupported CMDhcpLeaseInfo object"));

        return E_POINTER;
    }

    //
    // Find out if this lease was obtained using local alloc.
    //

    hr = pCLease->GetLocal(pfLocal);

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "PrepareArgumentsNonRequest: "
            "GetLocal failed %08x", hr));

        return hr;
    }

    //
    // If the lease had a TTL set, then retrieve it for use in a
    // resulting response. Else just say we don't have a ttl.
    //

    hr = pCLease->get_TTL( plTtl );
    *pfGotTtl = SUCCEEDED(hr);

    //
    // Get our request ID from the lease info object.
    //

    pRequestIDStruct->ClientUIDLength = MCAST_CLIENT_ID_LEN;

    pRequestIDStruct->ClientUID = new BYTE[ MCAST_CLIENT_ID_LEN ];

    if (pRequestIDStruct->ClientUID == NULL)
    {
        LOG((MSP_ERROR, "PrepareArgumentsNonRequest: out of memory in "
           "buffer allocation"));
    
        return E_OUTOFMEMORY;
    }

    hr = pCLease->GetRequestIDBuffer(pRequestIDStruct->ClientUIDLength,
                                     pRequestIDStruct->ClientUID);

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "PrepareArgumentsNonRequest: RequestID "
            "GetBuffer failed %08x", hr));
        
        delete (pRequestIDStruct->ClientUID);

        return hr;
    }

    //
    // Get the rest of the stuff, which belongs in the straight lease info
    // structure, from the lease info object.
    //

    // this does a new for us
    hr = pCLease->GetStruct(ppLeaseStruct);

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "PrepareArgumentsNonRequest - "
            "failed to grab pLeaseStruct - 0x%08x", hr));
        
        delete (pRequestIDStruct->ClientUID);
    
        return hr;
    }

    LOG((MSP_TRACE, "CMDhcp::PrepareArgumentsNonRequest exit"));

    return S_OK;
}

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//
// VerifyAndGetArrayBounds
//
// Helper function for variant/safearrays
//
// Array
//      IN Variant that contains a safearray
//
// ppsa
//      OUT safearray returned here
//
// pllBound
//      OUT array lower bound returned here
//
// pluBound
//      OUT array upper bound returned here
//
// RETURNS
//
// verifies that Array contains an array, and returns the array, upper
// and lower bounds.
//
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

static HRESULT
VerifyAndGetArrayBounds(
                        VARIANT Array,
                        SAFEARRAY ** ppsa,
                        long * pllBound,
                        long * pluBound
                       )
{
    LOG((MSP_TRACE, "VerifyAndGetArrayBounds: enter"));

    UINT                uDims;
    HRESULT             hr = S_OK;


    //
    // see if the variant & safearray are valid
    //
    try
    {
        if (!(V_ISARRAY(&Array)))
        {
            if ( Array.vt == VT_NULL )
            {
                //
                // null is usually valid
                //

                *ppsa = NULL;

                LOG((MSP_INFO, "Returning NULL array"));

                return S_FALSE;
            }

            LOG((MSP_ERROR, "Array - not an array"));

            return E_INVALIDARG;
        }

        if ( Array.parray == NULL )
        {
            //
            // null is usually valide
            //
            *ppsa = NULL;

            LOG((MSP_INFO, "Returning NULL array"));

            return S_FALSE;
        }

        *ppsa = V_ARRAY(&Array);

        uDims = SafeArrayGetDim( *ppsa );

    }
    catch(...)
    {
        hr = E_POINTER;
    }


    if (!SUCCEEDED(hr))
    {
        LOG((MSP_ERROR, "Array - invalid array"));

        return hr;
    }


    //
    // verify array
    //
    if ( uDims != 1 )
    {
        if ( uDims == 0 )
        {
            LOG((MSP_ERROR, "Array - has 0 dim"));

            return E_INVALIDARG;
        }
        else
        {
            LOG((MSP_WARN, "Array - has > 1 dim - will only use 1"));
        }
    }


    //
    // Get array bounds
    //
    SafeArrayGetUBound(
                       *ppsa,
                       1,
                       pluBound
                      );

    SafeArrayGetLBound(
                       *ppsa,
                       1,
                       pllBound
                      );

    LOG((MSP_TRACE, "VerifyAndGetArrayBounds: exit"));
    return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
// CMDhcp::FinalContruct
//
// Parameters
//     none
//
// Return Values
//     S_OK             Success
//     E_OUTOFMEMORY    Not enough memory to create free thread marshaler
//     E_FAIL           We are running the wrong version of dhcpcsvc.dll
//
// Description
//     This is called on construction. It creates the free threaded marshaler
//     and checks if the C API's DLL is the same version that we were compiled
//     with.
//////////////////////////////////////////////////////////////////////////////

HRESULT CMDhcp::FinalConstruct(void)
{
    LOG((MSP_TRACE, "CMDhcp::FinalConstruct - enter"));

    HRESULT hr = CoCreateFreeThreadedMarshaler( GetControllingUnknown(),
                                                & m_pFTM );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CMDhcp::FinalConstruct - "
            "failed to create FTM - exit 0x%08x", hr));

        //
        // Now, FinalRelease will get called, and then CoCreate will return
        // failure.
        //

        return hr;
    }

    // Munil uses this as an IN/OUT parameter.
    DWORD dwVersion = MCAST_API_CURRENT_VERSION; // defined in mdhccapi.h
    DWORD dwCode;

    dwCode = McastApiStartup(&dwVersion);

    // dwVersion now contains the actual version of the C API, but we don't
    // really care what it is.

    if (dwCode == ERROR_SUCCESS)
    {
        m_fApiIsInitialized = TRUE;

        LOG((MSP_TRACE, "CMDhcp::FinalConstruct - C API version "
            "is >= our version - exit S_OK"));

        return S_OK;
    }
    else
    {
        LOG((MSP_ERROR, "CMDhcp::FinalConstruct - C API version "
            "is < our version - exit E_FAIL"));

        //
        // Now, FinalRelease will get called, and then CoCreate will return
        // failure.
        //

        return E_FAIL;
    }
}

//////////////////////////////////////////////////////////////////////////////
//
// CMDhcp::FinalRelease
//
// Parameters
//     none
//
// Return Values
//     none
//
// Description
//     This is called on destruction. It releases the free threaded marshaler
//     and cleans up the C API instance. Note that it is also called if
//     FinalConstruct failed.
//
//////////////////////////////////////////////////////////////////////////////

void CMDhcp::FinalRelease(void)
{
    LOG((MSP_TRACE, "CMDhcp::FinalRelease - enter"));

    if ( m_pFTM )
    {
        m_pFTM->Release();
    }

    if ( m_fApiIsInitialized )
    {
        McastApiCleanup();
    }

    LOG((MSP_TRACE, "CMDhcp::FinalRelease - exit"));
}


//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
// IMcastAddressAllocation
//
// This is the main interface for the MDHCP address allocation.  An
// application will call CoCreateInstance on this interface to create the
// MDHCP client interface object.
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////


//////////////////////////////////////////////////////////////////////////////
// IMcastAddressAllocation::get_Scopes
//
// Parameters
//     pVariant [out] Pointer to a VARIANT that will receive an OLE-standard
//                      Collection of available multicast scopes. Each scope
//                      is an IDispatch pointer to an object that implements
//                      IMcastScope.
//
// Return Values
//     S_OK             Success
//     E_POINTER        The caller passed in an invalid pointer argument
//     E_FAIL           There are no scopes available
//     E_OUTOFMEMORY    Not enough memory to create the required objects
//     other            From MDhcpEnumerateScopes (win32 call)
//
// Description
//     This method is primarily for VB and other scripting languages; C++
//     programmers use EnumerateScopes instead.
/////////////////////////////////////////////////////////////////////////////

STDMETHODIMP CMDhcp::get_Scopes(
        VARIANT * pVariant
        )
{
    LOG((MSP_TRACE, "CMDhcp::get_Scopes enter"));

    // Check argument.
    if ( IsBadWritePtr(pVariant, sizeof(VARIANT) ) )
    {
        LOG((MSP_ERROR, "get_Scopes: invalid pointer passed in "
                "(%08x)", pVariant));
        return E_POINTER;
    }

    DWORD               i;
    DWORD               dwScopeCount = 0;
    MCAST_SCOPE_ENTRY * pScopeList = NULL;
    HRESULT             hr;
    BOOL                fLocal;

    //
    // Grab the scopes from the C API.
    //

    hr = GetScopeList(&dwScopeCount, &pScopeList, &fLocal);
    
    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "get_Scopes: GetScopeList failed "
                "(hr = %08x)", hr));
        return hr;
    }

    //
    // Now we wrap the array in COM wrappers.
    //

    IMcastScope ** ppWrappers = NULL;

    // this does a new into ppWrappers
    // as well as dwScopeCount individual object instantiations

    hr = CreateWrappers(dwScopeCount,
                        pScopeList,
                        &ppWrappers,
                        fLocal);

    // At this point we've got a bunch of COM objects that contain
    // individual scopes, and so we no longer need the array of
    // scopes. Even if CreateWrappers failed we must get rid of
    // the array of scopes.

    delete pScopeList;

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "get_Scopes: CreateWrappers failed "
                "(hr = %08x)", hr));
        return hr;
    }

    //
    // create the collection object - see collect.h
    //

    typedef CTapiIfCollection< IMcastScope * > ScopeCollection;
    CComObject<ScopeCollection> * p;
    hr = CComObject<ScopeCollection>::CreateInstance( &p );

    if ( (FAILED(hr)) || (p == NULL) )
    {
        LOG((MSP_ERROR, "get_Scopes: Could not create CTapiIfCollection "
            "object - return %lx", hr ));

        for (DWORD i = 0 ; i < dwScopeCount; i++) delete ppWrappers[i];
        delete ppWrappers;

        return hr;
    }

    //
    // get the Collection's IDispatch interface
    //

    IDispatch * pDisp;
    hr = p->_InternalQueryInterface( IID_IDispatch, (void **) &pDisp );

    if ( FAILED(hr) )
    {
        // Query interface failed so we don't know that if it addreffed
        // or not.

        LOG((MSP_ERROR, "get_Scopes: QI for IDispatch failed on "
            "ScopeCollection - %lx", hr ));

        delete p;

        //
        // PREFIXBUG 433295 - VLD
        // ppWrappers was allocated into CreateWrappers() method
        // we should deallocate it
        //

        for (DWORD i = 0 ; i < dwScopeCount; i++) delete ppWrappers[i];
        delete ppWrappers;

        return hr;
    }

    // initialize it using an iterator -- pointers to the beginning and
    // the ending element plus one.

    hr = p->Initialize( dwScopeCount,
                        ppWrappers,
                        ppWrappers + dwScopeCount );

    // ZoltanS fixed:
    // We started off by creating and calling QI on each object in
    // CreateWrappers. Then we passed the array of pointers to objects to
    // the Initialize method of the collection object. This method
    // called QI on each object to get each object's IDispatch pointer.
    // So now we are at refcount 2. We now Release() each object and get
    // back to refcount 1 on each object. Of course we must even do this
    // if the initialize failed (in that case to delete them outright).

    for (i = 0; i < dwScopeCount; i++)
    {
        ppWrappers[i]->Release();
    }

    // The array of pointers must now be deleted -- we now store the
    // objects in the collection instead. (or nowhere if initialize failed)

    delete ppWrappers;

    if (FAILED(hr))
    {
        // Initialize has failed -- we assume it did nothing, so we must
        // release all the COM objects ourselves

        LOG((MSP_ERROR, "get_Scopes: Could not initialize "
            "ScopeCollection object - return %lx", hr ));

        p->Release();

        return hr;
    }

    //
    // put the IDispatch interface pointer into the variant
    //

    LOG((MSP_INFO, "placing IDispatch value %08x in variant", pDisp));

    VariantInit(pVariant);
    pVariant->vt = VT_DISPATCH;
    pVariant->pdispVal = pDisp;

    LOG((MSP_TRACE, "CMDhcp::get_Scopes exit - return %lx", hr ));
    return hr;
}


//////////////////////////////////////////////////////////////////////////////
// IMcastAddressAllocation::EnumerateScopes
//
// Parameters
//     ppEnumMcastScope [out] Returns a pointer to a new IEnumMcastScope
//                               object. IEnumMcastScope is a standard
//                               enumerator interface that enumerates
//                               IMcastScope objects.
//
// Return Values
//     S_OK             Success
//     E_POINTER        The caller passed in an invalid pointer argument
//     E_FAIL           There are no scopes available
//     E_OUTOFMEMORY    Not enough memory to create the required objects
//     other            From MDhcpEnumerateScopes (win32 call)
//
// Description
//     This method is primarily for C++ programmers; VB and other scripting
//     languages use get_Scopes instead.
/////////////////////////////////////////////////////////////////////////////

STDMETHODIMP CMDhcp::EnumerateScopes(
        IEnumMcastScope ** ppEnumMcastScope
        )
{
    LOG((MSP_TRACE, "CMDhcp::EnumerateScopes enter"));

    if ( IsBadWritePtr(ppEnumMcastScope, sizeof(IEnumMcastScope *) ) )
    {
        LOG((MSP_ERROR, "EnumerateScopes: bad pointer argument "
                "(%08x)", ppEnumMcastScope));
        return E_POINTER;
    }

    DWORD               dwScopeCount = 0;
    MCAST_SCOPE_ENTRY * pScopeList = NULL;
    HRESULT             hr;
    BOOL                fLocal;

    //
    // Grab the scopes from the C API.
    //

    hr = GetScopeList(&dwScopeCount, &pScopeList, &fLocal);
    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "EnumerateScopes: GetScopeList failed "
                "(hr = %08x)", hr));
        return hr;
    }

    //
    // Now we wrap the array in COM wrappers.
    //

    IMcastScope ** ppWrappers = NULL;

    // this does a new into ppWrappers
    hr = CreateWrappers(dwScopeCount,
                        pScopeList,
                        &ppWrappers,
                        fLocal);

    // At this point we've got a bunch of COM objects that contain
    // individual scopes, and so we no longer need the array of
    // scopes. Even if CreateWrappers failed we must get rid of
    // the array of scopes.

    delete pScopeList;

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "EnumerateScopes: CreateWrappers failed "
                "(hr = %08x)", hr));
        return hr;
    }

    //
    // Now we create and set up the enumerator.
    //

    typedef _CopyInterface<IMcastScope> CCopy;
    typedef CSafeComEnum<IEnumMcastScope, &IID_IEnumMcastScope,
        IMcastScope *, CCopy> CEnumerator;

    CComObject<CEnumerator> *pEnum = NULL;

    hr = CComObject<CEnumerator>::CreateInstance(&pEnum);
    if ((FAILED(hr)) || (pEnum == NULL))
    {
        LOG((MSP_ERROR, "Couldn't create enumerator object: %08x", hr));
        delete ppWrappers;
        return hr;
    }

    // Get the IEnumMcastScope interface.
    hr = pEnum->_InternalQueryInterface(
        IID_IEnumMcastScope,
        (void **)ppEnumMcastScope
        );

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "QI on enumerator object failed: %08x", hr));
        delete ppWrappers;

        delete pEnum;

        return hr;
    }

    // This takes ownership of the wrapper list so we will no longer
    // delete the wrapper list if this succeeds.
    hr = pEnum->Init(ppWrappers, ppWrappers + dwScopeCount, NULL,
                     AtlFlagTakeOwnership);

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "Init enumerator object failed: %08x", hr));
        delete ppWrappers;
        pEnum->Release();
        return hr;
    }

    LOG((MSP_TRACE, "CMDhcp::EnumerateScopes exit"));
    return S_OK;
}


//////////////////////////////////////////////////////////////////////////////
// IMcastAddressAllocation::RequestAddress
//
// Parameters
//     pScope          [in]  This identifies the multicast scope from which
//                             the application wants to be given an address.
//                             The application first calls get_Scopes or
//                             EnumerateScopes to obtain a list of available
//                             scopes.
//     LeaseStartTime  [in]  Requested time for the lease on these addresses
//                             to start / begin. The start time that is
//                             actually granted may be different.
//     LeaseStopTime   [in]  Requested time for the lease on these addresses
//                             to stop / end. The stop time that is actually
//                             granted may be different.
//     NumAddresses    [in]  The number of addresses requested. Fewer
//                             addresses may actually be granted. NOTE:
//                             although these COM interfaces and their
//                             implementation support allocation of multiple
//                             addresses at a time, this is not currently
//                             supported by the underlying Win32 calls. You
//                             may need to use a loop instead.
//     ppLeaseResponse [out] Pointer to an interface pointer that will be set
//                             to point to a new IMcastLeaseInfo object. This
//                             interface can then be used to discover the
//                             actual attributes of the granted lease. See
//                             below for a description of IMcastScope.
//
// Return Values
//     S_OK            Success
//     E_POINTER       The caller passed in an invalid pointer argument
//     E_OUTOFMEMORY   Not enough memory to create the required objects
//     E_INVALIDARG    Requested too many addresses, format conversion
//                     failed for the start time or stop time, or the stop
//                     time is less than the start time
//     other           From MdhcpRequestAddress (win32 call)
//
// Description
//     Call this method to obtain a new lease for one or more multicast
//     addresses. You will first need to call EnumerateScopes or get_Scopes,
//     as well as CreateMDhcpRequestID.
/////////////////////////////////////////////////////////////////////////////

STDMETHODIMP CMDhcp::RequestAddress(IMcastScope      * pScope,
                                    DATE               LeaseStartTime,
                                    DATE               LeaseStopTime,
                                    long               NumAddresses,
                                    IMcastLeaseInfo ** ppLeaseResponse)
{
    LOG((MSP_TRACE, "CMDhcp::RequestAddress enter: asking for %d addresses",
        NumAddresses));

    if ( IsBadReadPtr( pScope, sizeof(IMcastScope) ) )
    {
        LOG((MSP_ERROR, "CMDhcp::RequestAddress - "
            "bad scope pointer - exit E_POINTER"));

        return E_POINTER;
    }

    // no need to check ppLeaseResponse -- WrapMDhcpLeaseInfo handles it

    MCAST_CLIENT_UID   requestID;
    MCAST_SCOPE_CTX    scopeCtx;
    MCAST_LEASE_INFO * pLeaseRequest;
    HRESULT            hr;
    BOOL               fLocal;
    long               lTtl;

    // Munge input arguments into three structs for passing to the C API.
    // pLeaseRequest and requestID->ClientUID are allocated. We must delete them when
    // we're done.
    hr = PrepareArgumentsRequest(pScope,         // goes into scopeCtx
                                 LeaseStartTime, // goes into leaseRequest
                                 LeaseStopTime,  // goes into leaseRequest
                                 NumAddresses,   // goes into leaseRequest
                                 &requestID,     // we generate it
                                 &scopeCtx,
                                 &pLeaseRequest,
                                 &fLocal,
                                 &lTtl
                                );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CMDHcp::RequestAddress - "
            "PrepareArgumentsRequest failed - exit 0x%08x", hr));

        return hr;
    }


    MCAST_LEASE_INFO * pLeaseResponse = (MCAST_LEASE_INFO *) new BYTE
        [ sizeof(MCAST_LEASE_INFO) + sizeof(DWORD) * NumAddresses ];

    if (pLeaseResponse == NULL)
    {
        LOG((MSP_ERROR, "RequestAddress: out of memory in response alloc"));
        delete requestID.ClientUID;
        delete pLeaseRequest;
        return E_OUTOFMEMORY;
    }

    DWORD dwCode;

    dwCode = LocalRequestAddress(fLocal,
                                 &requestID,
                                 &scopeCtx,
                                 pLeaseRequest,
                                 pLeaseResponse);

    // No matter what, we no longer need this.
    delete pLeaseRequest;

    if (dwCode != ERROR_SUCCESS)
    {
        LOG((MSP_ERROR, "RequestAddress: C API call failed "
            "(code = %d)", dwCode));
        delete requestID.ClientUID;
        delete pLeaseResponse;
        return HRESULT_FROM_ERROR_CODE(dwCode);
    }

    // Wrap the lease response, along with the requestID, in an interface
    // and return it.
    // The wrapper assumes ownership of the lease structure and
    // requestID.clientuid.

    hr = WrapMDhcpLeaseInfo(TRUE,
                            lTtl,
                            fLocal,
                            pLeaseResponse,
                            &requestID,
                            ppLeaseResponse);

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "RequestAddress: WrapMDhcpLeaseInfo failed "
            "(hr = %08x)", hr));
        
        delete pLeaseResponse;
        delete requestID.ClientUID;

        return hr;
    }

    return S_OK;
}


//////////////////////////////////////////////////////////////////////////////
// IMcastAddressAllocation::RenewAddress
//
// Parameters
//     pRenewRequest   [in]  Pointer to an IMcastLeaseInfo object specifying
//                             the attributes of the requested renewal, such
//                             as which address(es) to renew. This is
//                             obtained by calling CreateLeaseInfo.
//     ppRenewResponse [out] Pointer to an interface pointer that will be set
//                             to point to a new IMcastLeaseInfo object. This
//                             interface can then be used to discover the
//                             attributes of the renewed lease. See below for
//                             a description of IMcastScope.
//
// Return Values
//     S_OK			Success
//     E_OUTOFMEMORY	Not enough memory to create the required objects
//     E_POINTER		The caller passed in an invalid pointer argument
//     E_INVALIDARG     Start time is greater than stop time
//     other			From MdhcpRenewAddress (win32 call)
//
// Description
//     To renew a lease, call CreateLeaseInfo to specify the parameters of
//     the renewal request, and then call this method to make the request.
/////////////////////////////////////////////////////////////////////////////

STDMETHODIMP CMDhcp::RenewAddress(
        long                        lReserved, // unused
        IMcastLeaseInfo           * pRenewRequest,
        IMcastLeaseInfo          ** ppRenewResponse
        )
{
    LOG((MSP_TRACE, "CMDhcp::RenewAddress enter"));

    // no need to check pRequestID or pRenewRequest --
    // PrepareArgumentsNonRequest handles that.
    // ppRenewResponse check handled by WrapMDhcpLeaseInfo

    MCAST_CLIENT_UID   requestID;
    MCAST_LEASE_INFO * pRenewRequestStruct;
    HRESULT            hr;
    BOOL               fLocal;
    BOOL               fGotTtl;
    long               lTtl;

    // Munge input arguments into three structs for passing to the C API.
    // pLeaseRequest and requestID->ClientUID are allocated. We must delete them when
    // we're done.
    hr = PrepareArgumentsNonRequest(pRenewRequest,
                                    &requestID,
                                    &pRenewRequestStruct,
                                    &fLocal,
                                    &fGotTtl,
                                    &lTtl);

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "RenewAddress: PrepareArgumentsNonRequest failed "
            "(hr = %08x)", hr));

        return hr;
    }

    //
    // Check that the start time is less than the stop time
    //

    if ( pRenewRequestStruct->LeaseStartTime >
         pRenewRequestStruct->LeaseEndTime )
    {
        LOG((MSP_ERROR, "PrepareArgumentsNonRequest - "
            "start time %d is greater than stop time %d - exit E_INVALIDARG",
            pRenewRequestStruct->LeaseStartTime,
            pRenewRequestStruct->LeaseEndTime));

        delete requestID.ClientUID;
        delete pRenewRequestStruct;

        return E_INVALIDARG;
    }


    MCAST_LEASE_INFO * pRenewResponse = (MCAST_LEASE_INFO *) new BYTE
        [ sizeof(MCAST_LEASE_INFO) +
          sizeof(DWORD) * pRenewRequestStruct->AddrCount ];

    if ( pRenewResponse == NULL )
    {
        LOG((MSP_ERROR, "RenewAddress: out of memory in response alloc"));

        delete requestID.ClientUID;
        delete pRenewRequestStruct;

        return E_OUTOFMEMORY;
    }

    DWORD dwCode = LocalRenewAddress(fLocal,
                                     &requestID,
                                     pRenewRequestStruct,
                                     pRenewResponse);

    //
    // We have performed the renew request so we no longer need the struct
    // for the request, even if the request failed.
    //

    delete pRenewRequestStruct;

    if ( dwCode != ERROR_SUCCESS )
    {
        LOG((MSP_ERROR, "RenewAddress: C API call failed "
            "(code = %d)", dwCode));

        delete requestID.ClientUID;
        delete pRenewResponse;

        return HRESULT_FROM_ERROR_CODE(dwCode);
    }

    //
    // Wrap pRenewResponse and the requestID in an interface and return it.
    // the wrapper takes ownership of the requestID.clientUID and the
    // response struct
    //

    hr = WrapMDhcpLeaseInfo(fGotTtl,
                            lTtl,
                            fLocal,
                            pRenewResponse,
                            &requestID,
                            ppRenewResponse);

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "RenewAddress: WrapMDhcpLeaseInfo failed "
            "(hr = %08x)", hr));

        delete requestID.ClientUID;
        delete pRenewResponse;

        return hr;
    }

    LOG((MSP_TRACE, "CMDhcp::RenewAddress exit"));

    return S_OK;
}


//////////////////////////////////////////////////////////////////////////////
// IMcastAddressAllocation::ReleaseAddress
//
// Parameters
//     pReleaseRequest [in] Pointer to an IMcastLeaseInfo object specifying
//                            the which address(es) to release. This is
//                            returned from a previous RequestAddress call or
//                            obtained by calling CreateLeaseInfo.
//
// Return Values
//     S_OK             Success
//     E_POINTER        The caller passed in an invalid pointer argument
//     E_OUTOFMEMORY    Not enough memory to make the request
//     other            From MdhcpReleaseAddress (win32 call)
//
// Description
//     Use this method to release a lease that was obtained previously.
/////////////////////////////////////////////////////////////////////////////

STDMETHODIMP CMDhcp::ReleaseAddress(
        IMcastLeaseInfo  * pReleaseRequest
        )
{
    LOG((MSP_TRACE, "CMDhcp::ReleaseAddress enter"));

    // no need to check pReleaseRequest --
    // PrepareArgumentsNonRequest handles that.

    MCAST_CLIENT_UID   requestID;
    MCAST_LEASE_INFO * pReleaseRequestStruct;
    HRESULT            hr;
    BOOL               fLocal;
    BOOL               fGotTtl; // unused after call
    long               lTtl;    // unused after call

    hr = PrepareArgumentsNonRequest(pReleaseRequest,
                                    &requestID,
                                    &pReleaseRequestStruct,
                                    &fLocal,
                                    &fGotTtl,
                                    &lTtl
                                    );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "ReleaseAddress: PrepareArgumentsNonRequest failed "
            "(hr = %08x)", hr));
        return hr;
    }

    DWORD dwCode;
    dwCode = LocalReleaseAddress(fLocal,
                                 &requestID,
                                 pReleaseRequestStruct);

    //
    // These were allocated by PrepareArgumentsNonRequest and there is no one
    // to own them now -- we delete them. This is true even if the
    // LocalReleaseAddress call failed.
    //

    delete pReleaseRequestStruct;
    delete requestID.ClientUID;

    if ( dwCode != ERROR_SUCCESS )
    {
        LOG((MSP_ERROR, "ReleaseAddress: C API call failed "
            "(code = %d)", dwCode));

        return HRESULT_FROM_ERROR_CODE(dwCode);
    }

    LOG((MSP_TRACE, "CMDhcp::ReleaseAddress exit"));

    return S_OK;
}


//////////////////////////////////////////////////////////////////////////////
// IMcastAddressAllocation::CreateLeaseInfo
//
// Parameters
//     LeaseStartTime    [in] The start time of the lease.
//     LeaseStopTime     [in] The stop time of the lease.
//     dwNumAddresses    [in] The number of addresses associated with the
//                               lease.
//     ppAddresses       [in] An array of LPWSTRs of size dwNumAddresses. Each
//                               LPWSTR (Unicode string pointer) is an IPv4
//                               address in "dot-quad" notation; e.g.
//                               "123.234.12.17".
//     pRequestID        [in] An LPWSTR (Unicode string pointer) specifying
//                               the request ID for the original request.
//     pServerAddress    [in] An LPWSTR (Unicode string pointer) specifying
//                               the address of the server that granted the
//                               original request. This address is an IPv4
//                               address in "dot quad" notation; e.g.
//                               "123.234.12.17".
//     ppReleaseRequest  [out] Returns a pointer to the IMcastLeaseInfo
//                               interface on the newly created lease
//                               information object.
//
// Return Values
//     S_OK             Success
//     E_POINTER        The caller passed in an invalid pointer argument
//     E_OUTOFMEMORY    Not enough memory to create the required objects
//     E_INVALIDARG     An error occured during the date format conversion
//
// Description
//     Use this method to create a lease information object for a subsequent
//     RenewAddress or ReleaseAddress call. This method is primarily for C++
//     programmers; VB and other scripting languages use
//     CreateLeaseInfoFromVariant instead.
//     The dwNumAddresses, ppAddresses, pRequestID, and pServerAddress
//     parameters are normally obtained by calling the corresponding
//     IMcastLeaseInfo methods on the lease info object corresponding to the
//     original request. These values should be saved in persistent storage
//     between executions of the application program. If you are renewing or
//     releasing a lease that was requested during the same run of the
//     application, you have no reason to use CreateLeaseInfo; just pass the
//     existing IMcastLeaseInfo pointer to RenewAddress or ReleaseAddress.
//////////////////////////////////////////////////////////////////////////////
#include <atlwin.cpp>

STDMETHODIMP CMDhcp::CreateLeaseInfo(
        DATE               LeaseStartTime,
        DATE               LeaseStopTime,
        DWORD              dwNumAddresses,
        LPWSTR *           ppAddresses,
        LPWSTR             pRequestID,
        LPWSTR             pServerAddress,
        IMcastLeaseInfo ** ppReleaseRequest
        )
{
    LOG((MSP_TRACE, "CMDhcp::CreateLeaseInfo enter"));

    if ( IsBadWritePtr(ppReleaseRequest, sizeof(IMcastLeaseInfo *) ) )
    {
        LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfo - "
            "invalid lease return pointer: 0x%08x - exit E_POINTER",
            ppReleaseRequest));
        
        return E_POINTER;
    }

    if ( IsBadStringPtr(pRequestID, (UINT) -1 ) )
    {
        LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfo - "
            "invalid RequestID pointer: 0x%08x - exit E_POINTER",
            pRequestID));

        return E_POINTER;
    }

    if ( ( dwNumAddresses < 1 ) || ( dwNumAddresses > USHRT_MAX ) )
    {
        LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfo - "
            "invalid number of addresses: %d - exit E_INVALIDARG",
            dwNumAddresses));

        return E_INVALIDARG;
    }

    if (IsBadReadPtr(ppAddresses, sizeof(LPWSTR) * dwNumAddresses) )
    {
        LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfo - "
            "invalid addresses array pointer: 0x%08x - exit E_POINTER",
            ppAddresses));

        return E_POINTER;
    }

    if ( IsBadStringPtr(pServerAddress, (UINT) -1 ) )
    {
        LOG((MSP_ERROR, "CreateLeaseInfo: invalid Server Address pointer: %08x",
            pRequestID));
        return E_POINTER;
    }

    HRESULT hr;

    // create the com object.
    CComObject<CMDhcpLeaseInfo> * pMDhcpLeaseInfo;
    hr = CComObject<CMDhcpLeaseInfo>::CreateInstance(&pMDhcpLeaseInfo);

    if ( (FAILED(hr)) || (pMDhcpLeaseInfo == NULL) )
    {
        LOG((MSP_ERROR, "CreateLeaseInfo: can't create MDhcpLeaseInfo Object."));
        return hr;
    }

    // Get the IMcastLeaseInfo interface.
    hr = pMDhcpLeaseInfo->_InternalQueryInterface(
        IID_IMcastLeaseInfo,
        (void **)ppReleaseRequest
        );

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CreateLeaseInfo: QueryInterface failed: %x", hr));
        delete pMDhcpLeaseInfo;
        return hr;
    }

    // Fill in the object with the stuff the user wanted.
    hr = pMDhcpLeaseInfo->Initialize(LeaseStartTime,
                                     LeaseStopTime,
                                     dwNumAddresses,
                                     ppAddresses,
                                     pRequestID,
                                     pServerAddress);

    if ( FAILED(hr) )
    {
        LOG((MSP_ERROR, "CreateLeaseInfo: Initialize failed: %x", hr));
        delete pMDhcpLeaseInfo;
        return hr;
    }

    LOG((MSP_TRACE, "CMDhcp::CreateLeaseInfo exit"));
    return S_OK;
}


//////////////////////////////////////////////////////////////////////////////
// IMcastAddressAllocation::CreateLeaseInfoFromVariant
//
// Parameters
//     LeaseStartTime   [in] The start time of the lease.
//     LeaseStopTime    [in] The stop time of the lease.
//     vAddresses       [in] A VARIANT containing a SafeArray of BSTRs. Each
//                              BSTR (size-tagged Unicode string pointer) is
//                              an IPv4 address in "dot-quad" notation; e.g.
//                              "123.234.12.17".
//     pRequestID       [in] A BSTR (size-tagged Unicode string pointer)
//                              specifying the request ID for the original
//                              request.
//     pServerAddress   [in]  A BSTR (size-tagged Unicode string pointer)
//                              specifying the address of the server that
//                              granted the original request. This address is
//                              an IPv4 address in "dot quad" notation; e.g.
//                              "123.234.12.17".
//     ppReleaseRequest [out] Returns a pointer to the IMcastLeaseInfo
//                              interface on the newly created lease
//                              information object.
//
// Return Values
//     S_OK             Success
//     E_POINTER        The caller passed in an invalid pointer argument
//     E_OUTOFMEMORY    Not enough memory to create the required objects
//     E_INVALIDARG     An error occured during the date format conversion
//
// Description
//     Use this method to create a lease information object for a subsequent
//     RenewAddress or ReleaseAddress call. This method is primarily for VB
//     and other scripting languages; C++ programmers should use
//     CreateLeaseInfo instead.
//     The dwNumAddresses, ppAddresses, pRequestID, and pServerAddress
//     parameters are normally obtained by calling the corresponding
//     IMcastLeaseInfo methods on the lease info object corresponding to the
//     original request. These values should be saved in persistent storage
//     between executions of the application program. If you are renewing or
//     releasing a lease that was requested during the same run of the
//     application, you have no reason to use CreateLeaseInfoFromVariant; just
//     pass the existing IMcastLeaseInfo pointer to RenewAddress or
//     ReleaseAddress.
/////////////////////////////////////////////////////////////////////////////

STDMETHODIMP CMDhcp::CreateLeaseInfoFromVariant(
        DATE                        LeaseStartTime,
        DATE                        LeaseStopTime,
        VARIANT                     vAddresses,
        BSTR                        pRequestID,
        BSTR                        pServerAddress,
        IMcastLeaseInfo          ** ppReleaseRequest
        )
{
    LOG((MSP_TRACE, "CMDhcp::CreateLeaseInfoFromVariant enter"));

    // We will check the pointers in CreateLeaseInfo.

    HRESULT hr;

    // Get from the variant:
    DWORD    dwNumAddresses;
    LPWSTR * ppAddresses;

    SAFEARRAY * psaAddresses = NULL;  // SafeArray with the addresses
    long        lLowerBound = 0;      // lower bound of safearray
    long        lUpperBound = 0;      // upper bound of safearray

    hr = VerifyAndGetArrayBounds(
                        vAddresses,
                        &psaAddresses,
                        &lLowerBound,
                        &lUpperBound);

    if (FAILED(hr))
    {
        LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfoFromVariant: invalid "
            "VARIANT"));
        return E_INVALIDARG;
    }

    // This is how many addresses we *expect* (may have fewer).
    long lAddrCount = lUpperBound - lLowerBound + 1;

    if (lAddrCount < 1)
    {
        LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfoFromVariant: too few "
            "addresses (check #1) (%d)", lAddrCount));
        return E_INVALIDARG;
    }

    // We allocate as many as we are told to expect, but some of this
    // space may end up getting "wasted" if there are fewer valid ones
    // after all.

    ppAddresses = new LPWSTR[lAddrCount];

    if (ppAddresses == NULL)
    {
        LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfoFromVariant: "
            "out of memory in array allocation"));
        return E_OUTOFMEMORY;
    }

    long lCurrSrc;      // the current element in the safearray      (source)
    long lCurrDest = 0; // the current element in the struct's array (destination)

    // Get the addresses from the SafeArray and put them in our array.
    for (lCurrSrc = lLowerBound; lCurrSrc <= lUpperBound; lCurrSrc++)
    {
        hr = SafeArrayGetElement(
                                 psaAddresses,
                                 &lCurrSrc,
                                 & ( ppAddresses[lCurrDest] )
                                );


        if ( FAILED(hr) )
        {
            LOG((MSP_INFO, "CMDhcp::CreateLeaseInfoFromVariant: "
                "failed to get safearray element %d"
                " - skipping (array range %d-%d)",
                lCurrSrc, lLowerBound, lUpperBound));
        }
        else if ( ppAddresses[lCurrDest] == 0 )
        {
            LOG((MSP_INFO, "CMDhcp::CreateLeaseInfoFromVariant: "
                "got ZERO address from safearray "
                "element %d - skipping (array range %d-%d)",
                lCurrSrc, lLowerBound, lUpperBound));
        }
        else
        {
            // We got an element.
            lCurrDest++;
        }
    }

    // note the number of addresses we actually placed in the array
    dwNumAddresses = (DWORD) lCurrDest;

    if (dwNumAddresses < 1)
    {
        LOG((MSP_ERROR, "CMDhcp::CreateLeaseInfoFromVariant: "
            "too few addresses (check #2)"
            "(%d)", lAddrCount));
        delete ppAddresses;
        return E_INVALIDARG;
    }

    hr = CreateLeaseInfo(LeaseStartTime,
                         LeaseStopTime,
                         dwNumAddresses,
                         ppAddresses,
                         pRequestID,
                         pServerAddress,
                         ppReleaseRequest
                        );

    // No matter what, we no longer need this.
    delete ppAddresses;

    if (FAILED(hr))
    {
        LOG((MSP_TRACE, "CMDhcp::CreateLeaseInfoFromVariant : "
            "CreateLeaseInfo returned %08x", hr));

        return hr;
    }

    LOG((MSP_TRACE, "CMDhcp::CreateLeaseInfoFromVariant exit"));
    return S_OK;
}

// eof