//****************************************************************************
//
//  Module:     ULS.DLL
//  File:       localapp.cpp
//  Content:    This file contains the LocalApplication object.
//  History:
//      Wed 17-Apr-1996 11:13:54  -by-  Viroon  Touranachun [viroont]
//
//  Copyright (c) Microsoft Corporation 1996-1997
//
//****************************************************************************

#include "ulsp.h"
#include "localapp.h"
#include "localprt.h"
#include "attribs.h"
#include "callback.h"
#include "culs.h"

//****************************************************************************
// Event Notifiers
//****************************************************************************
//
//****************************************************************************
// HRESULT
// OnNotifyLocalAppAttributesChangeResult (IUnknown *pUnk, void *pv)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

HRESULT
OnNotifyLocalAppAttributesChangeResult (IUnknown *pUnk, void *pv)
{
    PSRINFO psri = (PSRINFO)pv;

    ((IULSLocalAppNotify*)pUnk)->AttributesChangeResult(psri->uReqID,
                                                        psri->hResult);
    return S_OK;
}

//****************************************************************************
// HRESULT
// OnNotifyLocalAppProtocolChangeResult (IUnknown *pUnk, void *pv)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

HRESULT
OnNotifyLocalAppProtocolChangeResult (IUnknown *pUnk, void *pv)
{
    PSRINFO psri = (PSRINFO)pv;

    ((IULSLocalAppNotify*)pUnk)->ProtocolChangeResult(psri->uReqID,
                                                      psri->hResult);
    return S_OK;
}

//****************************************************************************
// Class Implementation
//****************************************************************************
//
//****************************************************************************
// CLocalApp::CLocalApp (void)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

CLocalApp::CLocalApp (void)
{
    cRef = 0;
    szName = NULL;
    guid = GUID_NULL;
    szMimeType = NULL;
    pAttrs = NULL;
    pConnPt = NULL;
    return;
}

//****************************************************************************
// CLocalApp::~CLocalApp (void)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

CLocalApp::~CLocalApp (void)
{
    CLocalProt *plp;
    HANDLE hEnum;

    // Release the connection point
    //
    if (pConnPt != NULL)
    {
        pConnPt->ContainerReleased();
        ((IConnectionPoint*)pConnPt)->Release();
    };

    // Release the protocol objects
    //
    ProtList.Enumerate(&hEnum);
    while(ProtList.Next(&hEnum, (PVOID *)&plp) == NOERROR)
    {
        plp->Release();
    };
    ProtList.Flush();

    // Release the attributes object
    //
    if (pAttrs != NULL)
    {
        pAttrs->Release();
    };

    // Release the buffer resources
    //
    if (szName != NULL)
    {
        FreeLPTSTR(szName);
    };

    if (szMimeType != NULL)
    {
        FreeLPTSTR(szMimeType);
    };

    return;
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::Init (BSTR bstrName, REFGUID rguid, BSTR bstrMimeType)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::Init (BSTR bstrName, REFGUID rguid, BSTR bstrMimeType)
{
    HRESULT hr;

    // Cache the application information
    //
    guid = rguid;

    hr = BSTR_to_LPTSTR(&szName, bstrName);
    if (SUCCEEDED(hr))
    {
        hr = BSTR_to_LPTSTR(&szMimeType, bstrMimeType);
        if (SUCCEEDED(hr))
        {
            // Initialize the attributes list
            //
            pAttrs = new CAttributes (ULS_ATTRACCESS_NAME_VALUE);

            if (pAttrs != NULL)
            {
                // Make the connection point
                //
                pConnPt = new CConnectionPoint (&IID_IULSLocalAppNotify,
                                                (IConnectionPointContainer *)this);
                if (pConnPt != NULL)
                {
                    ((IConnectionPoint*)pConnPt)->AddRef();
                    hr = NOERROR;
                }
                else
                {
                    hr = ULS_E_MEMORY;
                };
            }
            else
            {
                hr = ULS_E_MEMORY;
            };
        };
    };

    return hr;
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::QueryInterface (REFIID riid, void **ppv)
//
// History:
//  Wed 17-Apr-1996 11:14:08  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::QueryInterface (REFIID riid, void **ppv)
{
    *ppv = NULL;

    if (riid == IID_IULSLocalApplication || riid == IID_IUnknown)
    {
        *ppv = (IULS *) this;
    }
    else
    {
        if (riid == IID_IConnectionPointContainer)
        {
            *ppv = (IConnectionPointContainer *) this;
        };
    };

    if (*ppv != NULL)
    {
        ((LPUNKNOWN)*ppv)->AddRef();
        return S_OK;
    }
    else
    {
        return ULS_E_NO_INTERFACE;
    };
}

//****************************************************************************
// STDMETHODIMP_(ULONG)
// CLocalApp::AddRef (void)
//
// History:
//  Wed 17-Apr-1996 11:14:17  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP_(ULONG)
CLocalApp::AddRef (void)
{
    cRef++;
    return cRef;
}

//****************************************************************************
// STDMETHODIMP_(ULONG)
// CLocalApp::Release (void)
//
// History:
//  Wed 17-Apr-1996 11:14:26  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP_(ULONG)
CLocalApp::Release (void)
{
    cRef--;

    if (cRef == 0)
    {
        delete this;
        return 0;
    }
    else
    {
        return cRef;
    };
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::NotifySink (void *pv, CONN_NOTIFYPROC pfn)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::NotifySink (void *pv, CONN_NOTIFYPROC pfn)
{
    HRESULT hr = S_OK;

    if (pConnPt != NULL)
    {
        hr = pConnPt->Notify(pv, pfn);
    };
    return hr;
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::AttributesChangeResult (CAttributes *pAttributes,
//                                    ULONG uReqID, HRESULT hResult,
//                                    APP_CHANGE_ATTRS uCmd)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::AttributesChangeResult (CAttributes *pAttributes,
                                   ULONG uReqID, HRESULT hResult,
                                   APP_CHANGE_ATTRS uCmd)
{
    SRINFO sri;

    // If the server accepts the changes, modify the local information
    //
    if (SUCCEEDED(hResult))
    {
        // Update based on the command.
        //
        switch(uCmd)
        {
            case ULS_APP_SET_ATTRIBUTES:
                hResult = pAttrs->SetAttributes(pAttributes);
                break;

            case ULS_APP_REMOVE_ATTRIBUTES:
                hResult = pAttrs->RemoveAttributes(pAttributes);
                break;

            default:
                ASSERT(0);
                break;
        };
    };

    // Notify the sink object
    //
    sri.uReqID = uReqID;
    sri.hResult = hResult;
    hResult = NotifySink((void *)&sri, OnNotifyLocalAppAttributesChangeResult);

#ifdef DEBUG
    DPRINTF (TEXT("CLocalApp--current attributes********************\r\n"));
    pAttrs->DebugOut();
    DPRINTF (TEXT("\r\n*************************************************"));
#endif // DEBUG;

    return hResult;
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::ProtocolChangeResult (CLocalProt *pProtocol,
//                                  ULONG uReqID, HRESULT hResult,
//                                  APP_CHANGE_PROT uCmd)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::ProtocolChangeResult (CLocalProt *pProtocol,
                                 ULONG uReqID, HRESULT hResult,
                                 APP_CHANGE_PROT uCmd)
{
    SRINFO sri;

    // If the server accepts the changes, modify the local information
    //
    if (SUCCEEDED(hResult))
    {
        // Update based on the command.
        //
        switch(uCmd)
        {
            case ULS_APP_ADD_PROT:
                hResult = ProtList.Insert((PVOID)pProtocol);
                if (SUCCEEDED(hResult))
                {
                    pProtocol->AddRef();
                };
                break;

            case ULS_APP_REMOVE_PROT:
                hResult = ProtList.Remove((PVOID)pProtocol);
                if (SUCCEEDED(hResult))
                {
                    pProtocol->Release();
                };
                break;

            default:
                ASSERT(0);
                break;
        };
    };

    // Notify the sink object
    //
    sri.uReqID = uReqID;
    sri.hResult = hResult;
    hResult = NotifySink((void *)&sri, OnNotifyLocalAppProtocolChangeResult);

#ifdef DEBUG
    DPRINTF (TEXT("CLocalApp--current Protocols********************\r\n"));
    DebugProtocolDump();
    DPRINTF (TEXT("\r\n*************************************************"));
#endif // DEBUG;

    return hResult;
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::CreateProtocol (BSTR bstrProtocolID, ULONG uPortNumber,
//                            BSTR bstrMimeType,
//                            IULSLocalAppProtocol **ppProtocol)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::CreateProtocol (BSTR bstrProtocolID, ULONG uPortNumber,
                           BSTR bstrMimeType,
                           IULSLocalAppProtocol **ppProtocol)
{
    CLocalProt *plp;
    HRESULT hr;

    // Validate parameter
    //
    if (ppProtocol == NULL)
    {
        return ULS_E_POINTER;
    };

    // Assume failure
    //
    *ppProtocol = NULL;

    // Create a new object
    //
    plp = new CLocalProt;

    if (plp != NULL)
    {
        hr = plp->Init(bstrProtocolID, uPortNumber, bstrMimeType);

        if (SUCCEEDED(hr))
        {
            plp->AddRef();
            *ppProtocol = plp;
        };
    }
    else
    {
        hr = ULS_E_MEMORY;
    };

    return hr;
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::ChangeProtocol (IULSLocalAppProtocol *pProtocol,
//                            ULONG *puReqID, APP_CHANGE_PROT uCmd)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::ChangeProtocol (IULSLocalAppProtocol *pProtocol,
                           ULONG *puReqID, APP_CHANGE_PROT uCmd)
{
    CLocalProt *plp;
    PVOID   pv;
    HRESULT hr;
    HANDLE  hLdapApp;
    LDAP_ASYNCINFO ldai; 
    HANDLE hEnum;

    // Validate parameters
    //
    if ((pProtocol == NULL) ||
        (puReqID == NULL))
    {
        return ULS_E_POINTER;
    };

    hr = pProtocol->QueryInterface(IID_IULSLocalAppProtocol, &pv);

    if (FAILED(hr))
    {
        return ULS_E_PARAMETER;
    };
    pProtocol->Release();

    // Check whether the protocol exists
    //
    ProtList.Enumerate(&hEnum);
    while(ProtList.Next(&hEnum, (PVOID *)&plp) == NOERROR)
    {
        if (plp->IsSameAs((CLocalProt *)pProtocol) == NOERROR)
        {
            break;
        };
    };

    if (plp != NULL)
    {
        // The protocol exists, fail if this add request
        //
        if (uCmd == ULS_APP_ADD_PROT)
        {
            return ULS_E_PARAMETER;
        };
    }
    else
    {
        // The protocol does not exist, fail if this remove request
        //
        if (uCmd == ULS_APP_REMOVE_PROT)
        {
            return ULS_E_PARAMETER;
        };
    };

    // Update the server information first
    //
    switch (uCmd)
    {
        case ULS_APP_ADD_PROT:
            hr = g_pCUls->RegisterLocalProtocol((CLocalProt*)pProtocol, &ldai);
            break;

        case ULS_APP_REMOVE_PROT:
            hr = g_pCUls->UnregisterLocalProtocol((CLocalProt*)pProtocol, &ldai);
            break;

        default:
            ASSERT(0);
            break;
    };
    
    switch (hr)
    {
        case NOERROR:
            //
            // Server starts updating the protocol successfullly
            // We will wait for the server response.
            //
            break;

        case S_FALSE:
            //
            // We have not registered, will do local response
            //
            hr = NOERROR;
            ldai.uMsgID = 0;    
            break;

        default:
            // ULS is locked. Return failure.
            //
            hr = ULS_E_ABORT;
            break; 
    }

    if (SUCCEEDED(hr))
    {
        REQUESTINFO ri;
        ULONG   uMsg;

        switch(uCmd)
        {
            case ULS_APP_ADD_PROT:
                uMsg = (ldai.uMsgID == 0 ? WM_ULS_LOCAL_REGISTER_PROTOCOL:
                                           WM_ULS_REGISTER_PROTOCOL);
                break;

            case ULS_APP_REMOVE_PROT:
                uMsg = (ldai.uMsgID == 0 ? WM_ULS_LOCAL_UNREGISTER_PROTOCOL :
                                           WM_ULS_UNREGISTER_PROTOCOL);
                break;

            default:
                ASSERT(0);
                break;
        };
 
        // If updating server was successfully requested, wait for the response
        //
        ri.uReqType = uMsg;
        ri.uMsgID = ldai.uMsgID;
        ri.pv     = (PVOID)this;
        ri.lParam = (LPARAM)((CLocalProt*)pProtocol);

        hr = g_pReqMgr->NewRequest(&ri);

        if (SUCCEEDED(hr))
        {
            // Make sure the objects do not disappear before we get the response
            //
            this->AddRef();
            pProtocol->AddRef();

            // Return the request ID
            //
            *puReqID = ri.uReqID;

            // If not registered with server, start async response ourselves
            //
            if (ldai.uMsgID == 0)
            {
                g_pCUls->LocalAsyncRespond(uMsg, ri.uReqID, NOERROR);
            };
        };
    };

    return hr;
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::AddProtocol (IULSLocalAppProtocol *pProtocol,
//                         ULONG *puReqID)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::AddProtocol (IULSLocalAppProtocol *pProtocol,
                        ULONG *puReqID)
{
    return ChangeProtocol(pProtocol, puReqID, ULS_APP_ADD_PROT);
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::RemoveProtocol (IULSLocalAppProtocol *pProtocol,
//                            ULONG *puReqID)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::RemoveProtocol (IULSLocalAppProtocol *pProtocol,
                           ULONG *puReqID)
{
    return ChangeProtocol(pProtocol, puReqID, ULS_APP_REMOVE_PROT);
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::EnumProtocols (IEnumULSLocalAppProtocols **ppEnumProtocol)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::EnumProtocols (IEnumULSLocalAppProtocols **ppEnumProtocol)
{
    CEnumLocalAppProtocols *pep;
    HRESULT hr;

    // Validate parameters
    //
    if (ppEnumProtocol == NULL)
    {
        return ULS_E_POINTER;
    };

    // Assume failure
    //
    *ppEnumProtocol = NULL;

    // Create a peer enumerator
    //
    pep = new CEnumLocalAppProtocols;

    if (pep != NULL)
    {
        hr = pep->Init(&ProtList);

        if (SUCCEEDED(hr))
        {
            // Get the enumerator interface
            //
            pep->AddRef();
            *ppEnumProtocol = pep;
        }
        else
        {
            delete pep;
        };
    }
    else
    {
        hr = ULS_E_MEMORY;
    };
    return hr;
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::ChangeAttributes (IULSAttributes *pAttributes, ULONG *puReqID,
//                              APP_CHANGE_ATTRS uCmd)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::ChangeAttributes (IULSAttributes *pAttributes, ULONG *puReqID,
                             APP_CHANGE_ATTRS uCmd)
{
    PVOID   pv;
    HRESULT hr;
    HANDLE  hLdapApp;
    LDAP_ASYNCINFO ldai; 

    // Validate parameters
    //
    if ((pAttributes == NULL) ||
        (puReqID == NULL))
    {
        return ULS_E_POINTER;
    };

    hr = pAttributes->QueryInterface(IID_IULSAttributes, &pv);

    if (FAILED(hr))
    {
        return ULS_E_PARAMETER;
    };

    // If no attributes, fails the call
    //
    if (((CAttributes*)pAttributes)->GetCount() == 0)
    {
        return ULS_E_PARAMETER;
    };

    // Check if already registered
    //
    hr = g_pCUls->GetAppHandle(&hLdapApp);

    switch (hr)
    {
        case NOERROR:
        {
            LPTSTR pAttrList;
            ULONG  cAttrs, cb;

            // Yes, get the attributes list
            //
            switch (uCmd)
            {
                case ULS_APP_SET_ATTRIBUTES:
                    hr = ((CAttributes*)pAttributes)->GetAttributePairs(&pAttrList,
                                                                        &cAttrs,
                                                                        &cb);
                    if (SUCCEEDED(hr))
                    {
                        hr = ::UlsLdap_SetAppAttrs(hLdapApp, cAttrs, pAttrList,
                                                   &ldai);
                        FreeLPTSTR(pAttrList);
                    };
                    break;

                case ULS_APP_REMOVE_ATTRIBUTES:
                    hr = ((CAttributes*)pAttributes)->GetAttributeList(&pAttrList,
                                                                        &cAttrs,
                                                                        &cb);

                    if (SUCCEEDED(hr))
                    {
                        hr = ::UlsLdap_RemoveAppAttrs(hLdapApp, cAttrs, pAttrList,
                                                      &ldai);
                        FreeLPTSTR(pAttrList);
                    };
                    break;

                default:
                    ASSERT(0);
                    break;
            };
            break;
        }
    
        case S_FALSE:
            //
            // Not registered, will do local response
            //
            hr = NOERROR;
            ldai.uMsgID = 0;
            break;

        default:
            // ULS is locked. Return failure.
            //
            hr = ULS_E_ABORT;
            break; 
    };

    if (SUCCEEDED(hr))
    {
        REQUESTINFO ri;
        ULONG   uMsg;

        switch(uCmd)
        {
            case ULS_APP_SET_ATTRIBUTES:
                uMsg = (ldai.uMsgID == 0 ? WM_ULS_LOCAL_SET_APP_ATTRS :
                                           WM_ULS_SET_APP_ATTRS);
                break;

            case ULS_APP_REMOVE_ATTRIBUTES:
                uMsg = (ldai.uMsgID == 0 ? WM_ULS_LOCAL_REMOVE_APP_ATTRS :
                                           WM_ULS_REMOVE_APP_ATTRS);
                break;

            default:
                ASSERT(0);
                break;
        };

        // If updating server was successfully requested, wait for the response
        //
        ri.uReqType = uMsg;
        ri.uMsgID = ldai.uMsgID;
        ri.pv     = (PVOID)this;
        ri.lParam = (LPARAM)((CAttributes *)pAttributes);

        hr = g_pReqMgr->NewRequest(&ri);

        if (SUCCEEDED(hr))
        {
            // Make sure the objects do not disappear before we get the response
            //
            this->AddRef();
            pAttributes->AddRef();

            // Return the request ID
            //
            *puReqID = ri.uReqID;

            // If not registered with server, start async response ourselves
            //
            if (ldai.uMsgID == 0)
            {
                g_pCUls->LocalAsyncRespond(uMsg, ri.uReqID, NOERROR);
            };
        };
    };

    // Matching the QueryInterface
    //
    pAttributes->Release();
    return hr;
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::SetAttributes (IULSAttributes *pAttributes, ULONG *puReqID)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::SetAttributes (IULSAttributes *pAttributes, ULONG *puReqID)
{
    return ChangeAttributes(pAttributes, puReqID, ULS_APP_SET_ATTRIBUTES);
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::RemoveAttributes (IULSAttributes *pAttributes, ULONG *puReqID)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::RemoveAttributes (IULSAttributes *pAttributes, ULONG *puReqID)
{
    return ChangeAttributes(pAttributes, puReqID, ULS_APP_REMOVE_ATTRIBUTES);
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::GetAppInfo (PLDAP_APPINFO *ppAppInfo)
//
// History:
//  Wed 17-Apr-1996 11:15:02  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::GetAppInfo (PLDAP_APPINFO *ppAppInfo)
{
    PLDAP_APPINFO pai;
    ULONG cName, cMime;
    LPTSTR szAttrs;
    ULONG cAttrs, cbAttrs;
    HRESULT hr;

    // Assume failure
    //
    *ppAppInfo = NULL;

    // Calculate the buffer size
    //
    cName = lstrlen(szName)+1;
    cMime = lstrlen(szMimeType)+1;

    // Get the attribute pairs
    //
    hr = pAttrs->GetAttributePairs(&szAttrs, &cAttrs, &cbAttrs);
    if (FAILED(hr))
    {
        return hr;
    };

    // Allocate the buffer
    //
    pai = (PLDAP_APPINFO)LocalAlloc(LPTR, sizeof(LDAP_APPINFO) +
                                                ((cName + cMime)* sizeof(TCHAR)) +
                                                cbAttrs);
    if (pai == NULL)
    {
        hr = ULS_E_MEMORY;
    }
    else
    {
        // Fill the structure content
        //
        pai->uSize              = sizeof(*pai);
        pai->guid               = guid;
        pai->uOffsetName        = sizeof(*pai);
        pai->uOffsetMimeType    = pai->uOffsetName + (cName*sizeof(TCHAR));
        pai->cAttributes        = cAttrs;
        pai->uOffsetAttributes  = (cAttrs != 0 ?
                                   pai->uOffsetMimeType  + (cMime*sizeof(TCHAR)) :
                                   0);

        // Copy the user information
        //
        lstrcpy((LPTSTR)(((PBYTE)pai)+pai->uOffsetName), szName);
        lstrcpy((LPTSTR)(((PBYTE)pai)+pai->uOffsetMimeType), szMimeType);
        if (cAttrs)
        {
            CopyMemory(((PBYTE)pai)+pai->uOffsetAttributes, szAttrs, cbAttrs);
        };

        // Return the structure
        //
        *ppAppInfo = pai;
    };

    if (szAttrs != NULL)
    {
        FreeLPTSTR(szAttrs);
    };
    return NOERROR;
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::EnumConnectionPoints(IEnumConnectionPoints **ppEnum)
//
// History:
//  Wed 17-Apr-1996 11:15:02  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::EnumConnectionPoints(IEnumConnectionPoints **ppEnum)
{
    CEnumConnectionPoints *pecp;
    HRESULT hr;

    // Validate parameters
    //
    if (ppEnum == NULL)
    {
        return ULS_E_POINTER;
    };
    
    // Assume failure
    //
    *ppEnum = NULL;

    // Create an enumerator
    //
    pecp = new CEnumConnectionPoints;
    if (pecp == NULL)
        return ULS_E_MEMORY;

    // Initialize the enumerator
    //
    hr = pecp->Init((IConnectionPoint *)pConnPt);
    if (FAILED(hr))
    {
        delete pecp;
        return hr;
    };

    // Give it back to the caller
    //
    pecp->AddRef();
    *ppEnum = pecp;
    return S_OK;
}

//****************************************************************************
// STDMETHODIMP
// CLocalApp::FindConnectionPoint(REFIID riid, IConnectionPoint **ppcp)
//
// History:
//  Wed 17-Apr-1996 11:15:09  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CLocalApp::FindConnectionPoint(REFIID riid, IConnectionPoint **ppcp)
{
    IID siid;
    HRESULT hr;

    // Validate parameters
    //
    if (ppcp == NULL)
    {
        return ULS_E_POINTER;
    };
    
    // Assume failure
    //
    *ppcp = NULL;

    if (pConnPt != NULL)
    {
        hr = pConnPt->GetConnectionInterface(&siid);

        if (SUCCEEDED(hr))
        {
            if (riid == siid)
            {
                *ppcp = (IConnectionPoint *)pConnPt;
                (*ppcp)->AddRef();
                hr = S_OK;
            }
            else
            {
                hr = ULS_E_NO_INTERFACE;
            };
        };
    }
    else
    {
        hr = ULS_E_NO_INTERFACE;
    };

    return hr;
}

#ifdef DEBUG
//****************************************************************************
// void
// CLocalApp::DebugProtocolDump (void)
//
// History:
//  Wed 17-Apr-1996 11:14:03  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

void
CLocalApp::DebugProtocolDump (void)
{
    CLocalProt *plp;
    BSTR bstrID;
    LPTSTR pszID;
    ULONG  cCount;
    HANDLE hEnum;

    // Each protocol
    //
    cCount = 1;
    ProtList.Enumerate(&hEnum);
    while(ProtList.Next(&hEnum, (PVOID *)&plp) == NOERROR)
    {
        if (SUCCEEDED(plp->GetID (&bstrID)))
        {
            BSTR_to_LPTSTR(&pszID, bstrID);
            DPRINTF2(TEXT("%d> %s"), cCount++, pszID);
            FreeLPTSTR(pszID);
            SysFreeString(bstrID);
        };
    };
    return;
}
#endif // DEBUG

//****************************************************************************
// CEnumLocalAppProtocols::CEnumLocalAppProtocols (void)
//
// History:
//  Wed 17-Apr-1996 11:15:18  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

CEnumLocalAppProtocols::CEnumLocalAppProtocols (void)
{
    cRef = 0;
    hEnum = NULL;
    return;
}

//****************************************************************************
// CEnumLocalAppProtocols::~CEnumLocalAppProtocols (void)
//
// History:
//  Wed 17-Apr-1996 11:15:18  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

CEnumLocalAppProtocols::~CEnumLocalAppProtocols (void)
{
    CLocalProt *plp;

    ProtList.Enumerate(&hEnum);
    while(ProtList.Next(&hEnum, (PVOID *)&plp) == NOERROR)
    {
        plp->Release();
    };
    ProtList.Flush();
    return;
}

//****************************************************************************
// STDMETHODIMP
// CEnumLocalAppProtocols::Init (CList *pProtList)
//
// History:
//  Wed 17-Apr-1996 11:15:25  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CEnumLocalAppProtocols::Init (CList *pProtList)
{
    CLocalProt *plp;
    HRESULT hr;

    // Duplicate the protocol list
    //
    hr = ProtList.Clone (pProtList, NULL);

    if (SUCCEEDED(hr))
    {
        // Add reference to each protocol object
        //
        ProtList.Enumerate(&hEnum);
        while(ProtList.Next(&hEnum, (PVOID *)&plp) == NOERROR)
        {
            plp->AddRef();
        };

        // Reset the enumerator
        //
        ProtList.Enumerate(&hEnum);
    };
    return hr;
}

//****************************************************************************
// STDMETHODIMP
// CEnumLocalAppProtocols::QueryInterface (REFIID riid, void **ppv)
//
// History:
//  Wed 17-Apr-1996 11:15:31  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CEnumLocalAppProtocols::QueryInterface (REFIID riid, void **ppv)
{
    if (riid == IID_IEnumULSLocalAppProtocols || riid == IID_IUnknown)
    {
        *ppv = (IEnumULSLocalAppProtocols *) this;
        AddRef();
        return S_OK;
    }
    else
    {
        *ppv = NULL;
        return ULS_E_NO_INTERFACE;
    };
}

//****************************************************************************
// STDMETHODIMP_(ULONG)
// CEnumLocalAppProtocols::AddRef (void)
//
// History:
//  Wed 17-Apr-1996 11:15:37  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP_(ULONG)
CEnumLocalAppProtocols::AddRef (void)
{
    cRef++;
    return cRef;
}

//****************************************************************************
// STDMETHODIMP_(ULONG)
// CEnumLocalAppProtocols::Release (void)
//
// History:
//  Wed 17-Apr-1996 11:15:43  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP_(ULONG)
CEnumLocalAppProtocols::Release (void)
{
    cRef--;

    if (cRef == 0)
    {
        delete this;
        return 0;
    }
    else
    {
        return cRef;
    };
}

//****************************************************************************
// STDMETHODIMP 
// CEnumLocalAppProtocols::Next (ULONG cProtocols,
//                               IULSLocalAppProtocol **rgpProt,
//                               ULONG *pcFetched)
//
// History:
//  Wed 17-Apr-1996 11:15:49  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP 
CEnumLocalAppProtocols::Next (ULONG cProtocols, IULSLocalAppProtocol **rgpProt,
                              ULONG *pcFetched)
{
    CLocalProt *plp;
    ULONG   cCopied;
    HRESULT hr;

    // Validate the pointer
    //
    if (rgpProt == NULL)
        return ULS_E_POINTER;

    // Validate the parameters
    //
    if ((cProtocols == 0) ||
        ((cProtocols > 1) && (pcFetched == NULL)))
        return ULS_E_PARAMETER;

    // Check the enumeration index
    //
    cCopied = 0;

    // Can copy if we still have more protocols
    //
    while ((cCopied < cProtocols) &&
           (ProtList.Next(&hEnum, (PVOID *)&plp) == NOERROR))
    {
        rgpProt[cCopied] = plp;
        plp->AddRef();
        cCopied++;
    };

    // Determine the returned information based on other parameters
    //
    if (pcFetched != NULL)
    {
        *pcFetched = cCopied;
    };
    return (cProtocols == cCopied ? S_OK : S_FALSE);
}

//****************************************************************************
// STDMETHODIMP
// CEnumLocalAppProtocols::Skip (ULONG cProtocols)
//
// History:
//  Wed 17-Apr-1996 11:15:56  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CEnumLocalAppProtocols::Skip (ULONG cProtocols)
{
    CLocalProt *plp;
    ULONG cSkipped;

    // Validate the parameters
    //
    if (cProtocols == 0) 
        return ULS_E_PARAMETER;

    // Check the enumeration index limit
    //
    cSkipped = 0;

    // Can skip only if we still have more attributes
    //
    while ((cSkipped < cProtocols) &&
           (ProtList.Next(&hEnum, (PVOID *)&plp) == NOERROR))
    {
        cSkipped++;
    };

    return (cProtocols == cSkipped ? S_OK : S_FALSE);
}

//****************************************************************************
// STDMETHODIMP
// CEnumLocalAppProtocols::Reset (void)
//
// History:
//  Wed 17-Apr-1996 11:16:02  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CEnumLocalAppProtocols::Reset (void)
{
    ProtList.Enumerate(&hEnum);
    return S_OK;
}

//****************************************************************************
// STDMETHODIMP
// CEnumLocalAppProtocols::Clone(IEnumULSLocalAppProtocols **ppEnum)
//
// History:
//  Wed 17-Apr-1996 11:16:11  -by-  Viroon  Touranachun [viroont]
// Created.
//****************************************************************************

STDMETHODIMP
CEnumLocalAppProtocols::Clone(IEnumULSLocalAppProtocols **ppEnum)
{
    CEnumLocalAppProtocols *pep;
    HRESULT hr;

    // Validate parameters
    //
    if (ppEnum == NULL)
    {
        return ULS_E_POINTER;
    };

    *ppEnum = NULL;

    // Create an enumerator
    //
    pep = new CEnumLocalAppProtocols;
    if (pep == NULL)
        return ULS_E_MEMORY;

    // Clone the information
    //
    pep->hEnum = hEnum;
    hr = pep->ProtList.Clone (&ProtList, &(pep->hEnum));

    if (SUCCEEDED(hr))
    {
        CLocalProt *plp;
        HANDLE hEnumTemp;

        // Add reference to each protocol object
        //
        pep->ProtList.Enumerate(&hEnumTemp);
        while(pep->ProtList.Next(&hEnumTemp, (PVOID *)&plp) == NOERROR)
        {
            plp->AddRef();
        };

        // Return the cloned enumerator
        //
        pep->AddRef();
        *ppEnum = pep;
    }
    else
    {
        delete pep;
    };
    return hr;
}