[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