//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 2000.
//
//  File:       V A L I D A T I O N M A N A G E R . C P P
//
//  Contents:   Validates device host inputs
//
//  Notes:
//
//  Author:     mbend   9 Oct 2000
//
//----------------------------------------------------------------------------

#include "pch.h"
#pragma hdrstop

#include "uhbase.h"
#include "ValidationManager.h"
#include "uhutil.h"
#include "ncstring.h"
#include "validate.h"
#include "uhcommon.h"

// Functions declarationc
HRESULT HrValidateDevice(
    IXMLDOMNodePtr & pNodeDevice,
    CUString & strErrorString);

CValidationManager::CValidationManager ()
{
}

CValidationManager::~CValidationManager ()
{
}

HRESULT HrGetDocumentAndRootNode(
    BSTR bstrTemplate,
    IXMLDOMDocumentPtr & pDoc,
    IXMLDOMNodePtr & pRootNode)
{
    TraceTag(ttidValidate, "HrGetDocumentAndRootNode");
    HRESULT hr = S_OK;

    // Load document and fetch needed items
    hr = HrLoadDocument(bstrTemplate, pDoc);
    if(SUCCEEDED(hr))
    {
        hr = pRootNode.HrAttach(pDoc);
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "HrGetDocumentAndRootNode");
    return hr;
}

struct PresenceItem
{
    const wchar_t * m_szName;
    bool            m_bEmpty;
    bool            m_bSuffix;
    bool            m_bOptional;
    LONG            m_cchMax;
};

//    m_szName              EMPTY   SUFFIX  OPTONAL CCH
//    ----------------------------------------------------------
const PresenceItem g_arpiDeviceItems[] =
{
    {L"deviceType",         false,  true,   false,  64},
    {L"friendlyName",       false,  false,  false,  64},
    {L"manufacturer",       false,  false,  false,  64},
    {L"manufacturerURL",    false,  false,  true,   -1},
    {L"modelDescription",   false,  false,  true,   128},
    {L"modelName",          false,  false,  false,  32},
    {L"modelNumber",        false,  false,  true,   32},
    {L"modelURL",           false,  false,  true,   -1},
    {L"serialNumber",       false,  false,  true,   64},
    {L"UDN",                false,  false,  false,  -1},
    {L"UPC",                false,  false,  true,   12},
};

const long c_nDeviceItems = celems(g_arpiDeviceItems);

PresenceItem g_arpiServiceItems[] =
{
    {L"serviceType",        false,  true,   false,  64},
    {L"serviceId",          false,  true,   false,  64},
    {L"SCPDURL",            false,  false,  false,  -1},
    {L"controlURL",         true,   false,  false,  -1},
    {L"eventSubURL",        true,   false,  false,  -1},
};

const long c_nServiceItems = celems(g_arpiServiceItems);

PresenceItem g_arpiIconItems[] =
{
    {L"mimetype",           false,  false,  false,  -1},
    {L"width",              false,  false,  false,  -1},
    {L"height",             false,  false,  false,  -1},
    {L"depth",              false,  false,  false,  -1},
    {L"url",                false,  false,  false,  -1},
};

const long c_nIconItems = celems(g_arpiIconItems);

PresenceItem g_arpiRootItems[] =
{
    {L"/root/specVersion",  false,  false,  false,  -1},
};

const long c_nRootItems = celems(g_arpiRootItems);

HRESULT HrValidateSufixes(IXMLDOMNodePtr & pNode,
                          const wchar_t * szName,
                          LONG            cchMax,
                          CUString & strErrorString)
{
    HRESULT         hr = S_OK;
    DWORD           ctok = 0;
    LPCWSTR         pchText;
    CUString        strText;
    IXMLDOMNodePtr  pNodeItem;

    // This function validates the serviceType, deviceType, and serviceId
    // elements in the following way:
    // Each of these is of the form: urn:domain-name:keyword:SUFFIX:version
    // Since they all follow the same format (which currently we DO NOT
    // validate), we can make an assumption that the 4th token (SUFFIX) is the
    // one that we need to validate. The validation is strictly as according
    // to UPnP architecture 1.0 where this suffix must be <= 64 characters in
    // length.
    //

    hr = HrSelectNode(szName, pNode, pNodeItem);
    if (SUCCEEDED(hr))
    {
        hr = HrGetNodeText(pNodeItem, strText);
        if (SUCCEEDED(hr))
        {
            pchText = strText.GetBuffer();
            while (*pchText)
            {
                if (*pchText == L':')
                {
                    ctok++;
                    pchText++;

                    if (ctok == 3)
                    {
                        // Fourth token is the one we need to examine
                        LONG    cch = 0;

                        while (*pchText && *pchText != L':')
                        {
                            pchText++;
                            cch++;
                        }

                        // ISSUE-2000/11/29-danielwe: We don't yet
                        // validate the format of the suffix
                        //

                        if (cch > cchMax)
                        {
                            hr = strErrorString.HrPrintf(
                                WszLoadString(_Module.GetResourceInstance(),
                                              IDS_SUFFIX_TOO_LONG),
                                strText);
                            if (SUCCEEDED(hr))
                            {
                                hr = UPNP_E_SUFFIX_TOO_LONG;
                            }
                        }

                        break;
                    }
                }
                else
                {
                    pchText++;
                }
            }
        }
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateSufixes(%S)",
            strErrorString.GetLength() ? strErrorString.GetBuffer(): L"Unspecified");
    return hr;
}

HRESULT HrValidatePresenceItems(
    IXMLDOMNodePtr & pNode,
    long nPresenceItems,
    const PresenceItem * arPresenceItems,
    CUString & strErrorString)
{
    HRESULT hr = S_OK;

    for(long n = 0; n < nPresenceItems && SUCCEEDED(hr); ++n)
    {
        AssertSz(FImplies(arPresenceItems[n].m_bEmpty,
                          arPresenceItems[n].m_cchMax == -1),
                 "Empty elements mean there shouldn't be a size to verify "
                 "against! Fix the array above!");

        if(arPresenceItems[n].m_bEmpty)
        {
            hr = HrIsNodePresentOnceAndEmpty(arPresenceItems[n].m_szName, pNode);
            if(S_OK != hr)
            {
                hr = strErrorString.HrPrintf(
                    WszLoadString(_Module.GetResourceInstance(), IDS_EMPTY_NODE_NOT_PRESENT),
                    arPresenceItems[n].m_szName);
                if(SUCCEEDED(hr))
                {
                    hr = UPNP_E_REQUIRED_ELEMENT_ERROR;
                }
            }
        }
        else if (!arPresenceItems[n].m_bSuffix)
        {
            hr = HrIsNodePresentOnceAndNotEmpty(arPresenceItems[n].m_szName, pNode);
            if(S_OK != hr)
            {
                if (UPNP_E_DUPLICATE_NOT_ALLOWED == hr)
                {
                    // Didn't find the item and it's not optional
                    hr = strErrorString.HrPrintf(
                        WszLoadString(_Module.GetResourceInstance(),
                                      IDS_DUPLICATES_NOT_ALLOWED),
                        arPresenceItems[n].m_szName);
                    if(SUCCEEDED(hr))
                    {
                        hr = UPNP_E_DUPLICATE_NOT_ALLOWED;
                    }
                }
                else if (!arPresenceItems[n].m_bOptional)
                {
                    // Didn't find the item and it's not optional
                    hr = strErrorString.HrPrintf(
                        WszLoadString(_Module.GetResourceInstance(),
                                      IDS_NON_EMPTY_NODE_NOT_PRESENT),
                        arPresenceItems[n].m_szName);
                    if(SUCCEEDED(hr))
                    {
                        hr = UPNP_E_REQUIRED_ELEMENT_ERROR;
                    }
                }
                else
                {
                    // Element was optional
                    hr = S_OK;
                }
            }
            else if (arPresenceItems[n].m_cchMax != -1)
            {
                // Check length if one is specified
                //
                hr = HrIsNodeOfValidLength(arPresenceItems[n].m_szName, pNode,
                                           arPresenceItems[n].m_cchMax);
                if(S_FALSE == hr)
                {
                    hr = strErrorString.HrPrintf(
                        WszLoadString(_Module.GetResourceInstance(), IDS_ELEMENT_VALUE_TOO_LONG),
                        arPresenceItems[n].m_szName);
                    if(SUCCEEDED(hr))
                    {
                        hr = UPNP_E_VALUE_TOO_LONG;
                    }
                }
                else if (arPresenceItems[n].m_bOptional && (FAILED(hr)))
                {
                    // If item was optional, forget any errors
                    hr = S_OK;
                }
            }
        }

        if (SUCCEEDED(hr))
        {
            if (arPresenceItems[n].m_bSuffix && arPresenceItems[n].m_cchMax != -1)
            {
                hr = HrValidateSufixes(pNode, arPresenceItems[n].m_szName,
                                       arPresenceItems[n].m_cchMax,
                                       strErrorString);
            }
        }
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidatePresenceItems(%S)",
            strErrorString.GetLength() ? strErrorString.GetBuffer(): L"Unspecified");
    return hr;
}

HRESULT HrValidateDeviceService(
    IXMLDOMNodePtr & pNodeService,
    CUString & strErrorString)
{
    HRESULT hr = S_OK;

    hr = HrValidatePresenceItems(pNodeService, c_nServiceItems, g_arpiServiceItems, strErrorString);

    TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDeviceService");
    return hr;
}

HRESULT HrCheckForDuplicatesInList(
    IXMLDOMNodeListPtr & pNodeList,
    CUString & strErrorString)
{
    HRESULT hr = S_OK;

    CUArray<CUString> arstrValues;
    while(SUCCEEDED(hr))
    {
        IXMLDOMNodePtr pNode;
        HRESULT hrTemp = pNodeList->nextNode(pNode.AddressOf());
        if(S_OK != hrTemp)
        {
            break;
        }
        CUString strText;
        hr = HrGetNodeText(pNode, strText);
        if(SUCCEEDED(hr))
        {
            long nIndex = 0;
            hrTemp = arstrValues.HrFind(strText, nIndex);
            if(S_OK == hrTemp)
            {
                // We found a duplicate
                hr = strErrorString.HrPrintf(
                    WszLoadString(_Module.GetResourceInstance(), IDS_DUPLICATES_NOT_ALLOWED),
                    strText.GetBuffer());
                if(SUCCEEDED(hr))
                {
                    hr = UPNP_E_DUPLICATE_NOT_ALLOWED;
                }
            }
            else
            {
                hr = arstrValues.HrPushBack(strText);
            }
        }
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "HrCheckForDuplicatesInList");
    return hr;
}

HRESULT HrValidateDeviceServices(
    IXMLDOMNodePtr & pNodeDevice,
    CUString & strErrorString)
{
    HRESULT hr = S_OK;

    // serviceList is required and must contain a service
    BOOL bServiceNotPresent = TRUE;

    HRESULT hrTemp = HrIsNodePresentOnce(L"serviceList", pNodeDevice);
    if(S_OK == hrTemp)
    {
        IXMLDOMNodeListPtr pNodeList;
        hrTemp = HrSelectNodes(L"serviceList/service", pNodeDevice, pNodeList);
        if(S_OK == hrTemp)
        {
            while(SUCCEEDED(hr))
            {
                IXMLDOMNodePtr pNode;
                hrTemp = pNodeList->nextNode(pNode.AddressOf());
                if(S_OK != hrTemp)
                {
                    break;
                }

                // We have a service
                bServiceNotPresent = FALSE;

                hr = HrValidateDeviceService(pNode, strErrorString);
            }
        }

        // Make sure all ServiceId's are unique
        if(SUCCEEDED(hr))
        {
            pNodeList.Release();
            hrTemp = HrSelectNodes(L"serviceList/service/serviceId", pNodeDevice, pNodeList);
            if(S_OK == hrTemp)
            {
                hr = HrCheckForDuplicatesInList(pNodeList, strErrorString);
            }
        }
    }

    if(bServiceNotPresent)
    {
        hr = strErrorString.HrAssign(
            WszLoadString(_Module.GetResourceInstance(), IDS_SERVICE_MISSING));
        if(SUCCEEDED(hr))
        {
            hr = UPNP_E_INVALID_SERVICE;
        }
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDeviceServices");
    return hr;
}

HRESULT HrValidateDeviceIcon(
    IXMLDOMNodePtr & pNodeIcon,
    CUString & strErrorString)
{
    HRESULT hr = S_OK;

    hr = HrValidatePresenceItems(pNodeIcon, c_nIconItems, g_arpiIconItems, strErrorString);

    TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDeviceIcon");
    return hr;
}

HRESULT HrValidateDeviceIcons(
    IXMLDOMNodePtr & pNodeDevice,
    CUString & strErrorString)
{
    HRESULT hr = S_OK;

    HRESULT hrTemp = HrIsNodePresentOnce(L"iconList", pNodeDevice);
    if(S_OK == hrTemp)
    {
        BOOL    fGotAnIcon = FALSE;

        IXMLDOMNodeListPtr pNodeList;
        hrTemp = HrSelectNodes(L"iconList/icon", pNodeDevice, pNodeList);
        if(S_OK == hrTemp)
        {
            while(SUCCEEDED(hr))
            {
                IXMLDOMNodePtr pNode;
                hrTemp = pNodeList->nextNode(pNode.AddressOf());
                if(S_OK != hrTemp)
                {
                    break;
                }

                fGotAnIcon = TRUE;
                hr = HrValidateDeviceIcon(pNode, strErrorString);
            }
        }

        if (!fGotAnIcon)
        {
            hr = strErrorString.HrAssign(
                WszLoadString(_Module.GetResourceInstance(), IDS_ICON_MISSING));
            if(SUCCEEDED(hr))
            {
                hr = UPNP_E_INVALID_ICON;
            }
        }
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDeviceIcons");
    return hr;
}

HRESULT HrValidateDeviceChildren(
    IXMLDOMNodePtr & pNodeDevice,
    CUString & strErrorString)
{
    HRESULT hr = S_OK;

    BOOL    fGotADevice = FALSE;

    HRESULT hrTemp = HrIsNodePresentOnce(L"deviceList", pNodeDevice);
    if(S_OK == hrTemp)
    {
        IXMLDOMNodeListPtr pNodeList;
        hrTemp = HrSelectNodes(L"deviceList/device", pNodeDevice, pNodeList);
        if(S_OK == hrTemp)
        {
            while(SUCCEEDED(hr))
            {
                IXMLDOMNodePtr pNode;
                hrTemp = pNodeList->nextNode(pNode.AddressOf());
                if(S_OK != hrTemp)
                {
                    break;
                }

                fGotADevice = TRUE;
                hr = HrValidateDevice(pNode, strErrorString);
            }
        }

        if (!fGotADevice)
        {
            hr = strErrorString.HrAssign(
                WszLoadString(_Module.GetResourceInstance(), IDS_DEVICE_MISSING));
            if(SUCCEEDED(hr))
            {
                hr = UPNP_E_INVALID_DOCUMENT;
            }
        }
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDeviceChildren");
    return hr;
}

HRESULT HrValidateDevice(
    IXMLDOMNodePtr & pNodeDevice,
    CUString & strErrorString)
{
    TraceTag(ttidValidate, "HrValidateDevice");
    HRESULT hr = S_OK;

    hr = HrValidatePresenceItems(pNodeDevice, c_nDeviceItems, g_arpiDeviceItems, strErrorString);
    if(SUCCEEDED(hr))
    {
        hr = HrValidateDeviceServices(pNodeDevice, strErrorString);
        if(SUCCEEDED(hr))
        {
            hr = HrValidateDeviceIcons(pNodeDevice, strErrorString);
            if(SUCCEEDED(hr))
            {
                hr = HrValidateDeviceChildren(pNodeDevice, strErrorString);
            }
        }
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateDevice");
    return hr;
}

HRESULT HrValidateUDNs(
    IXMLDOMNodePtr & pNodeDevice,
    CUString & strErrorString)
{
    HRESULT hr = S_OK;

    IXMLDOMNodeListPtr pNodeList;
    hr = HrSelectNodes(L"//UDN", pNodeDevice, pNodeList);
    if(SUCCEEDED(hr))
    {
        hr = HrCheckForDuplicatesInList(pNodeList, strErrorString);
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "HrValidateUDNs");
    return hr;
}

/*
HRESULT HrValidateDevice(
    IXMLDOMNodePtr & pNodeDevice)
{
    TraceTag(ttidValidate, "");
    HRESULT hr = S_OK;

    TraceHr(ttidValidate, FAL, hr, FALSE, "");
    return hr;
}
*/

// IUPnPValidationManager methods

STDMETHODIMP CValidationManager::ValidateDescriptionDocument(
    /*[in]*/ BSTR bstrTemplate,
    /*[out, string]*/ wchar_t ** pszErrorString)
{
    CHECK_POINTER(bstrTemplate);
    CHECK_POINTER(pszErrorString);
    HRESULT hr = S_OK;
    CUString strErrorString;
    *pszErrorString = NULL;

    IXMLDOMDocumentPtr pDoc;
    IXMLDOMNodePtr pRootNode;

    hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
    
    if (SUCCEEDED(hr))
    {
        hr = HrGetDocumentAndRootNode(bstrTemplate, pDoc, pRootNode);
    }

    if(SUCCEEDED(hr))
    {
        hr = HrValidatePresenceItems(pRootNode, c_nRootItems, g_arpiRootItems, strErrorString);
        if(SUCCEEDED(hr))
        {
            hr = HrIsNodePresentOnce(L"/root/device", pRootNode);
            if(S_OK == hr)
            {
                hr = HrIsNodePresentOnce(L"/root/URLBase", pRootNode);
                if (S_OK != hr)
                {
                    hr = HrValidateUDNs(pRootNode, strErrorString);
                    if(SUCCEEDED(hr))
                    {
                        IXMLDOMNodePtr pNodeDevice;
                        hr = HrSelectNode(L"/root/device", pRootNode, pNodeDevice);
                        if(SUCCEEDED(hr))
                        {
                            hr = HrValidateDevice(pNodeDevice, strErrorString);
                        }
                    }
                }
                else
                {
                    hr = strErrorString.HrAssign(
                        WszLoadString(_Module.GetResourceInstance(), IDS_URLBASE_PRESENT));
                    if(SUCCEEDED(hr))
                    {
                        hr = UPNP_E_REQUIRED_ELEMENT_ERROR;
                    }
                }
            }
            else
            {
                hr = strErrorString.HrAssign(
                    WszLoadString(_Module.GetResourceInstance(), IDS_ROOT_DEVICE_MISSING));
                if(SUCCEEDED(hr))
                {
                    hr = UPNP_E_REQUIRED_ELEMENT_ERROR;
                }
            }
        }
    }

    // Let's make sure the root namespace is according to spec
    //
    if (SUCCEEDED(hr))
    {
        IXMLDOMNodePtr  pNodeRootSub;

        hr = HrSelectNode(L"/root", pRootNode, pNodeRootSub);
        if (SUCCEEDED(hr))
        {
            BSTR    bstrUri;

            hr = pNodeRootSub->get_namespaceURI(&bstrUri);
            if (S_OK != hr || lstrcmpi(bstrUri,
                                       L"urn:schemas-upnp-org:device-1-0"))
            {
                hr = strErrorString.HrPrintf(
                    WszLoadString(_Module.GetResourceInstance(),
                                  IDS_INVALID_ROOT_NAMESPACE),
                    bstrUri);
                if(SUCCEEDED(hr))
                {
                    hr = UPNP_E_INVALID_ROOT_NAMESPACE;
                }
            }
        }
    }

    if(FAILED(hr))
    {
        if(strErrorString.GetLength())
        {
            strErrorString.HrGetCOM(pszErrorString);
        }
    }
    TraceHr(ttidValidate, FAL, hr, FALSE, "CValidationManager::ValidateDescriptionDocument(%S)",
            *pszErrorString ? *pszErrorString : L"Unspecified");
    return hr;
}

STDMETHODIMP CValidationManager::ValidateServiceDescription(
    /*[in, string]*/ const wchar_t * szFullPath,
    /*[out, string]*/ wchar_t ** pszErrorString)
{
    CHECK_POINTER(szFullPath);
    CHECK_POINTER(pszErrorString);
    HRESULT hr = S_OK;
    CUString strErrorString;
    *pszErrorString = NULL;

    BSTR                bstrPath;
    IXMLDOMDocumentPtr  pDoc;

    hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
    
    if (SUCCEEDED(hr))
    {
        bstrPath = SysAllocString(szFullPath);
    }

    if (bstrPath)
    {
        hr = HrLoadDocumentFromFile(bstrPath, pDoc);
        if (SUCCEEDED(hr))
        {
            IXMLDOMElementPtr pxdeSDRoot;

            hr = pDoc->get_documentElement(pxdeSDRoot.AddressOf());
            if (S_OK == hr)
            {
                hr = HrValidateServiceDescription(pxdeSDRoot, pszErrorString);
            }
        }
        else
        {
            hr = strErrorString.HrPrintf(
                WszLoadString(_Module.GetResourceInstance(), IDS_INVALID_XML),
                szFullPath);
            if(SUCCEEDED(hr))
            {
                hr = UPNP_E_INVALID_XML;
            }
        }

        SysFreeString(bstrPath);
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }

    if(FAILED(hr))
    {
        if(strErrorString.GetLength())
        {
            strErrorString.HrGetCOM(pszErrorString);
        }
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "CValidationManager::ValidateServiceDescription(%S)",
            *pszErrorString ? *pszErrorString : L"Unspecified");
    return hr;
}

HRESULT CValidationManager::ValidateServiceDescriptions(const wchar_t * szResourcePath,
                                                        IXMLDOMNodePtr pRootNode,
                                                        wchar_t ** pszErrorString)
{
    HRESULT     hr = S_OK;

    IXMLDOMNodeListPtr pNodeList;

    // Select all of the SCPDURL nodes in the description document
    hr = HrSelectNodes(L"//device/serviceList/service/SCPDURL",
                       pRootNode, pNodeList);
    
    while (S_OK == hr)
    {
        IXMLDOMNodePtr  pNode;
        CUString        strUrl;
        
        hr = pNodeList->nextNode(pNode.AddressOf());
        if (S_OK == hr)
        {
            hr = HrGetNodeText(pNode, strUrl);
            if (SUCCEEDED(hr))
            {
                CUString    strFullPath;
                
                hr = HrMakeFullPath(szResourcePath, strUrl, strFullPath);
                if (SUCCEEDED(hr))
                {
                    hr = ValidateServiceDescription(strFullPath,
                                                    pszErrorString);
                }
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        // normalize error code
        hr = S_OK;
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "CValidationManager::ValidateServiceDescriptions(%S)",
            *pszErrorString ? *pszErrorString : L"Unspecified");
    return hr;
}

HRESULT CValidationManager::ValidateIconFiles(const wchar_t * szResourcePath,
                                              IXMLDOMNodePtr pRootNode,
                                              wchar_t ** pszErrorString)
{
    HRESULT             hr = S_OK;
    IXMLDOMNodeListPtr  pNodeList;
    CUString            strErrorString;

    // Select all of the SCPDURL nodes in the description document
    hr = HrSelectNodes(L"//device/iconList/icon/url", pRootNode, pNodeList);
    while (S_OK == hr)
    {
        IXMLDOMNodePtr  pNode;
        CUString        strUrl;

        hr = pNodeList->nextNode(pNode.AddressOf());
        if (S_OK == hr)
        {
            hr = HrGetNodeText(pNode, strUrl);
            if (SUCCEEDED(hr))
            {
                CUString    strFullPath;

                hr = HrMakeFullPath(szResourcePath, strUrl, strFullPath);
                if (SUCCEEDED(hr))
                {
                    if (!FFileExists((LPTSTR)strFullPath.GetBuffer(), FALSE))
                    {
                        hr = strErrorString.HrPrintf(
                            WszLoadString(_Module.GetResourceInstance(), IDS_INVALID_ICON),
                            strFullPath);
                        if(SUCCEEDED(hr))
                        {
                            hr = UPNP_E_INVALID_ICON;
                        }
                    }
                }
            }
        }
    }

    if(FAILED(hr))
    {
        if(strErrorString.GetLength())
        {
            strErrorString.HrGetCOM(pszErrorString);
        }
    }

    if (SUCCEEDED(hr))
    {
        // normalize error code
        hr = S_OK;
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "CValidationManager::ValidateIconFiles(%S)",
            *pszErrorString ? *pszErrorString : L"Unspecified");
    return hr;
}

STDMETHODIMP CValidationManager::ValidateDescriptionDocumentAndReferences(
    /*[in]*/ BSTR bstrTemplate,
    /*[in, string]*/ const wchar_t * szResourcePath,
    /*[out, string]*/ wchar_t ** pszErrorString)
{
    CHECK_POINTER(bstrTemplate);
    CHECK_POINTER(szResourcePath);
    CHECK_POINTER(pszErrorString);
    HRESULT hr = S_OK;
    *pszErrorString = NULL;

    hr = HrIsAllowedCOMCallLocality(CALL_LOCALITY_INPROC);
    
    if (SUCCEEDED(hr))
    {
        hr = ValidateDescriptionDocument(bstrTemplate, pszErrorString);
    }

    if (SUCCEEDED(hr))
    {
        IXMLDOMDocumentPtr  pDoc;
        IXMLDOMNodePtr      pRootNode;

        hr = HrGetDocumentAndRootNode(bstrTemplate, pDoc, pRootNode);
        if (SUCCEEDED(hr))
        {
            hr = ValidateServiceDescriptions(szResourcePath, pRootNode,
                                             pszErrorString);
            if (SUCCEEDED(hr))
            {
                hr = ValidateIconFiles(szResourcePath, pRootNode,
                                       pszErrorString);
            }
        }
    }

    TraceHr(ttidValidate, FAL, hr, FALSE, "CValidationManager::ValidateDescriptionDocumentAndReferences(%S)",
            *pszErrorString ? *pszErrorString : L"Unspecified");
    return hr;
}