On Wed, Mar 07, 2018 at 10:51:53PM +0000, Owen Rudge wrote:
Signed-off-by: Owen Rudge <orudge(a)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;