//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1999.
//
//  File:       ncxml.cpp
//
//  Contents:   helper functions for doing remarkably simple things
//              with the XML DOM.
//
//----------------------------------------------------------------------------


#include "pch.h"
#pragma hdrstop

#include <oleauto.h>    // for SysFreeString()

#include "ncbase.h"
#include "ncdebug.h"

#include "ncxml.h"
#include "ncinet2.h"    // HrCombineUrl, HrCopyAndValidateUrl


struct DTNAME
{
    LPCWSTR         m_pszType;
    SST_DATA_TYPE   m_sdt;
};


// Lookup table, sorted by string
// Note: if you add strings here, you also have to add a
//       corresponding entry to SST_DATA_TYPE
//
static CONST DTNAME g_rgdtTypeStrings[] =
{
    {L"bin.base64",     SDT_BIN_BASE64},        // base64 as defined in the MIME IETF spec
    {L"bin.hex",        SDT_BIN_HEX},           // Hexadecimal digits representing octets VT_ARRAY safearray or stream
    {L"boolean",        SDT_BOOLEAN},           // boolean, "1" or "0" VT_BOOL int
    {L"char",           SDT_CHAR},              // char, string VT_UI2 wchar
    {L"date",           SDT_DATE_ISO8601},      // date.iso8601, A date in ISO 8601 format. (no time) VT_DATE long
    {L"dateTime",       SDT_DATETIME_ISO8601},  // dateTime.iso8601, A date in ISO 8601 format, with optional time and no optional zone. Fractional seconds may be as precise as nanoseconds. VT_DATE long
    {L"dateTime.tz",    SDT_DATETIME_ISO8601TZ},// dateTime.iso8601tz, A date in ISO 8601 format, with optional time and optional zone. Fractional seconds may be as precise as nanoseconds. VT_DATE long
    {L"fixed.14.4",     SDT_FIXED_14_4},        // fixed.14.4, Same as "number" but no more than 14 digits to the left of the decimal point, and no more than 4 to the right. VT_CY large_integer
    {L"float",          SDT_FLOAT},             // float, Same as for "number." VT_R8 double
    {L"i1",             SDT_I1},                // i1, A number, with optional sign, no fractions, no exponent. VT_I1 char
    {L"i2",             SDT_I2},                // i2, " VT_I2 short
    {L"i4",             SDT_I4},                // i4, " VT_I4 long
    {L"int",            SDT_INT},               // int, A number, with optional sign, no fractions, no exponent. VT_I4 long
    {L"number",         SDT_NUMBER},            // number, A number, with no limit on digits, may potentially have a leading sign, fractional digits, and optionally an exponent. Punctuation as in US English. VT_R8 double
    {L"r4",             SDT_R4},                // r4, Same as "number." VT_FLOAT float
    {L"r8",             SDT_R8},                // r8, " VT_DOUBLE double
    {L"string",         SDT_STRING},            // string, pcdata VT_BSTR BSTR
    {L"time",           SDT_TIME_ISO8601},      // time.iso8601, A time in ISO 8601 format, with no date and no time zone. VT_DATE long
    {L"time.tz",        SDT_TIME_ISO8601TZ},    // time.iso8601.tz, A time in ISO 8601 format, with no date but optional time zone. VT_DATE. long
    {L"ui1",            SDT_UI1},               // ui1, A number, unsigned, no fractions, no exponent. VT_UI1 unsigned char
    {L"ui2",            SDT_UI2},               // ui2, " VT_UI2 unsigned short
    {L"ui4",            SDT_UI4},               // ui4, " VT_UI4 unsigned long
    {L"uri",            SDT_URI},               // uri, Universal Resource Identifier VT_BSTR BSTR
    {L"uuid",           SDT_UUID},              // uuid, Hexadecimal digits representing octets, optional embedded hyphens which should be ignored. VT_BSTR GUID
    //
    // note(cmr): ADD NEW VALUES IN ALPHABETICAL ORDER
    //
};

// NOTE: the order of elements in this array must correspond to the order of
// elements in the SST_DATA_TYPE enum.
static CONST VARTYPE g_rgvtTypes[] =
{
    VT_BSTR,
    VT_BSTR,
    VT_I4,
    VT_CY,
    VT_BOOL,
    VT_DATE,
    VT_DATE,
    VT_DATE,
    VT_DATE,
    VT_DATE,
    VT_I1,
    VT_I2,
    VT_I4,
    VT_UI1,
    VT_UI2,
    VT_UI4,
    VT_R4,
    VT_R8,
    VT_R8,
    VT_BSTR,
    VT_ARRAY,
    VT_ARRAY,
    VT_UI2,
    VT_BSTR,
    //
    // note(cmr): ADD NEW VALUES IMMEDIATELY BEFORE THIS COMMENT.
    //            If adding new values, see comment above.
    //
    VT_EMPTY
};


/*
 * Function:    GetStringFromType()
 *
 * Purpose:     Returns an xml-data type string for the specified
 *              SDT_DATA_TYPE value.
 *
 * Arguments:
 *  sdt             [in]    The data type whose descriptive string is desired.
 *                          This must be a valid SST_DATA_TYPE value (less
 *                          than SDT_INVALID)
 *
 * Returns:
 *  A pointer to a constant string id representing the type.  This string
 *  should not be modified or freed.
 *  This method will never return NULL.
 *
 */
LPCWSTR
GetStringFromType(CONST SST_DATA_TYPE sdt)
{
    // there must be an entry in g_rgdtTypeStrings for every real
    // value of SST_DATA_TYPE
    //
    Assert(SDT_INVALID == celems(g_rgdtTypeStrings));
    Assert(sdt < SDT_INVALID);

    // since comparing SDT_s is somewhat cheaper than comparing whole strings,
    // we just walk through the whole list sequentually
    //

    CONST INT nSize = celems(g_rgdtTypeStrings);
    LPCWSTR pszResult;
    INT i;

    pszResult = NULL;
    i = 0;
    for ( ; i < nSize; ++i)
    {
        SST_DATA_TYPE sdtCurrent;

        sdtCurrent = g_rgdtTypeStrings[i].m_sdt;
        if (sdt == sdtCurrent)
        {
            // we have a match
            pszResult = g_rgdtTypeStrings[i].m_pszType;
            break;
        }
    }

    AssertSz(pszResult, "GetStringFromType: "
             "sdt not found in g_rgdtTypeStrings!");

    return pszResult;
}


/*
 * Function:    GetTypeFromString()
 *
 * Purpose:     Returns the appropriate SDT_DATA_TYPE value for the given
 *              xml-data type string.
 *
 * Arguments:
 *  pszTypeString   [in]    The data type identifier whose SDT_DATA_TYPE value
 *                          is desired.
 *
 * Returns:
 *  If the string is found, it returns the appropriate value of the
 *  SST_DATA_TYPE enumeration.
 *  If the string is not found, it returns SDT_INVALID.
 *
 * Notes:
 *  The source string is compared to known strings in a case-insensitive
 *  comparison.
 *
 */
SST_DATA_TYPE
GetTypeFromString(LPCWSTR pszTypeString)
{
    // there must be an entry in g_rgdtTypeStrings for every real
    // value of SST_DATA_TYPE
    //
    Assert(SDT_INVALID == celems(g_rgdtTypeStrings));

    SST_DATA_TYPE sdtResult;

    sdtResult = SDT_INVALID;

    {
        // now search for the string in the list, using a
        // standard binary search
        //
        INT nLow;
        INT nMid;
        INT nHigh;

        nLow = 0;
        nHigh = celems(g_rgdtTypeStrings) - 1;

        while (TRUE)
        {
            if (nLow > nHigh)
            {
                // not found
                //
                break;
            }

            nMid = (nLow + nHigh) / 2;

            {
                LPCWSTR pszCurrent;
                int result;

                pszCurrent = g_rgdtTypeStrings[nMid].m_pszType;

                result = _wcsicmp(pszTypeString, pszCurrent);
                if (result < 0)
                {
                    nHigh = nMid - 1;
                }
                else if (result > 0)
                {
                    nLow = nMid + 1;
                }
                else
                {
                    // found
                    //
                    sdtResult = g_rgdtTypeStrings[nMid].m_sdt;
                    break;
                }
            }
        }
    }

    return sdtResult;
}

VARTYPE
GetVarTypeFromString(LPCWSTR pszTypeString)
{
    SST_DATA_TYPE sdt = SDT_INVALID;

    sdt = GetTypeFromString(pszTypeString);

    return g_rgvtTypes[sdt];
}


// not an exported function, but one that we use internally...
HRESULT
HrGetChildElement(IXMLDOMNode * pxdn,
                  LPCWSTR pszNodeName,
                  IXMLDOMNode ** ppxdn);


CONST WCHAR pszTextType [] = L"string";

//+---------------------------------------------------------------------------
//
//  Function:   HrGetTypedValueFromElement
//
//  Purpose:    Given an IXMLDOMNode that should be of type
//              dt:string, returns a new BSTR containing that
//              string.
//
//  Arguments:
//      pxdn        IXMLDOMNode to extract the string from.
//                  It is intended that this node be of type
//                  NODE_ELEMENT.
//
//      pszDataType The type of the data encoded in the element.
//
//      pvarOut     Address of a VARIANT that will obtain the
//                  data value.  This must be freed when
//                  no longer needed.
//
//  Returns:
//      S_OK if *pvarOut contains the data of the desired type
//
//  Notes:
//
HRESULT
HrGetTypedValueFromElement(IXMLDOMNode * pxdn,
                           CONST LPCWSTR pszDataType,
                           VARIANT * pvarOut)
{
    Assert(pxdn);
    Assert(pszDataType);
    Assert(pvarOut);

    HRESULT hr;
    BSTR bstrDataType;

    bstrDataType = ::SysAllocString(pszDataType);
    if (bstrDataType)
    {
        hr = pxdn->put_dataType(bstrDataType);
        if (SUCCEEDED(hr))
        {
            hr = pxdn->get_nodeTypedValue(pvarOut);
            if (FAILED(hr))
            {
                TraceError("HrGetTypedValueFromElement: "
                           "get_nodeTypedValue()", hr);

                // clear pvarOut
                ::VariantInit(pvarOut);
            }
        }
        else
        {
            TraceError("HrGetTypedValueFromElement: "
                       "put_dataType()", hr);
        }

        ::SysFreeString(bstrDataType);
    }
    else
    {
        hr = E_OUTOFMEMORY;

        TraceError("HrGetTypedValueFromElement: "
                   "SysAllocString()", hr);
    }

    TraceError("HrGetTypedValueFromElement", hr);
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Function:   HrGetTypedValueFromChildElement
//
//  Purpose:    Given an IXMLDOMElement, finds its child element
//              of the given name, and extracts the data contained
//              in that child element, decoding it from the specified
//              format.
//
//  Arguments:
//      pxdn          The element which should contain the specified
//                    children
//
//      arypszTokens  A serial list of child element names that uniquely
//                    describe the desired element.
//
//      cTokens       The number of element names contained in
//                    arypszTokens.
//
//      pszDataType   The type of the data encoded in the element.
//
//      pvarOut       Address of a VARIANT that will obtain the
//                    data value.  This must be freed when
//                    no longer needed.
//
//  Returns:
//      S_OK if *pbstrOut has been set to a new BSTR
//      S_FALSE if the specified element did not exist.
//              in this case, *pbstrOut is set to NULL
//
//  Notes:
//      for example, if the document looked like this:
//        <foo><bar>text</bar></foo>
//      and pxdn referred to <foo>, arypszTokens = [ "bar" ]
//      cTokens = 1, and sdtType = "string", this would
//      return "text".
//      See the definition of HrGetNestedChildElement() for
//      further explination.
//
HRESULT
HrGetTypedValueFromChildElement(IXMLDOMNode * pxdn,
                                CONST LPCWSTR * arypszTokens,
                                CONST ULONG cTokens,
                                CONST LPCWSTR pszDataType,
                                VARIANT * pvarOut)
{
    Assert(pxdn);
    Assert(arypszTokens);
    Assert(cTokens);
    Assert(pszDataType);
    Assert(pvarOut);

    HRESULT hr;
    IXMLDOMNode * pxdnTemp;

    pxdnTemp = NULL;

    hr = HrGetNestedChildElement(pxdn, arypszTokens, cTokens, &pxdnTemp);
    if (FAILED(hr))
    {
        pxdnTemp = NULL;

        goto Cleanup;
    }

    if (S_OK == hr)
    {
        Assert(pxdn);

        hr = HrGetTypedValueFromElement(pxdnTemp, pszDataType, pvarOut);
        if (FAILED(hr))
        {
            goto Cleanup;
        }
    }
    // hr is S_FALSE if bstrResult is NULL, or S_OK if
    // pvarOut has been retrieved

Cleanup:
    SAFE_RELEASE(pxdnTemp);

    TraceErrorOptional("HrGetTypedValueFromChildElement", hr, (S_FALSE == hr));
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Function:   HrGetTextValueFromElement
//
//  Purpose:    Given an IXMLDOMNode that should be of type
//              dt:string, returns a new BSTR containing that
//              string.
//
//  Arguments:
//      pxdn        IXMLDOMNode to extract the string from.
//                  It is intended that this node be of type
//                  NODE_ELEMENT.
//
//      pbstrOut    Address of a BSTR that will obtain the
//                  text value.  This must be freed when
//                  no longer needed.
//
//  Returns:
//      S_OK if *pbstrOut has been set to a new BSTR
//
//  Notes:
//      This function should return an error if the element
//      contains things other than NODE_TEXT children.
//
HRESULT
HrGetTextValueFromElement(IXMLDOMNode * pxdn,
                          BSTR * pbstrOut)
{
    Assert(pxdn);
    Assert(pbstrOut);

    HRESULT hr;
    VARIANT varOut;

    *pbstrOut = NULL;

    hr = HrGetTypedValueFromElement(pxdn,
                                    pszTextType,
                                    &varOut);

    if (S_OK == hr)
    {
        Assert(VT_BSTR == V_VT(&varOut));

        *pbstrOut = V_BSTR(&varOut);

        // note: Don't clear varOut, since it's the returned BSTR
    }

    TraceErrorOptional("HrGetTextValueFromElement", hr, (S_FALSE == hr));
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Function:   HrGetTextValueFromChildElement
//
//  Purpose:    Given an IXMLDOMElement, finds its child element
//              of the given name, and extracts the dt:type="string"
//              data contained in that child element.
//
//  Arguments:
//      pxdn          The element which should contain the specified
//                    children
//
//      arypszTokens  A serial list of child element names that uniquely
//                    describe the desired element.
//
//      cTokens       The number of element names contained in
//                    arypszTokens.
//
//      pbstrOut    Address of a BSTR that will obtain the
//                  text value.  This must be freed when
//                  no longer needed.
//
//  Returns:
//      S_OK if *pbstrOut has been set to a new BSTR
//      S_FALSE if the specified element did not exist.
//              in this case, *pbstrOut is set to NULL
//
//  Notes:
//      for example, if the document looked like this:
//        <foo><bar>text</bar></foo>
//      and pxdn referred to <foo>, arypszTokens = [ "bar" ]
//      and cTokens = 1, this would return "text".
//      See the definition of HrGetNestedChildElement() for
//      further explination.
//
HRESULT
HrGetTextValueFromChildElement(IXMLDOMNode * pxdn,
                               const LPCWSTR * arypszTokens,
                               const ULONG cTokens,
                               BSTR * pbstrOut)
{
    HRESULT hr;
    VARIANT varOut;

    *pbstrOut = NULL;

    hr = HrGetTypedValueFromChildElement(pxdn,
                                         arypszTokens,
                                         cTokens,
                                         pszTextType,
                                         &varOut);
    if (S_OK == hr)
    {
        Assert(VT_BSTR == V_VT(&varOut));

        *pbstrOut = V_BSTR(&varOut);

        // note: Don't clear varOut, since it's the returned BSTR
    }

    TraceErrorOptional("HrGetTextValueFromChildElement", hr, (S_FALSE == hr));
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Function:   FIsThisTheNodeName
//
//  Purpose:    Returns TRUE if the tag name of the specified XML DOM
//              element is the specified string.
//
//  Arguments:
//      pxdn        Node whose name is being tested
//
//      pszNodeName Proposed name of the node
//
//  Returns:
//      TRUE if the node name matches, FALSE if not
//
//  Notes:
//      for the xml node declared as
//          <ab:foo xmlns:ab="urn:...">...</ab:foo>
//      IsThisTheNodeName() will return TRUE only when
//      pszNodeName == T"foo";
BOOL
FIsThisTheNodeName(IXMLDOMNode * pxdn,
                   LPCWSTR pszNodeName)
{
    Assert(pxdn);
    Assert(pszNodeName);

    HRESULT hr;
    BSTR bstrNodeName;
    BOOL fResult;
    int result;

    bstrNodeName = NULL;

    hr = pxdn->get_baseName(&bstrNodeName);
    if (SUCCEEDED(hr))
    {
        Assert(bstrNodeName);

        result = wcscmp(bstrNodeName, pszNodeName);

        ::SysFreeString(bstrNodeName);

        return (result == 0) ? TRUE : FALSE;
    }

    TraceError("OBJ: FIsThisTheNodeName - get_baseName", hr);
    Assert(!bstrNodeName);

    return FALSE;
}


//+---------------------------------------------------------------------------
//
//  Function:   FIsThisTheNodeNameWithNamespace
//
//  Purpose:    Returns TRUE if the element name of the specified XML DOM
//              element is the specified string and is in a given namespace.
//
//  Arguments:
//      pxdn               Node whose name is being tested
//      pszNodeName        Proposed base name of the node
//      pszNamespaceURI    Proposed namespace URI of the node
//
//  Returns:
//      TRUE if the node name matches, FALSE if not
//
//  Notes:
//      for the xml node declared as
//          <ab:foo xmlns:ab="urn:...">...</ab:foo>
//      FIsThisTheNodeNameWithNamespace() will return TRUE only when
//      pszNodeName == L"foo" and pszNamespaceURI == L"urn:..."
BOOL
FIsThisTheNodeNameWithNamespace(IXMLDOMNode * pxdn,
                                LPCWSTR pszNodeName,
                                LPCWSTR pszNamespaceURI)
{
    Assert(pxdn);
    Assert(pszNodeName);
    Assert(pszNamespaceURI);

    HRESULT hr = S_OK;;
    BSTR    bstrNodeName = NULL;
    BSTR    bstrNamespaceURI = NULL;
    BOOL    fResult = FALSE;

    hr = pxdn->get_baseName(&bstrNodeName);

    if (SUCCEEDED(hr))
    {
        if (bstrNodeName)
        {
            hr = pxdn->get_namespaceURI(&bstrNamespaceURI);

            if (SUCCEEDED(hr))
            {
                if (bstrNamespaceURI)
                {
                    if ((lstrcmpW(bstrNodeName, pszNodeName) == 0) &&
                        (lstrcmpW(bstrNamespaceURI, pszNamespaceURI) == 0))
                    {
                        fResult = TRUE;
                    }

                    ::SysFreeString(bstrNamespaceURI);
                }
            }

            ::SysFreeString(bstrNodeName);
        }
    }

    TraceError("FIsThisTheNodeNameWithNamespace(): "
               "Exiting", hr);

    return fResult;
}


//+---------------------------------------------------------------------------
//
//  Function:   FIsThisTheNodeTextValue
//
//  Purpose:    Determines whether the text value of a node matches a specified
//              value
//
//  Arguments:
//      pxdn         [in] The node to check
//      cszTextValue [in] The value to compare against
//
//  Returns:
//    TRUE if the node's text value matches the value specified in cszTextValue
//    FALSE otherwise
//
//  Author:     spather   2000/11/2
//
//  Notes:
//
BOOL
FIsThisTheNodeTextValue(
    IN IXMLDOMNode * pxdn,
    IN LPCWSTR     cszTextValue)
{
    HRESULT    hr = S_OK;
    BOOL       fResult = FALSE;
    BSTR       bstrNodeText = NULL;

    Assert(pxdn);
    Assert(cszTextValue);

    hr = pxdn->get_text(&bstrNodeText);

    if (SUCCEEDED(hr))
    {
        Assert(bstrNodeText);

        if (0 == lstrcmpW(bstrNodeText, cszTextValue))
        {
            fResult = TRUE;
        }
        SysFreeString(bstrNodeText);
    }
    else
    {
        TraceError("FIsThisTheNodeTextValue(): "
                   "Failed to get node text",
                   hr);
    }

    TraceError("FIsThisTheNodeTextValue(): "
               "Exiting",
               hr);

    return fResult;
}


//+---------------------------------------------------------------------------
//
//  Function:   FAreNodeValuesEqual
//
//  Purpose:    Compares the text values of two DOM nodes and, if they are
//              equal, returns TRUE.
//
//  Arguments:
//      pxdn1 [in] The first node
//      pxdn2 [in] The second node
//
//  Returns:
//    TRUE if the nodes' text values are equal, false otherwise.
//
//  Author:     spather   2000/11/2
//
//  Notes:
//
BOOL
FAreNodeValuesEqual(
    IN IXMLDOMNode * pxdn1,
    IN IXMLDOMNode * pxdn2)
{
    HRESULT    hr = S_OK;
    BOOL       fResult = FALSE;
    BSTR       bstrNode1Text = NULL;
    BSTR       bstrNode2Text = NULL;

    hr = pxdn1->get_text(&bstrNode1Text);

    if (SUCCEEDED(hr))
    {
        Assert(bstrNode1Text);

        hr = pxdn2->get_text(&bstrNode2Text);

        if (SUCCEEDED(hr))
        {
            Assert(bstrNode2Text);
        }
        else
        {
            TraceError("FAreNodeValuesEqual(): "
                       "Failed to get node 2 text",
                       hr);
        }
    }
    else
    {
        TraceError("FAreNodeValuesEqual(): "
                   "Failed to get node 1 text",
                   hr);
    }

    if (SUCCEEDED(hr))
    {
        if (0 == lstrcmpW(bstrNode1Text, bstrNode2Text))
        {
            fResult = TRUE;
        }
    }

    if (bstrNode1Text)
    {
        SysFreeString(bstrNode1Text);
    }

    if (bstrNode2Text)
    {
        SysFreeString(bstrNode2Text);
    }

    TraceError("FAreNodeValuesEqual(): "
               "Exiting",
               hr);

    return fResult;
}


//+---------------------------------------------------------------------------
//
//  Function:   HrAreThereDuplicatesInChildNodeTextValues
//
//  Purpose:    Starting from a specified parent node, this function
//              examines all child nodes that have a given name, and
//              checks their text values to see whether duplicates
//              exist (i.e. whether two or more of these child nodes
//              have the same text value).
//
//  Arguments:
//      pxdn              [in]  The parent node
//      cszXSLPattern     [in]  XSL Pattern identifying the name of the child
//                              nodes to examine
//      fCaseSensitive    [in]  Indicates whether the text values should be
//                              examined with a case-sensitive or case-
//                              insensitive comparison (if TRUE, compare will
//                              be case-sensitive)
//      pfDuplicatesExist [out] Receives a boolean value indicating whether or
//                              not there were duplicates in the child node
//                              values. (TRUE if there were duplicates
//
//  Returns:
//      If the function succeeds, the return value is S_OK. Otherwise, the
//      function returns one of the COM error codes defined in WinError.h.
//
//  Author:     spather   2000/11/5
//
//  Notes:
//    This function uses a very simple O(n^2) algorithm. Do not use it to
//    check nodes that have very many (> 100) children.

HRESULT
HrAreThereDuplicatesInChildNodeTextValues(
    IN  IXMLDOMNode * pxdn,
    IN  LPCWSTR     cszXSLPattern,
    IN  BOOL        fCaseSensitive,
    OUT BOOL        * pfDuplicatesExist)
{
    HRESULT            hr = S_OK;
    BOOL               fDuplicatesExist = FALSE;
    LONG               lNumNodes = 0;
    BSTR               * rgbstrValues = NULL;
    BSTR               bstrXSLPattern;
    IXMLDOMNodeList    * pxdnlNodes = NULL;

    Assert(pxdn);
    Assert(cszXSLPattern);
    Assert(pfDuplicatesExist);

    // Get the child nodes that match the XSL pattern.

    bstrXSLPattern = SysAllocString(cszXSLPattern);

    if (bstrXSLPattern)
    {
        hr = pxdn->selectNodes(bstrXSLPattern, &pxdnlNodes);

        SysFreeString(bstrXSLPattern);
    }
    else
    {
        hr = E_OUTOFMEMORY;
        TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
                   "Failed to allocate BSTR for XSL pattern",
                   hr);
    }

    // Construct an array of the child nodes' text values.

    if (SUCCEEDED(hr))
    {
        Assert(pxdnlNodes);

        hr = pxdnlNodes->get_length(&lNumNodes);

        if (SUCCEEDED(hr))
        {
            if (lNumNodes > 0)
            {
                rgbstrValues = new BSTR[lNumNodes];
                if (rgbstrValues)
                {
                    ZeroMemory(rgbstrValues, lNumNodes * sizeof(BSTR));

                    for (LONG i = 0; SUCCEEDED(hr) && (i < lNumNodes); i++)
                    {
                        IXMLDOMNode * pxdnChild = NULL;

                        hr = pxdnlNodes->get_item(i, &pxdnChild);

                        if (SUCCEEDED(hr))
                        {
                            Assert(pxdnChild);

                            hr = pxdnChild->get_text(&rgbstrValues[i]);

                            if (FAILED(hr))
                            {
                                TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
                                           "Failed to get node text value",
                                           hr);
                            }
                            pxdnChild->Release();
                        }
                        else
                        {
                            TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
                                       "Failed to get list item",
                                       hr);
                        }
                    }
                }
                else
                {
                    hr = E_OUTOFMEMORY;
                    TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
                               "Failed to allocate text value array",
                               hr);
                }

            }
        }

        pxdnlNodes->Release();
    }
    else
    {
        TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
                   "Failed to get nodes matching XSL pattern",
                   hr);
    }

    // Check the array for duplicates.

    if (SUCCEEDED(hr) && (lNumNodes > 0))
    {
        Assert(rgbstrValues);

        for (LONG i = 0; i < lNumNodes; i++)
        {
            for (LONG j = i+1; j < lNumNodes; j++)
            {
                if (fCaseSensitive)
                {
                    if (0 == lstrcmpW(rgbstrValues[i], rgbstrValues[j]))
                    {
                        fDuplicatesExist = TRUE;
                        break;
                    }
                }
                else
                {
                    if (0 == lstrcmpiW(rgbstrValues[i], rgbstrValues[j]))
                    {
                        fDuplicatesExist = TRUE;
                        break;
                    }
                }
            }
        }
    }

    // Clean up the array.

    if (rgbstrValues)
    {
        for (LONG i = 0; i < lNumNodes; i++)
        {
            if (rgbstrValues[i])
            {
                SysFreeString(rgbstrValues[i]);
                rgbstrValues[i] = NULL;
            }
        }
        delete [] rgbstrValues;
        rgbstrValues = NULL;
    }

    if (SUCCEEDED(hr))
    {
        *pfDuplicatesExist = fDuplicatesExist;
    }

    TraceError("HrAreThereDuplicatesInChildNodeTextValues(): "
               "Exiting",
               hr);
    return hr;
}

HRESULT
HrGetFirstChildElement(IXMLDOMNode * pxdn,
                       LPCWSTR pszNodeName,
                       IXMLDOMNode ** ppxdn)
{
    Assert(pxdn);
    Assert(pszNodeName);
    Assert(ppxdn);

    HRESULT hr;
    IXMLDOMNode * pxdnChild;

    pxdnChild = NULL;

    hr = pxdn->get_firstChild(&pxdnChild);
    if (FAILED(hr))
    {
        TraceError("HrGetFirstChildElement - get_firstChild", hr);

        pxdnChild = NULL;

        goto Cleanup;
    }
    // maybe there is a child, maybe not.

    hr = HrGetChildElement(pxdnChild, pszNodeName, ppxdn);

Cleanup:
    SAFE_RELEASE(pxdnChild);

    TraceErrorOptional("HrGetFirstChildElement", hr, (S_FALSE == hr));
    return hr;
}


HRESULT
HrGetNextChildElement(IXMLDOMNode * pxdnLastChild,
                      LPCWSTR pszNodeName,
                      IXMLDOMNode ** ppxdn)
{
    Assert(pxdnLastChild);
    Assert(pszNodeName);
    Assert(ppxdn);

    HRESULT hr;
    IXMLDOMNode * pxdnChild;
    IXMLDOMNode * pxdnResult;

    pxdnChild = NULL;
    pxdnResult = NULL;

    // we're searching for another child.
    hr = pxdnLastChild->get_nextSibling(&pxdnChild);
    if (FAILED(hr))
    {
        TraceError("HrGetNextChildElement - get_nextSibling", hr);
        pxdnChild = NULL;

        // we couldn't get the next child, and we haven't found a result,
        // so we need to barf.
        goto Cleanup;
    }
    // maybe there was a next node, maybe there wasn't

    hr = HrGetChildElement(pxdnChild, pszNodeName, &pxdnResult);
    if (FAILED(hr))
    {
        goto Cleanup;
    }

Cleanup:
    Assert(FImplies(S_OK == hr, pxdnResult));
    Assert(FImplies(S_OK != hr, !pxdnResult));

    SAFE_RELEASE(pxdnChild);

    *ppxdn = pxdnResult;

    TraceErrorOptional("HrGetNextChildElement", hr, (S_FALSE == hr));
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Function:   HrGetChildElement
//
//  Purpose:    Returns the first child element of a given name.
//
//  Arguments:
//      pxdn        Node to start searching at.  This node and its
//                  subsequent siblings are searched.
//                  Note that this MAY be null, in which case this
//                  function doesn't do much...
//
//      pszNodeName Name of the desired child node.
//
//      ppxdn       On return, contiains an AddRef()'d IXMLDOMNode
//                  pointer to the child element, if one of the
//                  given name exists.
//
//  Returns:
//      S_OK    if a matching child node has been found.  *ppxdn contains
//              a reference to this found node
//      S_FALSE if no matching child was found.  *ppxdn is set to NULL.
//
//  Notes:
//      To retrieve a child element declared as <xxxx:yyyy>, pszNodeName
//      must be "yyyy".
//      TODO: make sure the elements considered match a specified
//            namespace uri.
HRESULT
HrGetChildElement(IXMLDOMNode * pxdn,
                  LPCWSTR pszNodeName,
                  IXMLDOMNode ** ppxdn)
{
    Assert(ppxdn);
    Assert(pszNodeName);

    HRESULT hr;
    IXMLDOMNode * pxdnChild;

    if (pxdn)
    {
        pxdn->AddRef();
    }

    // in case pxdnChild is NULL
    hr = S_FALSE;
    pxdnChild = pxdn;

    while (pxdnChild)
    {
        // invaiant: pxdnChild is an addref()'d pointer to the current
        // child of interest
        IXMLDOMNode * pxdnTemp;
        DOMNodeType dnt;

        pxdnTemp = NULL;

        // get the DOMNodeType
        hr = pxdnChild->get_nodeType(&dnt);
        if (FAILED(hr))
        {
            TraceError("HrGetChildElement - get_nodeType", hr);

            goto Error;
        }

        if (NODE_ELEMENT == dnt)
        {
            if (FIsThisTheNodeName(pxdnChild, pszNodeName))
            {
                break;
            }
        }

        hr = pxdnChild->get_nextSibling(&pxdnTemp);
        if (FAILED(hr))
        {
            TraceError("HrGetChildElement - get_nextSibling", hr);

            // we couldn't get the next child, and we haven't found a result,
            // so we need to barf.
            goto Error;
        }
        Assert((S_OK == hr) || (S_FALSE == hr));
        Assert(FImplies(S_OK == hr, pxdnTemp));
        Assert(FImplies(S_FALSE == hr, !pxdnTemp));

        // note: if hr is S_FALSE, we're done

        pxdnChild->Release();
        pxdnChild = pxdnTemp;
    }

    Assert((S_OK == hr) || (S_FALSE == hr));
    Assert(FImplies(S_OK == hr, pxdnChild));
    Assert(FImplies(S_FALSE == hr, !pxdnChild));

    // post-loop-condition: pxdnChild is an addref'd pointer to the
    //                      matching child, of type NODE_ELEMENT.
    //                      if there is no match, pxdnChild is NULL.

Cleanup:
    *ppxdn = pxdnChild;

    TraceErrorOptional("HrGetChildElement", hr, (S_FALSE == hr));
    return hr;

Error:
    hr = E_FAIL;

    SAFE_RELEASE(pxdnChild);

    goto Cleanup;
}

// Used for Validation duplicate tags in Description Document - Guru
HRESULT HrIsElementPresentOnce(
    IXMLDOMNode * pxdnRoot,
    LPCWSTR pszNodeName ) {

    HRESULT hr = S_OK ;
    LONG nNumNodes = 0;
    IXMLDOMNodeList    * pxdnlNodes = NULL;
    BSTR bstrXSLPattern = NULL;

    bstrXSLPattern = SysAllocString(pszNodeName);

    if (bstrXSLPattern)
    {
        hr = pxdnRoot->selectNodes(bstrXSLPattern, &pxdnlNodes);
        if(hr == S_OK)
        {
            hr = pxdnlNodes->get_length(&nNumNodes);
            if(SUCCEEDED(hr))
            {
                if ( nNumNodes == 1 )
                   hr = S_OK;
                else {
                   hr = E_FAIL ;
                   //hr = S_FALSE;
                }
            }
        }
        else
            hr = E_FAIL;
    }
    else
    {
        hr = E_OUTOFMEMORY;
        TraceError("HrIsElementPresentOnce(): "
                   "Failed to allocate BSTR for XSL pattern",
                   hr);
    }

    SAFE_RELEASE(pxdnlNodes);
    SysFreeString(bstrXSLPattern);

    TraceErrorOptional("HrIsElementPresentOnce", hr, (S_FALSE == hr));
    return hr;
}

// Used for Validation duplicate tags in Description Document - Guru
HRESULT HrCheckForDuplicateElement(IXMLDOMNode * pxdnRoot,
                                            LPCWSTR pszNodeName ) {

    HRESULT hr = S_OK ;
    LONG nNumNodes = 0;
    IXMLDOMNodeList    * pxdnlNodes = NULL;
    BSTR bstrXSLPattern = NULL;

    bstrXSLPattern = SysAllocString(pszNodeName);

    if (bstrXSLPattern)
    {
        hr = pxdnRoot->selectNodes(bstrXSLPattern, &pxdnlNodes);
        if(hr == S_OK)
        {
            hr = pxdnlNodes->get_length(&nNumNodes);
            if(SUCCEEDED(hr))
            {
                if ( nNumNodes > 1 )
                {
                   hr = E_FAIL;
                   TraceError("HrCheckForDuplicateElement(): "
                             "Duplicate Tag Present",
                             hr);
                }
                else {
                    hr = S_OK;
                }
            }
        }
    }
    else
    {
        hr = E_OUTOFMEMORY;
        TraceError("HrCheckForDuplicateElement(): "
                   "Failed to allocate BSTR for XSL pattern",
                   hr);
    }

    SAFE_RELEASE(pxdnlNodes);
    SysFreeString(bstrXSLPattern);

    TraceErrorOptional("HrCheckForDuplicateElement", hr, (S_FALSE == hr));
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   HrGetNestedChildElement
//
//  Purpose:    Returns the nth-level child of a particular element,
//              given the name of the element and the name of each
//              intermediate element.
//
//  Arguments:
//      pxdnRoot      The element which should contain the specified
//                    children
//
//      arypszTokens  A serial list of child element names that uniquely
//                    describe the desired element.  See notes below.
//
//      cTokens       The number of element names contained in
//                    arypszTokens.
//
//      ppxdn         On return, contiains an AddRef()'d IXMLDOMNode
//                    pointer to the child element, if one of the
//                    given name exists.
//
//  Returns:
//      S_OK    if a matching child node has been found.  *ppxdn contains
//              a reference to this found node
//      S_FALSE if no matching child was found.  *ppxdn is set to NULL.
//
//  Notes:
//      To retrieve a child element declared as <xxxx:yyyy>, pszNodeName
//      must be "yyyy".
//      TODO: make it ensure that there is only ONE matching node!
//      also...
//      TODO: make sure the elements considered match a specified
//            namespace uri.
//
// overview of function:
// given a list of element names and a root element, it returns the
// "n"th child element of the given element.
// e.g., if the "root" element passed in was <foo> and the tokens
// passed in were ["bar","car","dar" and "ear"], and the document
// looked like the following,
// <foo> <--- pxdnRoot
//  <bar>
//   <car>
//    <dar>
//     <ear/>
//    </dar>
//   </car>
//  </bar>
// </foo>
// this returns the IXMLDOMNode reference to "<ear/>"
//
// if more than one of the desired name exists at a given level,
// this will chose the first element.
// <foo>
//  <bar>...</bar> <!-- this one is returned -->
//  <bar>...</bar>
// </foo>
//
// TODO: only return an element if its namespace matches the desired
//       namespace uri
//
//      pszNameSpaceURI = URI of the namespace that each element in the
//      token list must be defined in
HRESULT
HrGetNestedChildElement(IXMLDOMNode * pxdnRoot,
                        const LPCWSTR * arypszTokens,
                        const ULONG cTokens,
                        IXMLDOMNode ** ppxdn)
{
    HRESULT hr;
    IXMLDOMNode * pxdnCurrent;
    ULONG i;

    Assert(pxdnRoot);
    Assert(arypszTokens);
    Assert(cTokens);
    Assert(ppxdn);

    pxdnCurrent = pxdnRoot;
    pxdnCurrent->AddRef();

    i = 0;
    for ( ; i < cTokens; ++i)
    {
        Assert(arypszTokens[i]);

        IXMLDOMNode * pxdnChild;

        pxdnChild = NULL;


        hr = HrGetFirstChildElement(pxdnCurrent, arypszTokens[i], &pxdnChild);
        if (FAILED(hr))
        {
            goto Error;
        }
        else if (S_FALSE == hr)
        {
            // the child of the specified name doesn't exist.  well, then...
            // we return S_FALSE and NULL in this case, which is what
            // we happen to have in hr and will put in pxdnCurrent
            goto Error;
        }
        Assert(pxdnChild);

        pxdnCurrent->Release();
        pxdnCurrent = pxdnChild;
    }
    Assert(pxdnCurrent);

    // pxdnCurrent is the desired child

Cleanup:
    Assert(FImplies((S_OK == hr), pxdnCurrent));
    Assert(FImplies(S_OK != hr, !pxdnCurrent));

    *ppxdn = pxdnCurrent;

    TraceErrorOptional("HrGetNestedChildElement", hr, (S_FALSE == hr));
    return hr;

Error:
    // we didn't find a match
    SAFE_RELEASE(pxdnCurrent);
    goto Cleanup;
}


// return values:
//   S_OK       all values have been read
//   error      some element couldn't be read.
//              the returned array is undefined.
HRESULT
HrReadElementWithParseData (IXMLDOMNode * pxdn,
                            const ULONG cElems,
                            const DevicePropertiesParsingStruct dppsInfo [],
                            LPCWSTR pszBaseUrl,
                            LPWSTR arypszReadValues [])
{
    Assert(pxdn);
    Assert(cElems);
    Assert(dppsInfo);
    Assert(arypszReadValues);

    HRESULT hr = S_OK;
    ULONG i;

    ::ZeroMemory(arypszReadValues, sizeof(LPWSTR *) * cElems);

    i = 0;
    for ( ; i < cElems; ++i )
    {
        BSTR bstrTemp;

        bstrTemp = NULL;

        hr = HrGetTextValueFromChildElement(pxdn,
                                            &(dppsInfo[i].m_pszTagName),
                                            1,
                                            &bstrTemp);
        Assert(FImplies((S_FALSE == hr), !bstrTemp));
        if (S_OK == hr)
        {
            Assert(bstrTemp);

            if (dppsInfo[i].m_fIsUrl)
            {
                LPWSTR pszTemp;

                pszTemp = NULL;
                if (pszBaseUrl)
                {
                    // we have to combine base and relative urls
                    hr = HrCombineUrl(pszBaseUrl, bstrTemp, &pszTemp);
                }
                else
                {
                    // just copy and validate the url
                    hr = HrCopyAndValidateUrl(bstrTemp, &pszTemp);
                    if (S_FALSE == hr)
                    {
                        // invalid urls aren't acceptable
                        hr = E_FAIL;
                    }
                }

                if (SUCCEEDED(hr))
                {
                    arypszReadValues[i] = pszTemp;
                }
            }
            else
            {
                // just copy the value
                arypszReadValues[i] = WszAllocateAndCopyWsz(bstrTemp);
                if (!(arypszReadValues[i]))
                {
                    hr = E_OUTOFMEMORY;
                }
            }

            ::SysFreeString(bstrTemp);
        }
        // note: hr may have been changed by the above code

        if (FAILED(hr))
        {
            Assert(!(arypszReadValues[i]));
            goto Error;
        }
        TraceTag(ttidUPnPDescriptionDoc,"XML Tag - %S - %S",dppsInfo[i].m_pszTagName,arypszReadValues[i]);
    }

    if (S_FALSE == hr)
    {
        hr = S_OK;
    }

Cleanup:
    TraceError("HrReadElementWithParseData", hr);
    return hr;

Error:
    // we need to free the strings we've already allocated
    {
        ULONG j;

        // i is the number of elements successfully read above
        j = 0;
        for ( ; j < i; ++j )
        {
            Assert(arypszReadValues[j]);

            delete [] arypszReadValues[j];

            arypszReadValues[j] = NULL;
        }
    }

    goto Cleanup;
}

// Used for Validation duplicate tags in Description Document - Guru
// If the element is optional we check if the Tag is present At most Once
// IF the element is required we check if its present exactly once
HRESULT HrAreElementTagsValid(IXMLDOMNode *pxdnRoot,
                                     const ULONG cElems,
                                     const DevicePropertiesParsingStruct dppsInfo [])
{
    HRESULT hr = S_OK;
    ULONG i;

    i = 0;
    for ( ; i < cElems; ++i )
    {
        if (dppsInfo[i].m_fOptional)
        {
            hr = HrCheckForDuplicateElement(pxdnRoot,dppsInfo[i].m_pszTagName);
        }
        else {
            hr = HrIsElementPresentOnce(pxdnRoot,dppsInfo[i].m_pszTagName);
        }
        if(FAILED(hr))
            break;
    }


    TraceError("HrAreElementTagsValid", hr);
    return hr;
}

// return TRUE if all of the non-optional values (as specified by the
// m_fOptional in dppsInfo) are non-null in arypszReadValues.
// equivalently, it returns FALSE if there exists a value marked as
// required (m_fOptional == FALSE) that is NULL in arypszReadValues.
BOOL
fAreReadValuesComplete (const ULONG cElems,
                        const DevicePropertiesParsingStruct dppsInfo [],
                        const LPCWSTR arypszReadValues [])
{
    ULONG i;
    BOOL fResult;

    fResult = TRUE;
    i = 0;
    for ( ; i < cElems; ++i )
    {
        if (!dppsInfo[i].m_fOptional)
        {
            if (!(arypszReadValues[i]))
            {
                fResult = FALSE;
                break;
            }
        }
    }

    return fResult;
}


//+---------------------------------------------------------------------------
//
//  Function:   HrGetTextValueFromAttribute
//
//  Purpose:    Retrieves the text value of an attribute on a DOM element node
//
//  Arguments:
//      pxdn           [in] XML DOM Node representing the element whose
//                          attribute is to be read
//      szAttrName     [in] Name of the attribute
//      pbstrAttrValue [in] Receives the attribute text value.
//
//  Returns:
//    If the function successfully reads the attribute value, the return value
//    is S_OK and the attribute value is returned at pbstrAttrValue. If the
//    attribute does not exist on the element, the function returns S_FALSE,
//    and NULL at pbstrAttrValue. Otherwise the function returns one of the
//    error codes defined in WinError.h.
//
//  Author:     spather   2000/11/9
//
//  Notes:
//
HRESULT
HrGetTextValueFromAttribute(
    IN  IXMLDOMNode    * pxdn,
    IN  LPCWSTR        szAttrName,
    OUT BSTR           * pbstrAttrValue)
{
    HRESULT        hr = S_OK;
    IXMLDOMElement * pxde = NULL;
    BSTR           bstrValue = NULL;

    hr = pxdn->QueryInterface(IID_IXMLDOMElement,
                              (void **) &pxde);

    if (SUCCEEDED(hr))
    {
        BSTR   bstrAttrName = NULL;

        Assert(pxde);

        bstrAttrName = SysAllocString(szAttrName);

        if (bstrAttrName)
        {
            VARIANT varValue;

            VariantInit(&varValue);

            hr = pxde->getAttribute(bstrAttrName,
                                    &varValue);

            if (SUCCEEDED(hr))
            {
                if (VT_NULL == varValue.vt)
                {
                    Assert(S_FALSE == hr);
                }
                else if (VT_BSTR == varValue.vt)
                {
                    bstrValue = V_BSTR(&varValue);
                }
                else
                {
                    Assert(FALSE); // Should never be here.
                }
            }
            else
            {
                TraceError("HrGetTextValueFromAttribute(): "
                           "Failed to get attribute value",
                           hr);
            }

            SysFreeString(bstrAttrName);
        }
        else
        {
            hr = E_OUTOFMEMORY;
            TraceError("HrGetTextValueFromAttribute(): "
                       "Failed to allocate attribute name BSTR",
                       hr);
        }

        pxde->Release();
    }
    else
    {
        TraceError("HrGetTextValueFromAttribute(): "
                   "Failed to get IXMLDOMElement interface",
                   hr);
    }

    if (SUCCEEDED(hr))
    {
        *pbstrAttrValue = bstrValue;
    }
    else
    {
        if (bstrValue)
        {
            SysFreeString(bstrValue);
        }
    }

    TraceHr(ttidError, FAL, hr, (hr == S_FALSE), "HrGetTextValueFromAttribute");

    return hr;
}

/*
 * Function:    HrSetTextAttribute()
 *
 * Purpose:     Sets an text-valued attribute on an XML element.
 *
 * Arguments:
 *  pElement        [in]    The element on which to set the attribute
 *  pcwszAttrName   [in]    The attribute name
 *  pcwszValue      [in]    The value to give the attribute
 *
 * Returns:
 *  S_OK if successful, other HRESULT otherwise.
 */

HRESULT
HrSetTextAttribute(
                   IXMLDOMElement * pElement,
                   LPCWSTR        pcwszAttrName,
                   LPCWSTR        pcwszValue)
{
    HRESULT hr = S_OK;
    VARIANT vValue;
    BSTR    bstrValue, bstrAttrName;

    // Allocate BSTRs for the attribute name and value.

    bstrValue = SysAllocString(pcwszValue);

    if (bstrValue)
    {
        bstrAttrName = SysAllocString(pcwszAttrName);

        if (!bstrAttrName)
        {
            hr = E_OUTOFMEMORY;
            SysFreeString(bstrValue);
        }

    }
    else
    {
        hr = E_OUTOFMEMORY;
    }

    if (SUCCEEDED(hr))
    {
        // danielwe: Bug 144883
        VariantInit(&vValue);

        vValue.vt = VT_BSTR;
        V_BSTR(&vValue) = bstrValue;

        hr = pElement->setAttribute(bstrAttrName, vValue);

        SysFreeString(bstrValue);
        SysFreeString(bstrAttrName);
    }

    return hr;
}


/*
 * Function:    HrCreateElementWithType()
 *
 * Purpose:     Creates an XML element containing a binary value that is
 *              encoded in bin.base64.
 *
 * Arguments:
 *  pDoc                [in]    Document in which to create the element
 *  pcwszElementName    [in]    Name for the new element
 *  pszDataType         [in]    The type of the data encoded in the element.
 *  varData             [in]    Data to insert as the element's value
 *  ppElement           [out]   Address at which to place the pointer to the
 *                              new element object
 *
 * Returns:
 *  S_OK if successful, other HRESULT otherwise.
 *
 * Notes:
 *  This function does not actually insert the new element into the document
 *  tree.
 */

HRESULT
HrCreateElementWithType(
                     IN   IXMLDOMDocument *     pDoc,
                     IN   LPCWSTR               pcwszElementName,
                     IN   LPCWSTR               pszDataType,
                     IN   VARIANT               varData,
                     OUT  IXMLDOMElement **     ppElement)
{
    Assert(pDoc);
    Assert(pcwszElementName);
    Assert(pszDataType);
    Assert(ppElement);

    HRESULT         hr;
    BSTR            bstrElementName;

    hr = S_OK;
    *ppElement = NULL;

    bstrElementName = SysAllocString(pcwszElementName);
    if (bstrElementName)
    {
        IXMLDOMElement  * pElement = NULL;

        hr = pDoc->createElement(bstrElementName,
                                 &pElement);

        if (SUCCEEDED(hr))
        {
            BSTR bstrDataType;

            bstrDataType = ::SysAllocString(pszDataType);
            if (bstrDataType)
            {
                hr = pElement->put_dataType(bstrDataType);
                if (SUCCEEDED(hr))
                {
                    hr = pElement->put_nodeTypedValue(varData);
                    if (SUCCEEDED(hr))
                    {
                        *ppElement = pElement;
                        pElement->AddRef();
                    }
                    else
                    {
                        TraceError("HrCreateElementWithType(): "
                                   "put_nodeTypedValue", hr);
                    }
                }
                else
                {
                    TraceError("HrCreateElementWithType(): "
                               "put_dataType", hr);
                }

                ::SysFreeString(bstrDataType);
            }
            else
            {
                hr = E_OUTOFMEMORY;
                TraceError("HrCreateElementWithType(): "
                           "SysAllocString", hr);
            }

            pElement->Release();
        }
        else
        {
            TraceError("HrCreateElementWithType(): "
                       "Could not create element", hr);
        }

        ::SysFreeString(bstrElementName);
    }
    else
    {
        hr = E_OUTOFMEMORY;
        TraceError("HrCreateElementWithType(): "
                   "Could not allocate BSTR", hr);
    }

    return hr;
}


/*
 * Function:    HrCreateElementWithTextValue()
 *
 * Purpose:     Creates an XML element containing a text string as
 *              a value.
 *
 * Arguments:
 *  pDoc                [in]    Document in which to create the element
 *  pcwszElementName    [in]    Name for the new element
 *  pcwszTextValue      [in]    Text to insert as the element's value
 *  ppElement           [out]   Address at which to place the pointer to the
 *                              new element object
 *
 * Returns:
 *  S_OK if successful, other HRESULT otherwise.
 *
 * Notes:
 *  This function does not actually insert the new element into the document
 *  tree.
 */

HRESULT
HrCreateElementWithTextValue(
                             IN   IXMLDOMDocument * pDoc,
                             IN   LPCWSTR         pcwszElementName,
                             IN   LPCWSTR         pcwszTextValue,
                             OUT  IXMLDOMElement  ** ppElement)
{
    HRESULT         hr = S_OK;
    IXMLDOMElement  * pElement = NULL;
    BSTR            bstrElementName, bstrTextValue;

    *ppElement = NULL;

    // Allocate BSTRs for the element name and value.

    bstrElementName = SysAllocString(pcwszElementName);

    if (bstrElementName)
    {
        bstrTextValue = SysAllocString(pcwszTextValue);

        if (!bstrTextValue)
        {
            hr = E_OUTOFMEMORY;
            SysFreeString(bstrElementName);
        }

    }
    else
    {
        hr = E_OUTOFMEMORY;
    }

    if (SUCCEEDED(hr))
    {
        hr = pDoc->createElement(bstrElementName,
                                 &pElement);

        if (SUCCEEDED(hr))
        {
            IXMLDOMText * pText = NULL;

            hr = pDoc->createTextNode(bstrTextValue,
                                  &pText);

            if (SUCCEEDED(hr))
            {
                VARIANT vAfter;

                // danielwe: Bug 144882
                VariantInit(&vAfter);

                vAfter.vt = VT_EMPTY;

                hr = pElement->insertBefore(pText,
                                            vAfter,
                                            NULL);

                if (FAILED(hr))
                {
                    TraceError("HrCreateElementWithTextValue(): "
                               "Failed to insert text node", hr);
                }

                pText->Release();
            }
            else
            {
                TraceError("HrCreateElementWithTextValue(): "
                           "Failed to create text node", hr);
            }


            if (SUCCEEDED(hr))
            {
                *ppElement = pElement;
                pElement->AddRef();
            }

            pElement->Release();
        }
        else
        {
            TraceError("HrCreateElementWithTextValue(): "
                       "Could not create element", hr);
        }

        SysFreeString(bstrElementName);
        SysFreeString(bstrTextValue);
    }
    else
    {
        TraceError("HrCreateElementWithTextValue(): "
                   "Could not allocate BSTRs", hr);
    }

    return hr;
}

/*
 * Function:    HrCreateElement()
 *
 * Purpose:     Create an XML Element DOM object
 *
 * Arguments:
 *  pxdd                [in]    The document object in whose context the
 *                              element will be created
 *  pcwszElementName    [in]    Name of the element to be created
 *  pcwszNamespaceURI   [in]    URI identifying the namespace to which
 *                              the element name belongs
 *  ppxdnNewElt         [out]   The newly created element object
 *
 * Return Value:
 *  S_OK if successful, other HRESULT otherwise.
 *
 * Notes:
 *  (none)
 */

HRESULT
HrCreateElement(
    IN  IXMLDOMDocument    * pxdd,
    IN  LPCWSTR            pcwszElementName,
    IN  LPCWSTR            pcwszNamespaceURI,
    OUT IXMLDOMNode        ** ppxdnNewElt)
{
    HRESULT        hr = S_OK;
    IXMLDOMNode    * pxdnNewElt = NULL;
    BSTR           bstrElementName = NULL;
    BSTR           bstrNamespaceURI = NULL;

    Assert(pxdd);
    Assert(pcwszElementName);
    Assert(ppxdnNewElt);

    // Allocate BSTRs

    bstrElementName = SysAllocString(pcwszElementName);

    if (bstrElementName)
    {
        if (pcwszNamespaceURI)
        {
            bstrNamespaceURI = SysAllocString(pcwszNamespaceURI);

            if (NULL == bstrNamespaceURI)
            {
                hr = E_OUTOFMEMORY;
                TraceError("HrCreateElement(): "
                           "Failed to allocate memory for name string",
                           hr);
            }
        }

        if (SUCCEEDED(hr))
        {
            VARIANT varNodeType;

            VariantInit(&varNodeType);
            varNodeType.vt = VT_I4;
            V_I4(&varNodeType) = (int) NODE_ELEMENT;

            hr = pxdd->createNode(varNodeType,
                                  bstrElementName,
                                  bstrNamespaceURI,
                                  &pxdnNewElt);

            if (FAILED(hr))
            {
                TraceError("HrCreateElement(): "
                           "Failed to create element node",
                           hr);
            }


            if (bstrNamespaceURI)
            {
                SysFreeString(bstrNamespaceURI);
            }
        }


        SysFreeString(bstrElementName);
    }
    else
    {
        hr = E_OUTOFMEMORY;
        TraceError("HrCreateElement(): "
                   "Failed to allocate memory for name string",
                   hr);
    }

    if (SUCCEEDED(hr))
    {
        *ppxdnNewElt = pxdnNewElt;
    }

    TraceError("HrCreateElement(): "
               "Exiting",
               hr);

    return hr;
}


/*
 * Function:    HrAppendProcessingInstruction()
 *
 * Purpose:     Append a processing instruction to the DOM document
 *
 * Arguments:
 *  pxdd                [in]    The document object in whose context the
 *                              element will be created
 *  pcwszName           [in]    Name of the processing instruction to be created
 *  pcwszValue          [in]    The text value of the p.i.
 *
 * Return Value:
 *  S_OK if successful, other HRESULT otherwise.
 *
 * Notes:
 *  (none)
 */

HRESULT
HrAppendProcessingInstruction(
    IN  IXMLDOMDocument * pxdd,
    IN  LPCWSTR         pcwszName,
    IN  LPCWSTR         pcwszValue)
{
    HRESULT hr = S_OK;

    Assert(pxdd);
    Assert(pcwszName);
    Assert(pcwszValue);

    IXMLDOMProcessingInstruction* pxdpi = NULL;
    BSTR bstrTarget = SysAllocString(pcwszName);
    BSTR bstrData = SysAllocString(pcwszValue);
    if (bstrTarget && bstrData)
    {
        hr = pxdd->createProcessingInstruction(bstrTarget, bstrData, &pxdpi);
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }
    SysFreeString(bstrTarget);
    SysFreeString(bstrData);

    if (SUCCEEDED(hr))
    {
        IXMLDOMNode* pxdn = NULL;
        hr = pxdpi->QueryInterface(IID_IXMLDOMNode, (void**)&pxdn);
        if (SUCCEEDED(hr))
        {
            hr = pxdd->appendChild(pxdn, NULL);
            pxdn->Release();
        }
    }

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

    return hr;
}