[PATCH 6/7] wsdapi: Implement generation of SOAP XML message

Huw Davies huw at codeweavers.com
Mon Mar 12 05:48:12 CDT 2018


On Wed, Mar 07, 2018 at 10:51:53PM +0000, Owen Rudge wrote:
> Signed-off-by: Owen Rudge <orudge at codeweavers.com>
> ---
>   dlls/wsdapi/Makefile.in       |   2 +-
>   dlls/wsdapi/soap.c            | 423 
> +++++++++++++++++++++++++++++++++++++++++-
>   dlls/wsdapi/wsdapi_internal.h |   4 +
>   dlls/wsdapi/xml.c             |   2 +-
>   4 files changed, 427 insertions(+), 4 deletions(-)

There's a lot going on in the patch, can you split it?

> diff --git a/dlls/wsdapi/Makefile.in b/dlls/wsdapi/Makefile.in
> index 2e7c448125..762a3b2f12 100644
> --- a/dlls/wsdapi/Makefile.in
> +++ b/dlls/wsdapi/Makefile.in
> @@ -1,6 +1,6 @@
>  MODULE    = wsdapi.dll
>  IMPORTLIB = wsdapi
> -IMPORTS   = rpcrt4 user32 ws2_32
> +IMPORTS   = rpcrt4 user32 webservices ws2_32
>  
>  C_SRCS = \
>  	address.c \
> diff --git a/dlls/wsdapi/soap.c b/dlls/wsdapi/soap.c
> index 09b2215840..12b60ce23e 100644
> --- a/dlls/wsdapi/soap.c
> +++ b/dlls/wsdapi/soap.c
> @@ -25,6 +25,8 @@
>  
>  #include "wsdapi_internal.h"
>  #include "wine/debug.h"
> +#include "wine/heap.h"
> +#include "webservices.h"
>  
>  WINE_DEFAULT_DEBUG_CHANNEL(wsdapi);
>  
> @@ -58,12 +60,209 @@ static const WCHAR envelopeNsUri[] = {
>      'w','w','w','.','w','3','.','o','r','g','/',
>      '2','0','0','3','/','0','5','/','s','o','a','p','-','e','n','v','e','l','o','p','e', 0 };
>  
> +static const WCHAR addressingPrefix[] = { 'w','s','a', 0 };
> +static const WCHAR discoveryPrefix[] = { 'w','s','d', 0 };
> +static const WCHAR envelopePrefix[] = { 's','o','a','p', 0 };
> +static const WCHAR headerString[] = { 'H','e','a','d','e','r', 0 };
> +static const WCHAR actionString[] = { 'A','c','t','i','o','n', 0 };
> +static const WCHAR messageIdString[] = { 'M','e','s','s','a','g','e','I','D', 0 };
> +static const WCHAR toString[] = { 'T','o', 0 };
> +static const WCHAR relatesToString[] = { 'R','e','l','a','t','e','s','T','o', 0 };
> +static const WCHAR appSequenceString[] = { 'A','p','p','S','e','q','u','e','n','c','e', 0 };
> +static const WCHAR emptyString[] = { 0 };
>  static const WCHAR bodyString[] = { 'B','o','d','y', 0 };
>  static const WCHAR helloString[] = { 'H','e','l','l','o', 0 };
>  static const WCHAR endpointReferenceString[] = { 'E','n','d','p','o','i','n','t','R','e','f','e','r','e','n','c','e', 0 };
>  static const WCHAR addressString[] = { 'A','d','d','r','e','s','s', 0 };
>  static const WCHAR metadataVersionString[] = { 'M','e','t','a','d','a','t','a','V','e','r','s','i','o','n', 0 };
>  
> +struct discovered_namespace
> +{
> +    struct list entry;
> +    LPCWSTR prefix;
> +    LPCWSTR uri;
> +};
> +
> +static char *wide_to_utf8(LPCWSTR wideString)
> +{
> +    char *newString = NULL;
> +    int sizeNeeded = 0;
> +
> +    if (wideString == NULL)
> +        return NULL;
> +
> +    sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, wideString, -1, NULL, 0, NULL, NULL);
> +
> +    if (sizeNeeded < 0)
> +        return NULL;
> +
> +    newString = heap_alloc(sizeNeeded);
> +    WideCharToMultiByte(CP_UTF8, 0, wideString, -1, newString, sizeNeeded, NULL, NULL);
> +
> +    return newString;
> +}
> +
> +static WS_XML_STRING *populate_xml_string(LPCWSTR str)
> +{
> +    WS_XML_STRING *xml = heap_alloc_zero(sizeof(WS_XML_STRING));
> +
> +    if (xml == NULL)
> +        return NULL;
> +
> +    xml->bytes = (BYTE *)wide_to_utf8(str);
> +
> +    if (xml->bytes == NULL)
> +    {
> +        heap_free(xml);
> +        return NULL;
> +    }
> +
> +    xml->dictionary = NULL;
> +    xml->id = 0;
> +    xml->length = (xml->bytes == NULL) ? 0 : lstrlenA((LPCSTR)xml->bytes);
> +
> +    return xml;
> +}
> +
> +static inline void free_xml_string(WS_XML_STRING *value)
> +{
> +    if (value == NULL)
> +        return;
> +
> +    if (value->bytes != NULL)
> +        heap_free(value->bytes);
> +
> +    heap_free(value);
> +}
> +
> +static BOOL write_xml_attribute(WSDXML_ATTRIBUTE *attribute, WS_XML_WRITER *writer)
> +{
> +    WS_XML_STRING *localName = NULL, *elementNs = NULL, *nsPrefix = NULL;
> +    WS_XML_UTF16_TEXT utf16Text;
> +    BOOL retVal = FALSE;
> +    HRESULT ret;
> +
> +    if (attribute == NULL)
> +        return TRUE;
> +
> +    /* Start the attribute */
> +    localName = populate_xml_string(attribute->Name->LocalName);
> +    if (localName == NULL) goto cleanup;
> +
> +    if (attribute->Name->Space == NULL)
> +    {
> +        elementNs = populate_xml_string(emptyString);
> +        if (elementNs == NULL) goto cleanup;
> +
> +        nsPrefix = NULL;
> +    }
> +    else
> +    {
> +        elementNs = populate_xml_string(attribute->Name->Space->Uri);
> +        if (elementNs == NULL) goto cleanup;
> +
> +        nsPrefix = populate_xml_string(attribute->Name->Space->PreferredPrefix);
> +        if (nsPrefix == NULL) goto cleanup;
> +    }
> +
> +    ret = WsWriteStartAttribute(writer, nsPrefix, localName, elementNs, FALSE, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    utf16Text.text.textType = WS_XML_TEXT_TYPE_UTF16;
> +    utf16Text.bytes = (BYTE *)attribute->Value;
> +    utf16Text.byteCount = lstrlenW(attribute->Value) * sizeof(WCHAR);
> +
> +    ret = WsWriteText(writer, (WS_XML_TEXT *)&utf16Text, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    ret = WsWriteEndAttribute(writer, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    retVal = TRUE;
> +
> +cleanup:
> +    free_xml_string(localName);
> +    free_xml_string(elementNs);
> +    free_xml_string(nsPrefix);
> +
> +    return retVal;
> +}
> +
> +static BOOL write_xml_element(WSDXML_ELEMENT *element, WS_XML_WRITER *writer)
> +{
> +    WS_XML_STRING *localName = NULL, *elementNs = NULL, *nsPrefix = NULL;
> +    WSDXML_ATTRIBUTE *currentAttribute;
> +    WS_XML_UTF16_TEXT utf16Text;
> +    WSDXML_NODE *currentChild;
> +    WSDXML_TEXT *nodeAsText;
> +    BOOL retVal = FALSE;
> +    HRESULT ret;
> +    int textLen;
> +
> +    if (element == NULL)
> +        return TRUE;
> +
> +    /* Start the element */
> +    localName = populate_xml_string(element->Name->LocalName);
> +    if (localName == NULL) goto cleanup;
> +
> +    elementNs = populate_xml_string(element->Name->Space->Uri);
> +    if (elementNs == NULL) goto cleanup;
> +
> +    nsPrefix = populate_xml_string(element->Name->Space->PreferredPrefix);
> +    if (nsPrefix == NULL) goto cleanup;
> +
> +    ret = WsWriteStartElement(writer, nsPrefix, localName, elementNs, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    /* Write attributes */
> +    currentAttribute = element->FirstAttribute;
> +
> +    while (currentAttribute != NULL)
> +    {
> +        if (!write_xml_attribute(currentAttribute, writer)) goto cleanup;
> +        currentAttribute = currentAttribute->Next;
> +    }
> +
> +    /* Write child elements */
> +    currentChild = element->FirstChild;
> +
> +    while (currentChild != NULL)
> +    {
> +        if (currentChild->Type == ElementType)
> +        {
> +            if (!write_xml_element((WSDXML_ELEMENT *)currentChild, writer)) goto cleanup;
> +        }
> +        else if (currentChild->Type == TextType)
> +        {
> +            nodeAsText = (WSDXML_TEXT *)currentChild;
> +            textLen = lstrlenW(nodeAsText->Text);
> +
> +            utf16Text.text.textType = WS_XML_TEXT_TYPE_UTF16;
> +            utf16Text.bytes = (BYTE *)nodeAsText->Text;
> +            utf16Text.byteCount = min(WSD_MAX_TEXT_LENGTH, textLen) * sizeof(WCHAR);
> +
> +            ret = WsWriteText(writer, (WS_XML_TEXT *)&utf16Text, NULL);
> +            if (FAILED(ret)) goto cleanup;
> +        }
> +
> +        currentChild = currentChild->Next;
> +    }
> +
> +    /* End the element */
> +    ret = WsWriteEndElement(writer, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    retVal = TRUE;
> +
> +cleanup:
> +    free_xml_string(localName);
> +    free_xml_string(elementNs);
> +    free_xml_string(nsPrefix);
> +
> +    return retVal;
> +}
> +
>  static WSDXML_ELEMENT *add_child_element(IWSDXMLContext *xmlContext, WSDXML_ELEMENT *parent, LPCWSTR nsUri, LPCWSTR name, LPCWSTR text)
>  {
>      WSDXML_ELEMENT *elementObj;
> @@ -141,11 +340,231 @@ static LPWSTR ulonglong_to_string(void *parent, ULONGLONG value)
>      return ret;
>  }
>  
> +static BOOL add_discovered_namespace(struct list *namespaces, WSDXML_NAMESPACE *discoveredNs)
> +{
> +    struct discovered_namespace *ns;
> +
> +    LIST_FOR_EACH_ENTRY(ns, namespaces, struct discovered_namespace, entry)
> +    {
> +        if (lstrcmpW(ns->uri, discoveredNs->Uri) == 0)
> +            return TRUE; /* Already added */
> +    }
> +
> +    ns = WSDAllocateLinkedMemory(namespaces, sizeof(struct discovered_namespace));
> +
> +    if (ns == NULL)
> +        return FALSE;
> +
> +    ns->prefix = duplicate_string(ns, discoveredNs->PreferredPrefix);
> +    ns->uri = duplicate_string(ns, discoveredNs->Uri);
> +
> +    if ((ns->prefix == NULL) || (ns->uri == NULL))
> +        return FALSE;
> +
> +    list_add_tail(namespaces, &ns->entry);
> +    return TRUE;
> +}
> +
> +static WSDXML_ELEMENT *create_soap_header_xml_elements(IWSDXMLContext *xmlContext, WSD_SOAP_HEADER *header, struct list *discoveredNamespaces)
> +{
> +    WSDXML_ELEMENT *headerElement, *appSequenceElement;
> +    WSDXML_NAME *headerName = NULL;
> +
> +    /* <s:Header> */
> +    if (FAILED(IWSDXMLContext_AddNameToNamespace(xmlContext, envelopeNsUri, headerString, &headerName))) goto cleanup;

If the above condition succeeds, then it'll jump to cleanup with headerElement uninitialized.

> +    if (FAILED(WSDXMLBuildAnyForSingleElement(headerName, NULL, &headerElement))) goto cleanup;
> +    WSDFreeLinkedMemory(headerName);
> +
> +    /* <a:Action> */
> +    if (add_child_element(xmlContext, headerElement, addressingNsUri, actionString, header->Action) == NULL) goto cleanup;
> +
> +    /* <a:MessageId> */
> +    if (add_child_element(xmlContext, headerElement, addressingNsUri, messageIdString, header->MessageID) == NULL) goto cleanup;
> +
> +    /* <a:To> */
> +    if (add_child_element(xmlContext, headerElement, addressingNsUri, toString, header->To) == NULL) goto cleanup;
> +
> +    /* <a:RelatesTo> */
> +    if (header->RelatesTo.MessageID != NULL)
> +        if (add_child_element(xmlContext, headerElement, addressingNsUri, relatesToString, header->RelatesTo.MessageID) == NULL) goto cleanup;
> +
> +    /* <d:AppSequence> */
> +    appSequenceElement = add_child_element(xmlContext, headerElement, discoveryNsUri, appSequenceString, emptyString);
> +    if (appSequenceElement == NULL) goto cleanup;
> +
> +    /* TODO: InstanceId attribute */
> +
> +    /* TODO: MessageNumber attribute */
> +
> +    /* TODO: SequenceID attribute */
> +
> +    /* </d:AppSequence> */
> +
> +    /* TODO: Write any headers */
> +
> +    /* </s:Header> */
> +
> +    return headerElement;
> +
> +cleanup:
> +    if (headerName != NULL) WSDFreeLinkedMemory(headerName);
> +    WSDXMLCleanupElement(headerElement);
> +
> +    return NULL;
> +}
> +
> +static BOOL create_soap_envelope(IWSDXMLContext *xmlContext, WSD_SOAP_HEADER *header, WSDXML_ELEMENT *bodyElement, WS_HEAP **heap, char **outputXml, ULONG *xmlLength, struct list *discoveredNamespaces)
> +{
> +    WS_XML_STRING *actualEnvelopePrefix = NULL, *envelopeUriXmlStr = NULL, *tmpPrefix = NULL, *tmpUri = NULL;
> +    WSDXML_NAMESPACE *addressingNs = NULL, *discoveryNs = NULL, *envelopeNs = NULL;
> +    WSDXML_ELEMENT *headerElement = NULL;
> +    struct discovered_namespace *ns;
> +    WS_XML_BUFFER *buffer = NULL;
> +    WS_XML_WRITER *writer = NULL;
> +    WS_XML_STRING envelope;
> +    BOOL retVal = FALSE;
> +    HRESULT ret;
> +
> +    /* Create the necessary XML prefixes */
> +    if (FAILED(IWSDXMLContext_AddNamespace(xmlContext, addressingNsUri, addressingPrefix, &addressingNs))) goto cleanup;
> +    if (!add_discovered_namespace(discoveredNamespaces, addressingNs)) goto cleanup;
> +
> +    if (FAILED(IWSDXMLContext_AddNamespace(xmlContext, discoveryNsUri, discoveryPrefix, &discoveryNs))) goto cleanup;
> +    if (!add_discovered_namespace(discoveredNamespaces, discoveryNs)) goto cleanup;
> +
> +    if (FAILED(IWSDXMLContext_AddNamespace(xmlContext, envelopeNsUri, envelopePrefix, &envelopeNs))) goto cleanup;
> +    if (!add_discovered_namespace(discoveredNamespaces, envelopeNs)) goto cleanup;
> +
> +    envelope.bytes = (BYTE*)"Envelope";
> +    envelope.length = strlen((const char *) envelope.bytes);
> +    envelope.dictionary = NULL;
> +    envelope.id = 0;
> +
> +    actualEnvelopePrefix = populate_xml_string(envelopeNs->PreferredPrefix);
> +    envelopeUriXmlStr = populate_xml_string(envelopeNs->Uri);
> +
> +    if ((actualEnvelopePrefix == NULL) || (envelopeUriXmlStr == NULL)) goto cleanup;
> +
> +    /* Now try to create the appropriate WebServices buffers, etc */
> +    ret = WsCreateHeap(16384, 4096, NULL, 0, heap, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    ret = WsCreateXmlBuffer(*heap, NULL, 0, &buffer, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    ret = WsCreateWriter(NULL, 0, &writer, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    ret = WsSetOutputToBuffer(writer, buffer, NULL, 0, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    /* Create the header XML elements */
> +    headerElement = create_soap_header_xml_elements(xmlContext, header, discoveredNamespaces);
> +    if (headerElement == NULL) goto cleanup;
> +
> +    /* <s:Envelope> */
> +    ret = WsWriteStartElement(writer, actualEnvelopePrefix, &envelope, envelopeUriXmlStr, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    LIST_FOR_EACH_ENTRY(ns, discoveredNamespaces, struct discovered_namespace, entry)
> +    {
> +        tmpPrefix = populate_xml_string(ns->prefix);
> +        tmpUri = populate_xml_string(ns->uri);
> +
> +        if ((tmpPrefix == NULL) || (tmpUri == NULL)) goto cleanup;
> +
> +        ret = WsWriteXmlnsAttribute(writer, tmpPrefix, tmpUri, FALSE, NULL);
> +        if (FAILED(ret)) goto cleanup;
> +
> +        free_xml_string(tmpPrefix);
> +        free_xml_string(tmpUri);
> +    }
> +
> +    tmpPrefix = NULL;
> +    tmpUri = NULL;
> +
> +    /* Write the header */
> +    if (!write_xml_element(headerElement, writer)) goto cleanup;
> +
> +    /* Write the body */
> +    if (!write_xml_element(bodyElement, writer)) goto cleanup;
> +
> +    ret = WsWriteEndElement(writer, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    /* </s:Envelope> */
> +
> +    /* Generate the bytes of the document */
> +    ret = WsWriteXmlBufferToBytes(writer, buffer, NULL, NULL, 0, *heap, (void**)outputXml, xmlLength, NULL);
> +    if (FAILED(ret)) goto cleanup;
> +
> +    retVal = TRUE;
> +
> +cleanup:
> +    WSDFreeLinkedMemory(addressingNs);
> +    WSDFreeLinkedMemory(discoveryNs);
> +    WSDFreeLinkedMemory(envelopeNs);
> +
> +    WSDXMLCleanupElement(headerElement);
> +
> +    free_xml_string(actualEnvelopePrefix);
> +    free_xml_string(envelopeUriXmlStr);
> +
> +    if (writer != NULL)
> +        WsFreeWriter(writer);
> +
> +    /* Don't free the heap unless the operation has failed */
> +    if ((!retVal) && (*heap != NULL))
> +    {
> +        WsFreeHeap(*heap);
> +        *heap = NULL;
> +    }
> +
> +    return retVal;
> +}
> +
>  static BOOL write_and_send_message(IWSDiscoveryPublisherImpl *impl, WSD_SOAP_HEADER *header, WSDXML_ELEMENT *bodyElement,
>      struct list *discoveredNamespaces, IWSDUdpAddress *remoteAddress, int maxInitialDelay)
>  {
> -    FIXME("stub\n");
> -    return FALSE;
> +    const char *xmlHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
> +    WS_HEAP *heap = NULL;
> +    ULONG xmlLength = 0;
> +    int xmlHeaderLen;
> +    char *xml = NULL;
> +    char *fullXml;
> +    BOOL ret = FALSE;
> +
> +    if (!create_soap_envelope(impl->xmlContext, header, bodyElement, &heap, &xml, &xmlLength, discoveredNamespaces))
> +        return FALSE;
> +
> +    /* Prefix the XML header */
> +    xmlHeaderLen = strlen(xmlHeader);
> +    fullXml = heap_alloc_zero(xmlLength + xmlHeaderLen + 1);
> +
> +    if (fullXml == NULL)
> +    {
> +        WsFreeHeap(heap);
> +        return FALSE;
> +    }
> +
> +    memcpy(fullXml, xmlHeader, xmlHeaderLen);
> +    memcpy(fullXml + xmlHeaderLen, xml, xmlLength);
> +
> +    if (remoteAddress == NULL)
> +    {
> +        /* TODO: Send the message via UDP multicast */
> +        FIXME("TODO: Send the message via UDP multicast\n");
> +    }
> +    else
> +    {
> +        /* TODO: Send the message via UDP unicast */
> +        FIXME("TODO: Send the message via UDP unicast\n");
> +    }
> +
> +    heap_free(fullXml);
> +    WsFreeHeap(heap);
> +
> +    return ret;
>  }
>  
>  HRESULT send_hello_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR pszId, ULONGLONG ullMetadataVersion, ULONGLONG ullInstanceId,
> diff --git a/dlls/wsdapi/wsdapi_internal.h b/dlls/wsdapi/wsdapi_internal.h
> index 4f8ce91d36..c9c5a72bfc 100644
> --- a/dlls/wsdapi/wsdapi_internal.h
> +++ b/dlls/wsdapi/wsdapi_internal.h
> @@ -56,4 +56,8 @@ HRESULT send_hello_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR pszId, ULONG
>      const WSD_URI_LIST *pXAddrsList, const WSDXML_ELEMENT *pHeaderAny, const WSDXML_ELEMENT *pReferenceParameterAny,
>      const WSDXML_ELEMENT *pEndpointReferenceAny, const WSDXML_ELEMENT *pAny);
>  
> +/* xml.c */
> +
> +LPWSTR duplicate_string(void *parentMemoryBlock, LPCWSTR value);
> +
>  #endif
> diff --git a/dlls/wsdapi/xml.c b/dlls/wsdapi/xml.c
> index 4d6319c36b..fc02d8b2d4 100644
> --- a/dlls/wsdapi/xml.c
> +++ b/dlls/wsdapi/xml.c
> @@ -27,7 +27,7 @@
>  
>  WINE_DEFAULT_DEBUG_CHANNEL(wsdapi);
>  
> -static LPWSTR duplicate_string(void *parentMemoryBlock, LPCWSTR value)
> +LPWSTR duplicate_string(void *parentMemoryBlock, LPCWSTR value)
>  {
>      int valueLen;
>      LPWSTR dup;



More information about the wine-devel mailing list