widl: Always check the runtimeclass interfaces presence.
[wine/zf.git] / dlls / wsdapi / soap.c
blob4251c8cd08d4319c480e9341ee618a0f2aadbe3f
1 /*
2 * Web Services on Devices
4 * Copyright 2017-2018 Owen Rudge for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include <stdarg.h>
22 #include <limits.h>
24 #define COBJMACROS
26 #include "wsdapi_internal.h"
27 #include "wine/debug.h"
28 #include "wine/heap.h"
29 #include "webservices.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(wsdapi);
33 #define APP_MAX_DELAY 500
35 static const WCHAR *discoveryTo = L"urn:schemas-xmlsoap-org:ws:2005:04:discovery";
37 static const WCHAR *actionProbe = L"http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe";
39 static const WCHAR *addressingNsUri = L"http://schemas.xmlsoap.org/ws/2004/08/addressing";
40 static const WCHAR *discoveryNsUri = L"http://schemas.xmlsoap.org/ws/2005/04/discovery";
41 static const WCHAR *envelopeNsUri = L"http://www.w3.org/2003/05/soap-envelope";
43 static const WCHAR *addressingPrefix = L"wsa";
44 static const WCHAR *discoveryPrefix = L"wsd";
45 static const WCHAR *envelopePrefix = L"soap";
46 static const WCHAR *headerString = L"Header";
47 static const WCHAR *actionString = L"Action";
48 static const WCHAR *messageIdString = L"MessageID";
49 static const WCHAR *toString = L"To";
50 static const WCHAR *relatesToString = L"RelatesTo";
51 static const WCHAR *appSequenceString = L"AppSequence";
52 static const WCHAR *instanceIdString = L"InstanceId";
53 static const WCHAR *messageNumberString = L"MessageNumber";
54 static const WCHAR *sequenceIdString = L"SequenceId";
55 static const WCHAR *bodyString = L"Body";
56 static const WCHAR *helloString = L"Hello";
57 static const WCHAR *probeString = L"Probe";
58 static const WCHAR *probeMatchString = L"ProbeMatch";
59 static const WCHAR *probeMatchesString = L"ProbeMatches";
60 static const WCHAR *byeString = L"Bye";
61 static const WCHAR *endpointReferenceString = L"EndpointReference";
62 static const WCHAR *addressString = L"Address";
63 static const WCHAR *referenceParametersString = L"ReferenceParameters";
64 static const WCHAR *typesString = L"Types";
65 static const WCHAR *scopesString = L"Scopes";
66 static const WCHAR *xAddrsString = L"XAddrs";
67 static const WCHAR *metadataVersionString = L"MetadataVersion";
69 struct discovered_namespace
71 struct list entry;
72 LPCWSTR prefix;
73 LPCWSTR uri;
76 static LPWSTR utf8_to_wide(void *parent, const char *utf8_str, int length)
78 int utf8_str_len = 0, chars_needed = 0, bytes_needed = 0;
79 LPWSTR new_str = NULL;
81 if (utf8_str == NULL) return NULL;
83 utf8_str_len = (length < 0) ? lstrlenA(utf8_str) : length;
84 chars_needed = MultiByteToWideChar(CP_UTF8, 0, utf8_str, utf8_str_len, NULL, 0);
86 if (chars_needed <= 0) return NULL;
88 bytes_needed = sizeof(WCHAR) * (chars_needed + 1);
89 new_str = WSDAllocateLinkedMemory(parent, bytes_needed);
91 MultiByteToWideChar(CP_UTF8, 0, utf8_str, utf8_str_len, new_str, chars_needed);
92 new_str[chars_needed] = 0;
94 return new_str;
97 static char *wide_to_utf8(LPCWSTR wide_string, int *length)
99 char *new_string = NULL;
101 if (wide_string == NULL)
102 return NULL;
104 *length = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, NULL, 0, NULL, NULL);
106 if (*length < 0)
107 return NULL;
109 new_string = heap_alloc(*length);
110 WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, new_string, *length, NULL, NULL);
112 return new_string;
115 static WS_XML_STRING *populate_xml_string(LPCWSTR str)
117 WS_XML_STRING *xml = heap_alloc_zero(sizeof(WS_XML_STRING));
118 int utf8Length;
120 if (xml == NULL)
121 return NULL;
123 xml->bytes = (BYTE *)wide_to_utf8(str, &utf8Length);
125 if (xml->bytes == NULL)
127 heap_free(xml);
128 return NULL;
131 xml->dictionary = NULL;
132 xml->id = 0;
133 xml->length = (xml->bytes == NULL) ? 0 : (utf8Length - 1);
135 return xml;
138 static inline void free_xml_string(WS_XML_STRING *value)
140 if (value == NULL)
141 return;
143 heap_free(value->bytes);
145 heap_free(value);
148 static HRESULT write_xml_attribute(WSDXML_ATTRIBUTE *attribute, WS_XML_WRITER *writer)
150 WS_XML_STRING *local_name = NULL, *element_ns = NULL, *ns_prefix = NULL;
151 WS_XML_UTF16_TEXT utf16_text;
152 HRESULT ret = E_OUTOFMEMORY;
153 int text_len;
155 if (attribute == NULL)
156 return S_OK;
158 /* Start the attribute */
159 local_name = populate_xml_string(attribute->Name->LocalName);
160 if (local_name == NULL) goto cleanup;
162 if (attribute->Name->Space == NULL)
164 element_ns = populate_xml_string(L"");
165 if (element_ns == NULL) goto cleanup;
167 ns_prefix = NULL;
169 else
171 element_ns = populate_xml_string(attribute->Name->Space->Uri);
172 if (element_ns == NULL) goto cleanup;
174 ns_prefix = populate_xml_string(attribute->Name->Space->PreferredPrefix);
175 if (ns_prefix == NULL) goto cleanup;
178 ret = WsWriteStartAttribute(writer, ns_prefix, local_name, element_ns, FALSE, NULL);
179 if (FAILED(ret)) goto cleanup;
181 text_len = lstrlenW(attribute->Value);
183 utf16_text.text.textType = WS_XML_TEXT_TYPE_UTF16;
184 utf16_text.bytes = (BYTE *)attribute->Value;
185 utf16_text.byteCount = min(WSD_MAX_TEXT_LENGTH, text_len) * sizeof(WCHAR);
187 ret = WsWriteText(writer, (WS_XML_TEXT *)&utf16_text, NULL);
188 if (FAILED(ret)) goto cleanup;
190 ret = WsWriteEndAttribute(writer, NULL);
191 if (FAILED(ret)) goto cleanup;
193 cleanup:
194 free_xml_string(local_name);
195 free_xml_string(element_ns);
196 free_xml_string(ns_prefix);
198 return ret;
201 static HRESULT write_xml_element(WSDXML_ELEMENT *element, WS_XML_WRITER *writer)
203 WS_XML_STRING *local_name = NULL, *element_ns = NULL, *ns_prefix = NULL;
204 WSDXML_ATTRIBUTE *current_attribute;
205 WS_XML_UTF16_TEXT utf16_text;
206 WSDXML_NODE *current_child;
207 WSDXML_TEXT *node_as_text;
208 int text_len;
209 HRESULT ret = E_OUTOFMEMORY;
211 if (element == NULL)
212 return S_OK;
214 /* Start the element */
215 local_name = populate_xml_string(element->Name->LocalName);
216 if (local_name == NULL) goto cleanup;
218 element_ns = populate_xml_string(element->Name->Space->Uri);
219 if (element_ns == NULL) goto cleanup;
221 ns_prefix = populate_xml_string(element->Name->Space->PreferredPrefix);
222 if (ns_prefix == NULL) goto cleanup;
224 ret = WsWriteStartElement(writer, ns_prefix, local_name, element_ns, NULL);
225 if (FAILED(ret)) goto cleanup;
227 /* Write attributes */
228 current_attribute = element->FirstAttribute;
230 while (current_attribute != NULL)
232 ret = write_xml_attribute(current_attribute, writer);
233 if (FAILED(ret)) goto cleanup;
234 current_attribute = current_attribute->Next;
237 /* Write child elements */
238 current_child = element->FirstChild;
240 while (current_child != NULL)
242 if (current_child->Type == ElementType)
244 ret = write_xml_element((WSDXML_ELEMENT *)current_child, writer);
245 if (FAILED(ret)) goto cleanup;
247 else if (current_child->Type == TextType)
249 node_as_text = (WSDXML_TEXT *)current_child;
250 text_len = lstrlenW(node_as_text->Text);
252 utf16_text.text.textType = WS_XML_TEXT_TYPE_UTF16;
253 utf16_text.byteCount = min(WSD_MAX_TEXT_LENGTH, text_len) * sizeof(WCHAR);
254 utf16_text.bytes = (BYTE *)node_as_text->Text;
256 ret = WsWriteText(writer, (WS_XML_TEXT *)&utf16_text, NULL);
257 if (FAILED(ret)) goto cleanup;
260 current_child = current_child->Next;
263 /* End the element */
264 ret = WsWriteEndElement(writer, NULL);
266 cleanup:
267 free_xml_string(local_name);
268 free_xml_string(element_ns);
269 free_xml_string(ns_prefix);
271 return ret;
274 static HRESULT add_child_element(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri,
275 LPCWSTR name, LPCWSTR text, WSDXML_ELEMENT **out)
277 WSDXML_ELEMENT *element_obj;
278 WSDXML_NAME *name_obj;
279 HRESULT ret;
281 ret = IWSDXMLContext_AddNameToNamespace(xml_context, ns_uri, name, &name_obj);
282 if (FAILED(ret)) return ret;
284 ret = WSDXMLBuildAnyForSingleElement(name_obj, text, &element_obj);
285 WSDFreeLinkedMemory(name_obj);
287 if (FAILED(ret)) return ret;
289 /* Add the element as a child - this will link the element's memory allocation to the parent's */
290 ret = WSDXMLAddChild(parent, element_obj);
292 if (FAILED(ret))
294 WSDFreeLinkedMemory(element_obj);
295 return ret;
298 if (out != NULL) *out = element_obj;
299 return ret;
302 HRESULT register_namespaces(IWSDXMLContext *xml_context)
304 HRESULT ret;
306 ret = IWSDXMLContext_AddNamespace(xml_context, addressingNsUri, addressingPrefix, NULL);
307 if (FAILED(ret)) return ret;
309 ret = IWSDXMLContext_AddNamespace(xml_context, discoveryNsUri, discoveryPrefix, NULL);
310 if (FAILED(ret)) return ret;
312 return IWSDXMLContext_AddNamespace(xml_context, envelopeNsUri, envelopePrefix, NULL);
315 static BOOL create_guid(LPWSTR buffer)
317 WCHAR* uuidString = NULL;
318 UUID uuid;
320 if (UuidCreate(&uuid) != RPC_S_OK)
321 return FALSE;
323 UuidToStringW(&uuid, (RPC_WSTR*)&uuidString);
325 if (uuidString == NULL)
326 return FALSE;
328 wsprintfW(buffer, L"urn:uuid:%s", uuidString);
329 RpcStringFreeW((RPC_WSTR*)&uuidString);
331 return TRUE;
334 static void populate_soap_header(WSD_SOAP_HEADER *header, LPCWSTR to, LPCWSTR action, LPCWSTR message_id,
335 WSD_APP_SEQUENCE *sequence, const WSDXML_ELEMENT *any_headers)
337 ZeroMemory(header, sizeof(WSD_SOAP_HEADER));
339 header->To = to;
340 header->Action = action;
341 header->MessageID = message_id;
342 header->AppSequence = sequence;
343 header->AnyHeaders = (WSDXML_ELEMENT *)any_headers;
345 /* TODO: Implement RelatesTo, ReplyTo, From, FaultTo */
348 #define MAX_ULONGLONG_STRING_SIZE 25
350 static LPWSTR ulonglong_to_string(void *parent, ULONGLONG value)
352 LPWSTR ret;
354 ret = WSDAllocateLinkedMemory(parent, MAX_ULONGLONG_STRING_SIZE * sizeof(WCHAR));
356 if (ret == NULL)
357 return NULL;
359 wsprintfW(ret, L"%I64u", value);
360 return ret;
363 static WSDXML_ATTRIBUTE *add_attribute(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri, LPCWSTR name)
365 WSDXML_ATTRIBUTE *attribute, *cur_attrib;
366 WSDXML_NAME *name_obj = NULL;
368 if (ns_uri == NULL)
370 name_obj = WSDAllocateLinkedMemory(NULL, sizeof(WSDXML_NAME));
371 name_obj->LocalName = duplicate_string(name_obj, name);
372 name_obj->Space = NULL;
374 else
376 if (FAILED(IWSDXMLContext_AddNameToNamespace(xml_context, ns_uri, name, &name_obj)))
377 return NULL;
380 attribute = WSDAllocateLinkedMemory(parent, sizeof(WSDXML_ATTRIBUTE));
382 if (attribute == NULL)
384 WSDFreeLinkedMemory(name_obj);
385 return NULL;
388 attribute->Element = parent;
389 attribute->Name = name_obj;
390 attribute->Next = NULL;
391 attribute->Value = NULL;
393 if (name_obj != NULL)
394 WSDAttachLinkedMemory(attribute, name_obj);
396 if (parent->FirstAttribute == NULL)
398 /* Make this the first attribute of the parent */
399 parent->FirstAttribute = attribute;
401 else
403 /* Find the last attribute and add this as the next one */
404 cur_attrib = parent->FirstAttribute;
406 while (cur_attrib->Next != NULL)
408 cur_attrib = cur_attrib->Next;
411 cur_attrib->Next = attribute;
414 return attribute;
417 static void remove_attribute(WSDXML_ELEMENT *parent, WSDXML_ATTRIBUTE *attribute)
419 WSDXML_ATTRIBUTE *cur_attrib;
421 /* Find the last attribute and add this as the next one */
422 cur_attrib = parent->FirstAttribute;
424 if (cur_attrib == attribute)
425 parent->FirstAttribute = cur_attrib->Next;
426 else
428 while (cur_attrib != NULL)
430 /* Is our attribute the next attribute? */
431 if (cur_attrib->Next == attribute)
433 /* Remove it from the list */
434 cur_attrib->Next = attribute->Next;
435 break;
438 cur_attrib = cur_attrib->Next;
442 WSDFreeLinkedMemory(attribute);
445 static HRESULT add_ulonglong_attribute(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri, LPCWSTR name,
446 ULONGLONG value)
448 WSDXML_ATTRIBUTE *attribute = add_attribute(xml_context, parent, ns_uri, name);
450 if (attribute == NULL)
451 return E_FAIL;
453 attribute->Value = ulonglong_to_string(attribute, value);
455 if (attribute->Value == NULL)
457 remove_attribute(parent, attribute);
458 return E_FAIL;
461 return S_OK;
464 static HRESULT add_string_attribute(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri, LPCWSTR name,
465 LPCWSTR value)
467 WSDXML_ATTRIBUTE *attribute = add_attribute(xml_context, parent, ns_uri, name);
469 if (attribute == NULL)
470 return E_FAIL;
472 attribute->Value = duplicate_string(attribute, value);
474 if (attribute->Value == NULL)
476 remove_attribute(parent, attribute);
477 return E_FAIL;
480 return S_OK;
483 static BOOL add_discovered_namespace(struct list *namespaces, WSDXML_NAMESPACE *discovered_ns)
485 struct discovered_namespace *ns;
487 LIST_FOR_EACH_ENTRY(ns, namespaces, struct discovered_namespace, entry)
489 if (lstrcmpW(ns->uri, discovered_ns->Uri) == 0)
490 return TRUE; /* Already added */
493 ns = WSDAllocateLinkedMemory(namespaces, sizeof(struct discovered_namespace));
495 if (ns == NULL)
496 return FALSE;
498 ns->prefix = duplicate_string(ns, discovered_ns->PreferredPrefix);
499 ns->uri = duplicate_string(ns, discovered_ns->Uri);
501 if ((ns->prefix == NULL) || (ns->uri == NULL))
502 return FALSE;
504 list_add_tail(namespaces, &ns->entry);
505 return TRUE;
508 static HRESULT build_types_list(LPWSTR buffer, size_t buffer_size, const WSD_NAME_LIST *list, struct list *namespaces)
510 LPWSTR current_buf_pos = buffer;
511 size_t memory_needed = 0;
512 const WSD_NAME_LIST *cur = list;
516 /* Calculate space needed, including NULL character, colon and potential trailing space */
517 memory_needed = sizeof(WCHAR) * (lstrlenW(cur->Element->LocalName) +
518 lstrlenW(cur->Element->Space->PreferredPrefix) + 3);
520 if (current_buf_pos + memory_needed > buffer + buffer_size)
521 return E_INVALIDARG;
523 if (cur != list)
524 *current_buf_pos++ = ' ';
526 current_buf_pos += wsprintfW(current_buf_pos, L"%s:%s", cur->Element->Space->PreferredPrefix,
527 cur->Element->LocalName);
529 /* Record the namespace in the discovered namespaces list */
530 if (!add_discovered_namespace(namespaces, cur->Element->Space))
531 return E_FAIL;
533 cur = cur->Next;
534 } while (cur != NULL);
536 return S_OK;
539 static HRESULT build_uri_list(LPWSTR buffer, size_t buffer_size, const WSD_URI_LIST *list)
541 size_t memory_needed = 0, string_len = 0;
542 const WSD_URI_LIST *cur = list;
543 LPWSTR cur_buf_pos = buffer;
547 /* Calculate space needed, including trailing space */
548 string_len = lstrlenW(cur->Element);
549 memory_needed = (string_len + 1) * sizeof(WCHAR);
551 if (cur_buf_pos + memory_needed > buffer + buffer_size)
552 return E_INVALIDARG;
554 if (cur != list)
555 *cur_buf_pos++ = ' ';
557 memcpy(cur_buf_pos, cur->Element, memory_needed);
558 cur_buf_pos += string_len;
560 cur = cur->Next;
561 } while (cur != NULL);
563 return S_OK;
566 static HRESULT duplicate_element(WSDXML_ELEMENT *parent, const WSDXML_ELEMENT *node, struct list *namespaces)
568 WSDXML_ATTRIBUTE *cur_attribute, *new_attribute, *last_attribute = NULL;
569 WSDXML_ELEMENT *new_element;
570 WSDXML_TEXT *text_node;
571 WSDXML_NODE *cur_node;
572 HRESULT ret;
574 /* First record the namespace in the discovered namespaces list */
575 if (!add_discovered_namespace(namespaces, node->Name->Space))
576 return E_FAIL;
578 ret = WSDXMLBuildAnyForSingleElement(node->Name, NULL, &new_element);
579 if (FAILED(ret)) return ret;
581 /* Duplicate the nodes */
582 cur_node = node->FirstChild;
584 while (cur_node != NULL)
586 if (cur_node->Type == ElementType)
588 ret = duplicate_element(new_element, (WSDXML_ELEMENT *)cur_node, namespaces);
589 if (FAILED(ret)) goto cleanup;
591 else if (cur_node->Type == TextType)
593 text_node = WSDAllocateLinkedMemory(new_element, sizeof(WSDXML_TEXT));
594 if (text_node == NULL) goto failed;
596 text_node->Node.Parent = NULL;
597 text_node->Node.Next = NULL;
598 text_node->Node.Type = TextType;
599 text_node->Text = duplicate_string(text_node, ((WSDXML_TEXT *)cur_node)->Text);
601 if (text_node->Text == NULL) goto failed;
603 ret = WSDXMLAddChild(new_element, (WSDXML_ELEMENT *)text_node);
604 if (FAILED(ret)) goto cleanup;
607 cur_node = cur_node->Next;
610 /* Duplicate the attributes */
611 cur_attribute = node->FirstAttribute;
613 while (cur_attribute != NULL)
615 if ((cur_attribute->Name->Space != NULL) && (!add_discovered_namespace(namespaces, cur_attribute->Name->Space)))
616 goto failed;
618 new_attribute = WSDAllocateLinkedMemory(new_element, sizeof(WSDXML_ATTRIBUTE));
619 if (new_attribute == NULL) goto failed;
621 new_attribute->Element = new_element;
622 new_attribute->Name = duplicate_name(new_attribute, cur_attribute->Name);
623 new_attribute->Value = duplicate_string(new_attribute, cur_attribute->Value);
624 new_attribute->Next = NULL;
626 if ((new_attribute->Name == NULL) || (new_attribute->Value == NULL)) goto failed;
628 if (last_attribute == NULL)
629 new_element->FirstAttribute = new_attribute;
630 else
631 last_attribute->Next = new_attribute;
633 last_attribute = new_attribute;
634 cur_attribute = cur_attribute->Next;
637 ret = WSDXMLAddChild(parent, new_element);
638 if (FAILED(ret)) goto cleanup;
640 return ret;
642 failed:
643 ret = E_FAIL;
645 cleanup:
646 WSDXMLCleanupElement(new_element);
647 return ret;
650 static HRESULT create_soap_header_xml_elements(IWSDXMLContext *xml_context, WSD_SOAP_HEADER *header,
651 struct list *discovered_namespaces, WSDXML_ELEMENT **out_element)
653 WSDXML_ELEMENT *header_element = NULL, *app_sequence_element = NULL, *temp_element;
654 WSDXML_NAME *header_name = NULL;
655 HRESULT ret;
657 /* <s:Header> */
658 ret = IWSDXMLContext_AddNameToNamespace(xml_context, envelopeNsUri, headerString, &header_name);
659 if (FAILED(ret)) goto cleanup;
661 ret = WSDXMLBuildAnyForSingleElement(header_name, NULL, &header_element);
662 if (FAILED(ret)) goto cleanup;
664 WSDFreeLinkedMemory(header_name);
666 /* <a:Action> */
667 ret = add_child_element(xml_context, header_element, addressingNsUri, actionString, header->Action, &temp_element);
668 if (FAILED(ret)) goto cleanup;
670 /* <a:MessageId> */
671 ret = add_child_element(xml_context, header_element, addressingNsUri, messageIdString, header->MessageID, &temp_element);
672 if (FAILED(ret)) goto cleanup;
674 /* <a:To> */
675 ret = add_child_element(xml_context, header_element, addressingNsUri, toString, header->To, &temp_element);
676 if (FAILED(ret)) goto cleanup;
678 /* <a:RelatesTo> */
679 if (header->RelatesTo.MessageID != NULL)
681 ret = add_child_element(xml_context, header_element, addressingNsUri, relatesToString,
682 header->RelatesTo.MessageID, &temp_element);
683 if (FAILED(ret)) goto cleanup;
686 /* <d:AppSequence> */
687 ret = add_child_element(xml_context, header_element, discoveryNsUri, appSequenceString, L"", &app_sequence_element);
688 if (FAILED(ret)) goto cleanup;
690 /* InstanceId attribute */
691 ret = add_ulonglong_attribute(xml_context, app_sequence_element, NULL, instanceIdString, min(UINT_MAX,
692 header->AppSequence->InstanceId));
693 if (FAILED(ret)) goto cleanup;
695 /* SequenceID attribute */
696 if (header->AppSequence->SequenceId != NULL)
698 ret = add_string_attribute(xml_context, app_sequence_element, NULL, sequenceIdString, header->AppSequence->SequenceId);
699 if (FAILED(ret)) goto cleanup;
702 /* MessageNumber attribute */
703 ret = add_ulonglong_attribute(xml_context, app_sequence_element, NULL, messageNumberString, min(UINT_MAX,
704 header->AppSequence->MessageNumber));
705 if (FAILED(ret)) goto cleanup;
707 /* </d:AppSequence> */
709 /* Write any headers */
710 if (header->AnyHeaders != NULL)
712 ret = duplicate_element(header_element, header->AnyHeaders, discovered_namespaces);
713 if (FAILED(ret)) goto cleanup;
716 /* </s:Header> */
718 *out_element = header_element;
719 return ret;
721 cleanup:
722 if (header_name != NULL) WSDFreeLinkedMemory(header_name);
723 WSDXMLCleanupElement(header_element);
725 return ret;
728 static HRESULT create_soap_envelope(IWSDXMLContext *xml_context, WSD_SOAP_HEADER *header, WSDXML_ELEMENT *body_element,
729 WS_HEAP **heap, char **output_xml, ULONG *xml_length, struct list *discovered_namespaces)
731 WS_XML_STRING *actual_envelope_prefix = NULL, *envelope_uri_xmlstr = NULL, *tmp_prefix = NULL, *tmp_uri = NULL;
732 WSDXML_NAMESPACE *addressing_ns = NULL, *discovery_ns = NULL, *envelope_ns = NULL;
733 WSDXML_ELEMENT *header_element = NULL;
734 struct discovered_namespace *ns;
735 WS_XML_BUFFER *buffer = NULL;
736 WS_XML_WRITER *writer = NULL;
737 WS_XML_STRING envelope;
738 HRESULT ret = E_OUTOFMEMORY;
739 static BYTE envelopeString[] = "Envelope";
741 /* Create the necessary XML prefixes */
742 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, addressingNsUri, addressingPrefix, &addressing_ns))) goto cleanup;
743 if (!add_discovered_namespace(discovered_namespaces, addressing_ns)) goto cleanup;
745 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, discoveryNsUri, discoveryPrefix, &discovery_ns))) goto cleanup;
746 if (!add_discovered_namespace(discovered_namespaces, discovery_ns)) goto cleanup;
748 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, envelopeNsUri, envelopePrefix, &envelope_ns))) goto cleanup;
749 if (!add_discovered_namespace(discovered_namespaces, envelope_ns)) goto cleanup;
751 envelope.bytes = envelopeString;
752 envelope.length = sizeof(envelopeString) - 1;
753 envelope.dictionary = NULL;
754 envelope.id = 0;
756 actual_envelope_prefix = populate_xml_string(envelope_ns->PreferredPrefix);
757 envelope_uri_xmlstr = populate_xml_string(envelope_ns->Uri);
759 if ((actual_envelope_prefix == NULL) || (envelope_uri_xmlstr == NULL)) goto cleanup;
761 /* Now try to create the appropriate WebServices buffers, etc */
762 ret = WsCreateHeap(16384, 4096, NULL, 0, heap, NULL);
763 if (FAILED(ret)) goto cleanup;
765 ret = WsCreateXmlBuffer(*heap, NULL, 0, &buffer, NULL);
766 if (FAILED(ret)) goto cleanup;
768 ret = WsCreateWriter(NULL, 0, &writer, NULL);
769 if (FAILED(ret)) goto cleanup;
771 ret = WsSetOutputToBuffer(writer, buffer, NULL, 0, NULL);
772 if (FAILED(ret)) goto cleanup;
774 /* Create the header XML elements */
775 ret = create_soap_header_xml_elements(xml_context, header, discovered_namespaces, &header_element);
776 if (FAILED(ret)) goto cleanup;
778 /* <s:Envelope> */
779 ret = WsWriteStartElement(writer, actual_envelope_prefix, &envelope, envelope_uri_xmlstr, NULL);
780 if (FAILED(ret)) goto cleanup;
782 LIST_FOR_EACH_ENTRY(ns, discovered_namespaces, struct discovered_namespace, entry)
784 tmp_prefix = populate_xml_string(ns->prefix);
785 tmp_uri = populate_xml_string(ns->uri);
787 if ((tmp_prefix == NULL) || (tmp_uri == NULL)) goto cleanup;
789 ret = WsWriteXmlnsAttribute(writer, tmp_prefix, tmp_uri, FALSE, NULL);
790 if (FAILED(ret)) goto cleanup;
792 free_xml_string(tmp_prefix);
793 free_xml_string(tmp_uri);
796 tmp_prefix = NULL;
797 tmp_uri = NULL;
799 /* Write the header */
800 ret = write_xml_element(header_element, writer);
801 if (FAILED(ret)) goto cleanup;
803 /* Write the body */
804 ret = write_xml_element(body_element, writer);
805 if (FAILED(ret)) goto cleanup;
807 ret = WsWriteEndElement(writer, NULL);
808 if (FAILED(ret)) goto cleanup;
810 /* </s:Envelope> */
812 /* Generate the bytes of the document */
813 ret = WsWriteXmlBufferToBytes(writer, buffer, NULL, NULL, 0, *heap, (void**)output_xml, xml_length, NULL);
814 if (FAILED(ret)) goto cleanup;
816 cleanup:
817 WSDFreeLinkedMemory(addressing_ns);
818 WSDFreeLinkedMemory(discovery_ns);
819 WSDFreeLinkedMemory(envelope_ns);
821 WSDXMLCleanupElement(header_element);
823 free_xml_string(actual_envelope_prefix);
824 free_xml_string(envelope_uri_xmlstr);
826 if (writer != NULL)
827 WsFreeWriter(writer);
829 /* Don't free the heap unless the operation has failed */
830 if ((FAILED(ret)) && (*heap != NULL))
832 WsFreeHeap(*heap);
833 *heap = NULL;
836 return ret;
839 static HRESULT write_and_send_message(IWSDiscoveryPublisherImpl *impl, WSD_SOAP_HEADER *header, WSDXML_ELEMENT *body_element,
840 struct list *discovered_namespaces, IWSDUdpAddress *remote_address, int max_initial_delay)
842 static const char xml_header[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
843 ULONG xml_length = 0, xml_header_len = sizeof(xml_header) - 1;
844 WS_HEAP *heap = NULL;
845 char *xml = NULL;
846 char *full_xml;
847 HRESULT ret;
849 ret = create_soap_envelope(impl->xmlContext, header, body_element, &heap, &xml, &xml_length, discovered_namespaces);
850 if (ret != S_OK) return ret;
852 /* Prefix the XML header */
853 full_xml = heap_alloc(xml_length + xml_header_len + 1);
855 if (full_xml == NULL)
857 WsFreeHeap(heap);
858 return E_OUTOFMEMORY;
861 memcpy(full_xml, xml_header, xml_header_len);
862 memcpy(full_xml + xml_header_len, xml, xml_length);
863 full_xml[xml_length + xml_header_len] = 0;
865 if (remote_address == NULL)
867 /* Send the message via UDP multicast */
868 ret = send_udp_multicast(impl, full_xml, xml_length + xml_header_len, max_initial_delay) ? S_OK : E_FAIL;
870 else
872 /* Send the message via UDP unicast */
873 ret = send_udp_unicast(full_xml, xml_length + xml_header_len, remote_address, max_initial_delay);
876 heap_free(full_xml);
877 WsFreeHeap(heap);
879 return ret;
882 HRESULT send_hello_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id, ULONGLONG metadata_ver, ULONGLONG instance_id,
883 ULONGLONG msg_num, LPCWSTR session_id, const WSD_NAME_LIST *types_list, const WSD_URI_LIST *scopes_list,
884 const WSD_URI_LIST *xaddrs_list, const WSDXML_ELEMENT *hdr_any, const WSDXML_ELEMENT *ref_param_any,
885 const WSDXML_ELEMENT *endpoint_ref_any, const WSDXML_ELEMENT *any)
887 WSDXML_ELEMENT *body_element = NULL, *hello_element, *endpoint_reference_element, *ref_params_element;
888 struct list *discoveredNamespaces = NULL;
889 WSDXML_NAME *body_name = NULL;
890 WSD_SOAP_HEADER soapHeader;
891 WSD_APP_SEQUENCE sequence;
892 WCHAR message_id[64];
893 HRESULT ret = E_OUTOFMEMORY;
894 LPWSTR buffer;
896 sequence.InstanceId = instance_id;
897 sequence.MessageNumber = msg_num;
898 sequence.SequenceId = session_id;
900 if (!create_guid(message_id)) goto failed;
902 discoveredNamespaces = WSDAllocateLinkedMemory(NULL, sizeof(struct list));
903 if (!discoveredNamespaces) goto failed;
905 list_init(discoveredNamespaces);
907 populate_soap_header(&soapHeader, discoveryTo, L"http://schemas.xmlsoap.org/ws/2005/04/discovery/Hello", message_id, &sequence, hdr_any);
909 ret = IWSDXMLContext_AddNameToNamespace(impl->xmlContext, envelopeNsUri, bodyString, &body_name);
910 if (FAILED(ret)) goto cleanup;
912 /* <soap:Body>, <wsd:Hello> */
913 ret = WSDXMLBuildAnyForSingleElement(body_name, NULL, &body_element);
914 if (FAILED(ret)) goto cleanup;
916 ret = add_child_element(impl->xmlContext, body_element, discoveryNsUri, helloString, NULL, &hello_element);
917 if (FAILED(ret)) goto cleanup;
919 /* <wsa:EndpointReference>, <wsa:Address> */
920 ret = add_child_element(impl->xmlContext, hello_element, addressingNsUri, endpointReferenceString, NULL,
921 &endpoint_reference_element);
922 if (FAILED(ret)) goto cleanup;
924 ret = add_child_element(impl->xmlContext, endpoint_reference_element, addressingNsUri, addressString, id, NULL);
925 if (FAILED(ret)) goto cleanup;
927 /* Write any reference parameters */
928 if (ref_param_any != NULL)
930 ret = add_child_element(impl->xmlContext, endpoint_reference_element, addressingNsUri, referenceParametersString,
931 NULL, &ref_params_element);
932 if (FAILED(ret)) goto cleanup;
934 ret = duplicate_element(ref_params_element, ref_param_any, discoveredNamespaces);
935 if (FAILED(ret)) goto cleanup;
938 /* Write any endpoint reference headers */
939 if (endpoint_ref_any != NULL)
941 ret = duplicate_element(endpoint_reference_element, endpoint_ref_any, discoveredNamespaces);
942 if (FAILED(ret)) goto cleanup;
945 /* <wsd:Types> */
946 if (types_list != NULL)
948 buffer = WSDAllocateLinkedMemory(hello_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
949 if (buffer == NULL) goto failed;
951 ret = build_types_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), types_list, discoveredNamespaces);
952 if (FAILED(ret)) goto cleanup;
954 ret = add_child_element(impl->xmlContext, hello_element, discoveryNsUri, typesString, buffer, NULL);
955 if (FAILED(ret)) goto cleanup;
958 /* <wsd:Scopes> */
959 if (scopes_list != NULL)
961 buffer = WSDAllocateLinkedMemory(hello_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
962 if (buffer == NULL) goto failed;
964 ret = build_uri_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), scopes_list);
965 if (FAILED(ret)) goto cleanup;
967 ret = add_child_element(impl->xmlContext, hello_element, discoveryNsUri, scopesString, buffer, NULL);
968 if (FAILED(ret)) goto cleanup;
971 /* <wsd:XAddrs> */
972 if (xaddrs_list != NULL)
974 buffer = WSDAllocateLinkedMemory(hello_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
975 if (buffer == NULL) goto failed;
977 ret = build_uri_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), xaddrs_list);
978 if (FAILED(ret)) goto cleanup;
980 ret = add_child_element(impl->xmlContext, hello_element, discoveryNsUri, xAddrsString, buffer, NULL);
981 if (FAILED(ret)) goto cleanup;
984 /* <wsd:MetadataVersion> */
985 ret = add_child_element(impl->xmlContext, hello_element, discoveryNsUri, metadataVersionString,
986 ulonglong_to_string(hello_element, min(UINT_MAX, metadata_ver)), NULL);
988 if (FAILED(ret)) goto cleanup;
990 /* Write any body elements */
991 if (any != NULL)
993 ret = duplicate_element(hello_element, any, discoveredNamespaces);
994 if (FAILED(ret)) goto cleanup;
997 /* Write and send the message */
998 ret = write_and_send_message(impl, &soapHeader, body_element, discoveredNamespaces, NULL, APP_MAX_DELAY);
999 goto cleanup;
1001 failed:
1002 ret = E_OUTOFMEMORY;
1004 cleanup:
1005 WSDFreeLinkedMemory(body_name);
1006 WSDFreeLinkedMemory(body_element);
1007 WSDFreeLinkedMemory(discoveredNamespaces);
1009 return ret;
1012 HRESULT send_bye_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id, ULONGLONG instance_id, ULONGLONG msg_num,
1013 LPCWSTR session_id, const WSDXML_ELEMENT *any)
1015 WSDXML_ELEMENT *body_element = NULL, *bye_element, *endpoint_reference_element;
1016 struct list *discovered_namespaces = NULL;
1017 WSDXML_NAME *body_name = NULL;
1018 WSD_SOAP_HEADER soap_header;
1019 WSD_APP_SEQUENCE sequence;
1020 WCHAR message_id[64];
1021 HRESULT ret = E_OUTOFMEMORY;
1023 sequence.InstanceId = instance_id;
1024 sequence.MessageNumber = msg_num;
1025 sequence.SequenceId = session_id;
1027 if (!create_guid(message_id)) goto failed;
1029 discovered_namespaces = WSDAllocateLinkedMemory(NULL, sizeof(struct list));
1030 if (!discovered_namespaces) goto failed;
1032 list_init(discovered_namespaces);
1034 populate_soap_header(&soap_header, discoveryTo, L"http://schemas.xmlsoap.org/ws/2005/04/discovery/Bye", message_id, &sequence, NULL);
1036 ret = IWSDXMLContext_AddNameToNamespace(impl->xmlContext, envelopeNsUri, bodyString, &body_name);
1037 if (FAILED(ret)) goto cleanup;
1039 /* <soap:Body>, <wsd:Bye> */
1040 ret = WSDXMLBuildAnyForSingleElement(body_name, NULL, &body_element);
1041 if (FAILED(ret)) goto cleanup;
1043 ret = add_child_element(impl->xmlContext, body_element, discoveryNsUri, byeString, NULL, &bye_element);
1044 if (FAILED(ret)) goto cleanup;
1046 /* <wsa:EndpointReference>, <wsa:Address> */
1047 ret = add_child_element(impl->xmlContext, bye_element, addressingNsUri, endpointReferenceString, NULL,
1048 &endpoint_reference_element);
1049 if (FAILED(ret)) goto cleanup;
1051 ret = add_child_element(impl->xmlContext, endpoint_reference_element, addressingNsUri, addressString, id, NULL);
1052 if (FAILED(ret)) goto cleanup;
1054 /* Write any body elements */
1055 if (any != NULL)
1057 ret = duplicate_element(bye_element, any, discovered_namespaces);
1058 if (FAILED(ret)) goto cleanup;
1061 /* Write and send the message */
1062 ret = write_and_send_message(impl, &soap_header, body_element, discovered_namespaces, NULL, 0);
1063 goto cleanup;
1065 failed:
1066 ret = E_OUTOFMEMORY;
1068 cleanup:
1069 WSDFreeLinkedMemory(body_name);
1070 WSDFreeLinkedMemory(body_element);
1071 WSDFreeLinkedMemory(discovered_namespaces);
1073 return ret;
1076 HRESULT send_probe_matches_message(IWSDiscoveryPublisherImpl *impl, const WSD_SOAP_MESSAGE *probe_msg,
1077 IWSDMessageParameters *message_params, LPCWSTR id, ULONGLONG metadata_ver, ULONGLONG instance_id,
1078 ULONGLONG msg_num, LPCWSTR session_id, const WSD_NAME_LIST *types_list, const WSD_URI_LIST *scopes_list,
1079 const WSD_URI_LIST *xaddrs_list, const WSDXML_ELEMENT *header_any, const WSDXML_ELEMENT *ref_param_any,
1080 const WSDXML_ELEMENT *endpoint_ref_any, const WSDXML_ELEMENT *any)
1082 WSDXML_ELEMENT *body_element = NULL, *probe_matches_element, *probe_match_element, *endpoint_ref_element;
1083 WSDXML_ELEMENT *ref_params_element = NULL;
1084 struct list *discovered_namespaces = NULL;
1085 IWSDUdpAddress *remote_udp_addr = NULL;
1086 IWSDAddress *remote_addr = NULL;
1087 WSDXML_NAME *body_name = NULL;
1088 WSD_SOAP_HEADER soap_header;
1089 WSD_APP_SEQUENCE sequence;
1090 WCHAR msg_id[64];
1091 LPWSTR buffer;
1092 HRESULT ret;
1094 ret = IWSDMessageParameters_GetRemoteAddress(message_params, &remote_addr);
1096 if (FAILED(ret))
1098 WARN("Unable to retrieve remote address from IWSDMessageParameters\n");
1099 return ret;
1102 ret = IWSDAddress_QueryInterface(remote_addr, &IID_IWSDUdpAddress, (LPVOID *) &remote_udp_addr);
1104 if (FAILED(ret))
1106 WARN("Remote address is not a UDP address\n");
1107 goto cleanup;
1110 sequence.InstanceId = instance_id;
1111 sequence.MessageNumber = msg_num;
1112 sequence.SequenceId = session_id;
1114 if (!create_guid(msg_id)) goto failed;
1116 discovered_namespaces = WSDAllocateLinkedMemory(NULL, sizeof(struct list));
1117 if (!discovered_namespaces) goto failed;
1119 list_init(discovered_namespaces);
1121 populate_soap_header(&soap_header, L"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous",
1122 L"http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches", msg_id, &sequence, header_any);
1123 soap_header.RelatesTo.MessageID = probe_msg->Header.MessageID;
1125 ret = IWSDXMLContext_AddNameToNamespace(impl->xmlContext, envelopeNsUri, bodyString, &body_name);
1126 if (FAILED(ret)) goto cleanup;
1128 /* <soap:Body>, <wsd:ProbeMatches> */
1129 ret = WSDXMLBuildAnyForSingleElement(body_name, NULL, &body_element);
1130 if (FAILED(ret)) goto cleanup;
1132 ret = add_child_element(impl->xmlContext, body_element, discoveryNsUri, probeMatchesString, NULL,
1133 &probe_matches_element);
1134 if (FAILED(ret)) goto cleanup;
1136 /* <wsd:ProbeMatch> */
1137 ret = add_child_element(impl->xmlContext, probe_matches_element, discoveryNsUri, probeMatchString, NULL,
1138 &probe_match_element);
1139 if (FAILED(ret)) goto cleanup;
1141 /* <wsa:EndpointReference>, <wsa:Address> */
1142 ret = add_child_element(impl->xmlContext, probe_match_element, addressingNsUri, endpointReferenceString, NULL,
1143 &endpoint_ref_element);
1144 if (FAILED(ret)) goto cleanup;
1146 ret = add_child_element(impl->xmlContext, endpoint_ref_element, addressingNsUri, addressString, id, NULL);
1147 if (FAILED(ret)) goto cleanup;
1149 /* Write any reference parameters */
1150 if (ref_param_any != NULL)
1152 ret = add_child_element(impl->xmlContext, endpoint_ref_element, addressingNsUri, referenceParametersString,
1153 NULL, &ref_params_element);
1154 if (FAILED(ret)) goto cleanup;
1156 ret = duplicate_element(ref_params_element, ref_param_any, discovered_namespaces);
1157 if (FAILED(ret)) goto cleanup;
1160 /* Write any endpoint reference headers */
1161 if (endpoint_ref_any != NULL)
1163 ret = duplicate_element(endpoint_ref_element, endpoint_ref_any, discovered_namespaces);
1164 if (FAILED(ret)) goto cleanup;
1167 /* <wsd:Types> */
1168 if (types_list != NULL)
1170 buffer = WSDAllocateLinkedMemory(probe_match_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
1171 if (buffer == NULL) goto failed;
1173 ret = build_types_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), types_list, discovered_namespaces);
1174 if (FAILED(ret)) goto cleanup;
1176 ret = add_child_element(impl->xmlContext, probe_match_element, discoveryNsUri, typesString, buffer, NULL);
1177 if (FAILED(ret)) goto cleanup;
1180 /* <wsd:Scopes> */
1181 if (scopes_list != NULL)
1183 buffer = WSDAllocateLinkedMemory(probe_match_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
1184 if (buffer == NULL) goto failed;
1186 ret = build_uri_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), scopes_list);
1187 if (FAILED(ret)) goto cleanup;
1189 ret = add_child_element(impl->xmlContext, probe_match_element, discoveryNsUri, scopesString, buffer, NULL);
1190 if (FAILED(ret)) goto cleanup;
1193 /* <wsd:XAddrs> */
1194 if (xaddrs_list != NULL)
1196 buffer = WSDAllocateLinkedMemory(probe_match_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
1197 if (buffer == NULL) goto failed;
1199 ret = build_uri_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), xaddrs_list);
1200 if (FAILED(ret)) goto cleanup;
1202 ret = add_child_element(impl->xmlContext, probe_match_element, discoveryNsUri, xAddrsString, buffer, NULL);
1203 if (FAILED(ret)) goto cleanup;
1206 /* <wsd:MetadataVersion> */
1207 ret = add_child_element(impl->xmlContext, probe_match_element, discoveryNsUri, metadataVersionString,
1208 ulonglong_to_string(probe_match_element, min(UINT_MAX, metadata_ver)), NULL);
1209 if (FAILED(ret)) goto cleanup;
1211 /* Write any body elements */
1212 if (any != NULL)
1214 ret = duplicate_element(probe_match_element, any, discovered_namespaces);
1215 if (FAILED(ret)) goto cleanup;
1218 /* Write and send the message */
1219 ret = write_and_send_message(impl, &soap_header, body_element, discovered_namespaces, remote_udp_addr, APP_MAX_DELAY);
1220 goto cleanup;
1222 failed:
1223 ret = E_FAIL;
1225 cleanup:
1226 WSDFreeLinkedMemory(body_name);
1227 WSDFreeLinkedMemory(body_element);
1228 WSDFreeLinkedMemory(discovered_namespaces);
1230 if (remote_udp_addr != NULL) IWSDUdpAddress_Release(remote_udp_addr);
1231 if (remote_addr != NULL) IWSDAddress_Release(remote_addr);
1233 return ret;
1236 static LPWSTR xml_text_to_wide_string(void *parent_memory, WS_XML_TEXT *text)
1238 if (text->textType == WS_XML_TEXT_TYPE_UTF8)
1240 WS_XML_UTF8_TEXT *utf8_text = (WS_XML_UTF8_TEXT *) text;
1241 return utf8_to_wide(parent_memory, (const char *) utf8_text->value.bytes, utf8_text->value.length);
1243 else if (text->textType == WS_XML_TEXT_TYPE_UTF16)
1245 WS_XML_UTF16_TEXT *utf_16_text = (WS_XML_UTF16_TEXT *) text;
1246 return duplicate_string(parent_memory, (LPCWSTR) utf_16_text->bytes);
1249 FIXME("Support for text type %d not implemented.\n", text->textType);
1250 return NULL;
1253 static inline BOOL read_isspace(unsigned int ch)
1255 return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
1258 static HRESULT str_to_uint64(const unsigned char *str, ULONG len, UINT64 max, UINT64 *ret)
1260 const unsigned char *ptr = str;
1262 *ret = 0;
1263 while (len && read_isspace(*ptr)) { ptr++; len--; }
1264 while (len && read_isspace(ptr[len - 1])) { len--; }
1265 if (!len) return WS_E_INVALID_FORMAT;
1267 while (len--)
1269 unsigned int val;
1271 if (!isdigit(*ptr)) return WS_E_INVALID_FORMAT;
1272 val = *ptr - '0';
1274 if ((*ret > max / 10 || *ret * 10 > max - val)) return WS_E_NUMERIC_OVERFLOW;
1275 *ret = *ret * 10 + val;
1276 ptr++;
1279 return S_OK;
1282 #define MAX_UINT64 (((UINT64)0xffffffff << 32) | 0xffffffff)
1284 static HRESULT wide_text_to_ulonglong(LPCWSTR text, ULONGLONG *value)
1286 char *utf8_text;
1287 int utf8_length;
1288 HRESULT ret;
1290 utf8_text = wide_to_utf8(text, &utf8_length);
1292 if (utf8_text == NULL) return E_OUTOFMEMORY;
1293 if (utf8_length == 1) return E_FAIL;
1295 ret = str_to_uint64((const unsigned char *) utf8_text, utf8_length - 1, MAX_UINT64, value);
1296 heap_free(utf8_text);
1298 return ret;
1301 static HRESULT move_to_element(WS_XML_READER *reader, const char *element_name, WS_XML_STRING *uri)
1303 WS_XML_STRING envelope;
1304 BOOL found = FALSE;
1305 HRESULT ret;
1307 envelope.bytes = (BYTE *) element_name;
1308 envelope.length = strlen(element_name);
1309 envelope.dictionary = NULL;
1310 envelope.id = 0;
1312 ret = WsReadToStartElement(reader, &envelope, uri, &found, NULL);
1313 if (FAILED(ret)) return ret;
1315 return found ? ret : E_FAIL;
1318 static void trim_trailing_slash(LPWSTR uri)
1320 /* Trim trailing slash from URI */
1321 int uri_len = lstrlenW(uri);
1322 if (uri_len > 0 && uri[uri_len - 1] == '/') uri[uri_len - 1] = 0;
1325 static HRESULT ws_element_to_wsdxml_element(WS_XML_READER *reader, IWSDXMLContext *context, WSDXML_ELEMENT *parent_element)
1327 WSDXML_ATTRIBUTE *cur_wsd_attrib = NULL, *new_wsd_attrib = NULL;
1328 const WS_XML_ELEMENT_NODE *element_node = NULL;
1329 WSDXML_ELEMENT *cur_element = parent_element;
1330 const WS_XML_TEXT_NODE *text_node = NULL;
1331 LPWSTR uri = NULL, element_name = NULL;
1332 WS_XML_STRING *ns_string = NULL;
1333 WS_XML_ATTRIBUTE *attrib = NULL;
1334 WSDXML_ELEMENT *element = NULL;
1335 const WS_XML_NODE *node = NULL;
1336 WSDXML_NAME *name = NULL;
1337 WSDXML_TEXT *text = NULL;
1338 HRESULT ret;
1339 int i;
1341 for (;;)
1343 if (cur_element == NULL) break;
1345 ret = WsReadNode(reader, NULL);
1346 if (FAILED(ret)) goto cleanup;
1348 ret = WsGetReaderNode(reader, &node, NULL);
1349 if (FAILED(ret)) goto cleanup;
1351 switch (node->nodeType)
1353 case WS_XML_NODE_TYPE_ELEMENT:
1354 element_node = (const WS_XML_ELEMENT_NODE *) node;
1356 uri = utf8_to_wide(NULL, (const char *) element_node->ns->bytes, element_node->ns->length);
1357 if (uri == NULL) goto outofmemory;
1359 /* Link element_name to uri so they will be freed at the same time */
1360 element_name = utf8_to_wide(uri, (const char *) element_node->localName->bytes,
1361 element_node->localName->length);
1362 if (element_name == NULL) goto outofmemory;
1364 trim_trailing_slash(uri);
1366 ret = IWSDXMLContext_AddNameToNamespace(context, uri, element_name, &name);
1367 if (FAILED(ret)) goto cleanup;
1369 WSDFreeLinkedMemory(uri);
1370 uri = NULL;
1372 ret = WSDXMLBuildAnyForSingleElement(name, NULL, &element);
1373 if (FAILED(ret)) goto cleanup;
1374 WSDXMLAddChild(cur_element, element);
1376 WSDFreeLinkedMemory(name);
1377 name = NULL;
1379 cur_wsd_attrib = NULL;
1381 /* Add attributes */
1382 for (i = 0; i < element_node->attributeCount; i++)
1384 attrib = element_node->attributes[i];
1385 if (attrib->isXmlNs) continue;
1387 new_wsd_attrib = WSDAllocateLinkedMemory(element, sizeof(WSDXML_ATTRIBUTE));
1388 if (new_wsd_attrib == NULL) goto outofmemory;
1390 ns_string = attrib->ns;
1391 if (ns_string->length == 0) ns_string = element_node->ns;
1393 uri = utf8_to_wide(NULL, (const char *) ns_string->bytes, ns_string->length);
1394 if (uri == NULL) goto outofmemory;
1396 trim_trailing_slash(uri);
1398 /* Link element_name to uri so they will be freed at the same time */
1399 element_name = utf8_to_wide(uri, (const char *) attrib->localName->bytes, attrib->localName->length);
1400 if (element_name == NULL) goto outofmemory;
1402 ret = IWSDXMLContext_AddNameToNamespace(context, uri, element_name, &name);
1403 if (FAILED(ret)) goto cleanup;
1405 WSDFreeLinkedMemory(uri);
1406 uri = NULL;
1408 new_wsd_attrib->Value = xml_text_to_wide_string(new_wsd_attrib, attrib->value);
1409 if (new_wsd_attrib->Value == NULL) goto outofmemory;
1411 new_wsd_attrib->Name = name;
1412 new_wsd_attrib->Element = cur_element;
1413 new_wsd_attrib->Next = NULL;
1415 WSDAttachLinkedMemory(new_wsd_attrib, name);
1416 name = NULL;
1418 if (cur_wsd_attrib == NULL)
1419 element->FirstAttribute = new_wsd_attrib;
1420 else
1421 cur_wsd_attrib->Next = new_wsd_attrib;
1423 cur_wsd_attrib = new_wsd_attrib;
1426 cur_element = element;
1427 break;
1429 case WS_XML_NODE_TYPE_TEXT:
1430 text_node = (const WS_XML_TEXT_NODE *) node;
1432 if (cur_element == NULL)
1434 WARN("No parent element open but encountered text element!\n");
1435 continue;
1438 if (cur_element->FirstChild != NULL)
1440 WARN("Text node encountered but parent already has child!\n");
1441 continue;
1444 text = WSDAllocateLinkedMemory(element, sizeof(WSDXML_TEXT));
1445 if (text == NULL) goto outofmemory;
1447 text->Node.Parent = element;
1448 text->Node.Next = NULL;
1449 text->Node.Type = TextType;
1450 text->Text = xml_text_to_wide_string(text, text_node->text);
1452 if (text->Text == NULL)
1454 WARN("Text node returned null string.\n");
1455 WSDFreeLinkedMemory(text);
1456 continue;
1459 cur_element->FirstChild = (WSDXML_NODE *) text;
1460 break;
1462 case WS_XML_NODE_TYPE_END_ELEMENT:
1463 /* Go up a level to the parent element */
1464 cur_element = cur_element->Node.Parent;
1465 break;
1467 default:
1468 break;
1472 return S_OK;
1474 outofmemory:
1475 ret = E_OUTOFMEMORY;
1477 cleanup:
1478 /* Free uri and element_name if applicable */
1479 WSDFreeLinkedMemory(uri);
1480 WSDFreeLinkedMemory(name);
1481 return ret;
1484 static WSDXML_ELEMENT *find_element(WSDXML_ELEMENT *parent, LPCWSTR name, LPCWSTR ns_uri)
1486 WSDXML_ELEMENT *cur = (WSDXML_ELEMENT *) parent->FirstChild;
1488 while (cur != NULL)
1490 if ((lstrcmpW(cur->Name->LocalName, name) == 0) && (lstrcmpW(cur->Name->Space->Uri, ns_uri) == 0))
1491 return cur;
1493 cur = (WSDXML_ELEMENT *) cur->Node.Next;
1496 return NULL;
1499 static void remove_element(WSDXML_ELEMENT *element)
1501 WSDXML_NODE *cur;
1503 if (element == NULL)
1504 return;
1506 if (element->Node.Parent->FirstChild == (WSDXML_NODE *) element)
1507 element->Node.Parent->FirstChild = element->Node.Next;
1508 else
1510 cur = element->Node.Parent->FirstChild;
1512 while (cur != NULL)
1514 if (cur->Next == (WSDXML_NODE *) element)
1516 cur->Next = element->Node.Next;
1517 break;
1520 cur = cur->Next;
1524 WSDDetachLinkedMemory(element);
1525 WSDFreeLinkedMemory(element);
1528 static WSD_NAME_LIST *build_types_list_from_string(IWSDXMLContext *context, LPCWSTR buffer, void *parent)
1530 WSD_NAME_LIST *list = NULL, *cur_list = NULL, *prev_list = NULL;
1531 LPWSTR name_start = NULL, temp_buffer = NULL;
1532 LPCWSTR prefix_start = buffer;
1533 WSDXML_NAMESPACE *ns;
1534 WSDXML_NAME *name;
1535 int buffer_len, i;
1537 if (buffer == NULL)
1538 return NULL;
1540 temp_buffer = duplicate_string(parent, buffer);
1541 if (temp_buffer == NULL) goto cleanup;
1543 buffer_len = lstrlenW(temp_buffer);
1545 list = WSDAllocateLinkedMemory(parent, sizeof(WSD_NAME_LIST));
1546 if (list == NULL) goto cleanup;
1548 ZeroMemory(list, sizeof(WSD_NAME_LIST));
1549 prefix_start = temp_buffer;
1551 for (i = 0; i < buffer_len; i++)
1553 if (temp_buffer[i] == ':')
1555 temp_buffer[i] = 0;
1556 name_start = &temp_buffer[i + 1];
1558 else if ((temp_buffer[i] == ' ') || (i == buffer_len - 1))
1560 WSDXML_NAMESPACE *known_ns;
1562 if (temp_buffer[i] == ' ')
1563 temp_buffer[i] = 0;
1565 if (cur_list == NULL)
1566 cur_list = list;
1567 else
1569 cur_list = WSDAllocateLinkedMemory(parent, sizeof(WSD_NAME_LIST));
1570 if (cur_list == NULL) goto cleanup;
1572 prev_list->Next = cur_list;
1575 name = WSDAllocateLinkedMemory(cur_list, sizeof(WSDXML_NAME));
1576 if (name == NULL) goto cleanup;
1578 ns = WSDAllocateLinkedMemory(cur_list, sizeof(WSDXML_NAMESPACE));
1579 if (ns == NULL) goto cleanup;
1581 ZeroMemory(ns, sizeof(WSDXML_NAMESPACE));
1582 ns->PreferredPrefix = duplicate_string(ns, prefix_start);
1584 known_ns = xml_context_find_namespace_by_prefix(context, ns->PreferredPrefix);
1586 if (known_ns != NULL)
1587 ns->Uri = duplicate_string(ns, known_ns->Uri);
1589 name->Space = ns;
1590 name->LocalName = duplicate_string(name, name_start);
1592 cur_list->Element = name;
1593 prefix_start = &temp_buffer[i + 1];
1594 name_start = NULL;
1598 WSDFreeLinkedMemory(temp_buffer);
1599 return list;
1601 cleanup:
1602 WSDFreeLinkedMemory(list);
1603 WSDFreeLinkedMemory(temp_buffer);
1605 return NULL;
1608 static WSDXML_TYPE *generate_type(LPCWSTR uri, void *parent)
1610 WSDXML_TYPE *type = WSDAllocateLinkedMemory(parent, sizeof(WSDXML_TYPE));
1612 if (type == NULL)
1613 return NULL;
1615 type->Uri = duplicate_string(parent, uri);
1616 type->Table = NULL;
1618 return type;
1621 static BOOL is_duplicate_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id)
1623 struct message_id *msg_id, *msg_id_cursor;
1624 BOOL ret = FALSE;
1625 int len;
1627 EnterCriticalSection(&impl->message_ids_critical_section);
1629 LIST_FOR_EACH_ENTRY_SAFE(msg_id, msg_id_cursor, &impl->message_ids, struct message_id, entry)
1631 if (lstrcmpW(msg_id->id, id) == 0)
1633 ret = TRUE;
1634 goto end;
1638 msg_id = heap_alloc(sizeof(*msg_id));
1639 if (!msg_id) goto end;
1641 len = (lstrlenW(id) + 1) * sizeof(WCHAR);
1642 msg_id->id = heap_alloc(len);
1644 if (!msg_id->id)
1646 heap_free(msg_id);
1647 goto end;
1650 memcpy(msg_id->id, id, len);
1651 list_add_tail(&impl->message_ids, &msg_id->entry);
1653 end:
1654 LeaveCriticalSection(&impl->message_ids_critical_section);
1655 return ret;
1658 HRESULT read_message(IWSDiscoveryPublisherImpl *impl, const char *xml, int xml_length, WSD_SOAP_MESSAGE **out_msg, int *msg_type)
1660 WSDXML_ELEMENT *envelope = NULL, *header_element, *appsequence_element, *body_element;
1661 WS_XML_READER_TEXT_ENCODING encoding;
1662 WS_XML_ELEMENT_NODE *envelope_node;
1663 WSD_SOAP_MESSAGE *soap_msg = NULL;
1664 WS_XML_READER_BUFFER_INPUT input;
1665 WS_XML_ATTRIBUTE *attrib = NULL;
1666 IWSDXMLContext *context = NULL;
1667 WS_XML_STRING *soap_uri = NULL;
1668 const WS_XML_NODE *node;
1669 WS_XML_READER *reader = NULL;
1670 LPCWSTR value = NULL;
1671 LPWSTR uri, prefix;
1672 WS_HEAP *heap = NULL;
1673 HRESULT ret;
1674 int i;
1676 *msg_type = MSGTYPE_UNKNOWN;
1678 ret = WsCreateHeap(16384, 4096, NULL, 0, &heap, NULL);
1679 if (FAILED(ret)) goto cleanup;
1681 ret = WsCreateReader(NULL, 0, &reader, NULL);
1682 if (FAILED(ret)) goto cleanup;
1684 encoding.encoding.encodingType = WS_XML_READER_ENCODING_TYPE_TEXT;
1685 encoding.charSet = WS_CHARSET_AUTO;
1687 input.input.inputType = WS_XML_READER_INPUT_TYPE_BUFFER;
1688 input.encodedData = (char *) xml;
1689 input.encodedDataSize = xml_length;
1691 ret = WsSetInput(reader, (WS_XML_READER_ENCODING *) &encoding, (WS_XML_READER_INPUT *) &input, NULL, 0, NULL);
1692 if (FAILED(ret)) goto cleanup;
1694 soap_uri = populate_xml_string(envelopeNsUri);
1695 if (soap_uri == NULL) goto outofmemory;
1697 ret = move_to_element(reader, "Envelope", soap_uri);
1698 if (FAILED(ret)) goto cleanup;
1700 ret = WsGetReaderNode(reader, &node, NULL);
1701 if (FAILED(ret)) goto cleanup;
1703 if (node->nodeType != WS_XML_NODE_TYPE_ELEMENT)
1705 WARN("Unexpected node type (%d)\n", node->nodeType);
1706 ret = E_FAIL;
1707 goto cleanup;
1710 envelope_node = (WS_XML_ELEMENT_NODE *) node;
1712 ret = WSDXMLCreateContext(&context);
1713 if (FAILED(ret)) goto cleanup;
1715 /* Find XML namespaces from the envelope element's attributes */
1716 for (i = 0; i < envelope_node->attributeCount; i++)
1718 attrib = envelope_node->attributes[i];
1720 if (attrib->isXmlNs)
1722 uri = utf8_to_wide(NULL, (const char *) attrib->ns->bytes, attrib->ns->length);
1723 if (uri == NULL) continue;
1725 trim_trailing_slash(uri);
1727 prefix = utf8_to_wide(uri, (const char *) attrib->localName->bytes, attrib->localName->length);
1729 if (prefix == NULL)
1731 WSDFreeLinkedMemory(uri);
1732 continue;
1735 IWSDXMLContext_AddNamespace(context, uri, prefix, NULL);
1736 WSDFreeLinkedMemory(uri);
1740 /* Create the SOAP message to return to the caller */
1741 soap_msg = WSDAllocateLinkedMemory(NULL, sizeof(WSD_SOAP_MESSAGE));
1742 if (soap_msg == NULL) goto outofmemory;
1744 ZeroMemory(soap_msg, sizeof(WSD_SOAP_MESSAGE));
1746 envelope = WSDAllocateLinkedMemory(soap_msg, sizeof(WSDXML_ELEMENT));
1747 if (envelope == NULL) goto outofmemory;
1749 ZeroMemory(envelope, sizeof(WSDXML_ELEMENT));
1751 ret = ws_element_to_wsdxml_element(reader, context, envelope);
1752 if (FAILED(ret)) goto cleanup;
1754 /* Find the header element */
1755 header_element = find_element(envelope, headerString, envelopeNsUri);
1757 if (header_element == NULL)
1759 WARN("Unable to find header element in received SOAP message\n");
1760 ret = E_FAIL;
1761 goto cleanup;
1764 ret = WSDXMLGetValueFromAny(addressingNsUri, actionString, (WSDXML_ELEMENT *) header_element->FirstChild, &value);
1765 if (FAILED(ret)) goto cleanup;
1766 soap_msg->Header.Action = duplicate_string(soap_msg, value);
1767 if (soap_msg->Header.Action == NULL) goto outofmemory;
1769 ret = WSDXMLGetValueFromAny(addressingNsUri, toString, (WSDXML_ELEMENT *) header_element->FirstChild, &value);
1770 if (FAILED(ret)) goto cleanup;
1771 soap_msg->Header.To = duplicate_string(soap_msg, value);
1772 if (soap_msg->Header.To == NULL) goto outofmemory;
1774 ret = WSDXMLGetValueFromAny(addressingNsUri, messageIdString, (WSDXML_ELEMENT *) header_element->FirstChild, &value);
1775 if (FAILED(ret)) goto cleanup;
1777 /* Detect duplicate messages */
1778 if (is_duplicate_message(impl, value))
1780 ret = E_FAIL;
1781 goto cleanup;
1784 soap_msg->Header.MessageID = duplicate_string(soap_msg, value);
1785 if (soap_msg->Header.MessageID == NULL) goto outofmemory;
1787 /* Look for optional AppSequence element */
1788 appsequence_element = find_element(header_element, appSequenceString, discoveryNsUri);
1790 if (appsequence_element != NULL)
1792 WSDXML_ATTRIBUTE *current_attrib;
1794 soap_msg->Header.AppSequence = WSDAllocateLinkedMemory(soap_msg, sizeof(WSD_APP_SEQUENCE));
1795 if (soap_msg->Header.AppSequence == NULL) goto outofmemory;
1797 ZeroMemory(soap_msg->Header.AppSequence, sizeof(WSD_APP_SEQUENCE));
1799 current_attrib = appsequence_element->FirstAttribute;
1801 while (current_attrib != NULL)
1803 if (lstrcmpW(current_attrib->Name->Space->Uri, discoveryNsUri) != 0)
1805 current_attrib = current_attrib->Next;
1806 continue;
1809 if (lstrcmpW(current_attrib->Name->LocalName, instanceIdString) == 0)
1811 ret = wide_text_to_ulonglong(current_attrib->Value, &soap_msg->Header.AppSequence->InstanceId);
1812 if (FAILED(ret)) goto cleanup;
1814 else if (lstrcmpW(current_attrib->Name->LocalName, messageNumberString) == 0)
1816 ret = wide_text_to_ulonglong(current_attrib->Value, &soap_msg->Header.AppSequence->MessageNumber);
1817 if (FAILED(ret)) goto cleanup;
1819 else if (lstrcmpW(current_attrib->Name->LocalName, sequenceIdString) == 0)
1821 soap_msg->Header.AppSequence->SequenceId = duplicate_string(soap_msg, current_attrib->Value);
1822 if (soap_msg->Header.AppSequence->SequenceId == NULL) goto outofmemory;
1825 current_attrib = current_attrib->Next;
1829 /* Now detach and free known headers to leave the "any" elements */
1830 remove_element(find_element(header_element, actionString, addressingNsUri));
1831 remove_element(find_element(header_element, toString, addressingNsUri));
1832 remove_element(find_element(header_element, messageIdString, addressingNsUri));
1833 remove_element(find_element(header_element, appSequenceString, discoveryNsUri));
1835 soap_msg->Header.AnyHeaders = (WSDXML_ELEMENT *) header_element->FirstChild;
1837 if (soap_msg->Header.AnyHeaders != NULL)
1838 soap_msg->Header.AnyHeaders->Node.Parent = NULL;
1840 /* Find the body element */
1841 body_element = find_element(envelope, bodyString, envelopeNsUri);
1843 if (body_element == NULL)
1845 WARN("Unable to find body element in received SOAP message\n");
1846 ret = E_FAIL;
1847 goto cleanup;
1850 /* Now figure out which message we've been sent */
1851 if (lstrcmpW(soap_msg->Header.Action, actionProbe) == 0)
1853 WSDXML_ELEMENT *probe_element;
1854 WSD_PROBE *probe = NULL;
1856 probe_element = find_element(body_element, probeString, discoveryNsUri);
1857 if (probe_element == NULL) goto cleanup;
1859 probe = WSDAllocateLinkedMemory(soap_msg, sizeof(WSD_PROBE));
1860 if (probe == NULL) goto cleanup;
1862 ZeroMemory(probe, sizeof(WSD_PROBE));
1864 /* Check for the "types" element */
1865 ret = WSDXMLGetValueFromAny(discoveryNsUri, typesString, (WSDXML_ELEMENT *) probe_element->FirstChild, &value);
1867 if (FAILED(ret))
1869 WARN("Unable to find Types element in received Probe message\n");
1870 goto cleanup;
1873 probe->Types = build_types_list_from_string(context, value, probe);
1875 /* Now detach and free known headers to leave the "any" elements */
1876 remove_element(find_element(probe_element, typesString, discoveryNsUri));
1877 remove_element(find_element(probe_element, scopesString, discoveryNsUri));
1879 probe->Any = (WSDXML_ELEMENT *) probe_element->FirstChild;
1881 if (probe->Any != NULL)
1882 probe->Any->Node.Parent = NULL;
1884 soap_msg->Body = probe;
1885 soap_msg->BodyType = generate_type(actionProbe, soap_msg);
1886 if (soap_msg->BodyType == NULL) goto cleanup;
1888 *out_msg = soap_msg;
1889 soap_msg = NULL; /* caller will clean this up */
1890 *msg_type = MSGTYPE_PROBE;
1893 goto cleanup;
1895 outofmemory:
1896 ret = E_OUTOFMEMORY;
1898 cleanup:
1899 free_xml_string(soap_uri);
1900 WSDFreeLinkedMemory(soap_msg);
1901 if (context != NULL) IWSDXMLContext_Release(context);
1902 if (reader != NULL) WsFreeReader(reader);
1903 if (heap != NULL) WsFreeHeap(heap);
1905 return ret;