wsdapi: Build and write Scopes and XAddrs lists for Hello message.
[wine.git] / dlls / wsdapi / soap.c
blob9ba934e99d0c4ddd7dc4adb93ca5a41e3090829e
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[] = {
36 'u','r','n',':',
37 's','c','h','e','m','a','s','-','x','m','l','s','o','a','p','-','o','r','g',':',
38 'w','s',':','2','0','0','5',':','0','4',':',
39 'd','i','s','c','o','v','e','r','y', 0 };
41 static const WCHAR actionHello[] = {
42 'h','t','t','p',':','/','/',
43 's','c','h','e','m','a','s','.','x','m','l','s','o','a','p','.','o','r','g','/',
44 'w','s','/','2','0','0','5','/','0','4','/',
45 'd','i','s','c','o','v','e','r','y','/',
46 'H','e','l','l','o', 0 };
48 static const WCHAR addressingNsUri[] = {
49 'h','t','t','p',':','/','/',
50 's','c','h','e','m','a','s','.','x','m','l','s','o','a','p','.','o','r','g','/',
51 'w','s','/','2','0','0','4','/','0','8','/','a','d','d','r','e','s','s','i','n','g', 0 };
53 static const WCHAR discoveryNsUri[] = {
54 'h','t','t','p',':','/','/',
55 's','c','h','e','m','a','s','.','x','m','l','s','o','a','p','.','o','r','g','/',
56 'w','s','/','2','0','0','5','/','0','4','/','d','i','s','c','o','v','e','r','y', 0 };
58 static const WCHAR envelopeNsUri[] = {
59 'h','t','t','p',':','/','/',
60 'w','w','w','.','w','3','.','o','r','g','/',
61 '2','0','0','3','/','0','5','/','s','o','a','p','-','e','n','v','e','l','o','p','e', 0 };
63 static const WCHAR addressingPrefix[] = { 'w','s','a', 0 };
64 static const WCHAR discoveryPrefix[] = { 'w','s','d', 0 };
65 static const WCHAR envelopePrefix[] = { 's','o','a','p', 0 };
66 static const WCHAR headerString[] = { 'H','e','a','d','e','r', 0 };
67 static const WCHAR actionString[] = { 'A','c','t','i','o','n', 0 };
68 static const WCHAR messageIdString[] = { 'M','e','s','s','a','g','e','I','D', 0 };
69 static const WCHAR toString[] = { 'T','o', 0 };
70 static const WCHAR relatesToString[] = { 'R','e','l','a','t','e','s','T','o', 0 };
71 static const WCHAR appSequenceString[] = { 'A','p','p','S','e','q','u','e','n','c','e', 0 };
72 static const WCHAR instanceIdString[] = { 'I','n','s','t','a','n','c','e','I','d', 0 };
73 static const WCHAR messageNumberString[] = { 'M','e','s','s','a','g','e','N','u','m','b','e','r', 0 };
74 static const WCHAR sequenceIdString[] = { 'S','e','q','u','e','n','c','e','I','d', 0 };
75 static const WCHAR emptyString[] = { 0 };
76 static const WCHAR bodyString[] = { 'B','o','d','y', 0 };
77 static const WCHAR helloString[] = { 'H','e','l','l','o', 0 };
78 static const WCHAR endpointReferenceString[] = { 'E','n','d','p','o','i','n','t','R','e','f','e','r','e','n','c','e', 0 };
79 static const WCHAR addressString[] = { 'A','d','d','r','e','s','s', 0 };
80 static const WCHAR typesString[] = { 'T','y','p','e','s', 0 };
81 static const WCHAR scopesString[] = { 'S','c','o','p','e','s', 0 };
82 static const WCHAR xAddrsString[] = { 'X','A','d','d','r','s', 0 };
84 struct discovered_namespace
86 struct list entry;
87 LPCWSTR prefix;
88 LPCWSTR uri;
91 static char *wide_to_utf8(LPCWSTR wide_string, int *length)
93 char *new_string = NULL;
95 if (wide_string == NULL)
96 return NULL;
98 *length = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, NULL, 0, NULL, NULL);
100 if (*length < 0)
101 return NULL;
103 new_string = heap_alloc(*length);
104 WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, new_string, *length, NULL, NULL);
106 return new_string;
109 static WS_XML_STRING *populate_xml_string(LPCWSTR str)
111 WS_XML_STRING *xml = heap_alloc_zero(sizeof(WS_XML_STRING));
112 int utf8Length;
114 if (xml == NULL)
115 return NULL;
117 xml->bytes = (BYTE *)wide_to_utf8(str, &utf8Length);
119 if (xml->bytes == NULL)
121 heap_free(xml);
122 return NULL;
125 xml->dictionary = NULL;
126 xml->id = 0;
127 xml->length = (xml->bytes == NULL) ? 0 : (utf8Length - 1);
129 return xml;
132 static inline void free_xml_string(WS_XML_STRING *value)
134 if (value == NULL)
135 return;
137 heap_free(value->bytes);
139 heap_free(value);
142 static HRESULT write_xml_attribute(WSDXML_ATTRIBUTE *attribute, WS_XML_WRITER *writer)
144 WS_XML_STRING *local_name = NULL, *element_ns = NULL, *ns_prefix = NULL;
145 WS_XML_UTF16_TEXT utf16_text;
146 HRESULT ret = E_OUTOFMEMORY;
147 int text_len;
149 if (attribute == NULL)
150 return S_OK;
152 /* Start the attribute */
153 local_name = populate_xml_string(attribute->Name->LocalName);
154 if (local_name == NULL) goto cleanup;
156 if (attribute->Name->Space == NULL)
158 element_ns = populate_xml_string(emptyString);
159 if (element_ns == NULL) goto cleanup;
161 ns_prefix = NULL;
163 else
165 element_ns = populate_xml_string(attribute->Name->Space->Uri);
166 if (element_ns == NULL) goto cleanup;
168 ns_prefix = populate_xml_string(attribute->Name->Space->PreferredPrefix);
169 if (ns_prefix == NULL) goto cleanup;
172 ret = WsWriteStartAttribute(writer, ns_prefix, local_name, element_ns, FALSE, NULL);
173 if (FAILED(ret)) goto cleanup;
175 text_len = lstrlenW(attribute->Value);
177 utf16_text.text.textType = WS_XML_TEXT_TYPE_UTF16;
178 utf16_text.bytes = (BYTE *)attribute->Value;
179 utf16_text.byteCount = min(WSD_MAX_TEXT_LENGTH, text_len) * sizeof(WCHAR);
181 ret = WsWriteText(writer, (WS_XML_TEXT *)&utf16_text, NULL);
182 if (FAILED(ret)) goto cleanup;
184 ret = WsWriteEndAttribute(writer, NULL);
185 if (FAILED(ret)) goto cleanup;
187 cleanup:
188 free_xml_string(local_name);
189 free_xml_string(element_ns);
190 free_xml_string(ns_prefix);
192 return ret;
195 static HRESULT write_xml_element(WSDXML_ELEMENT *element, WS_XML_WRITER *writer)
197 WS_XML_STRING *local_name = NULL, *element_ns = NULL, *ns_prefix = NULL;
198 WSDXML_ATTRIBUTE *current_attribute;
199 WS_XML_UTF16_TEXT utf16_text;
200 WSDXML_NODE *current_child;
201 WSDXML_TEXT *node_as_text;
202 int text_len;
203 HRESULT ret = E_OUTOFMEMORY;
205 if (element == NULL)
206 return S_OK;
208 /* Start the element */
209 local_name = populate_xml_string(element->Name->LocalName);
210 if (local_name == NULL) goto cleanup;
212 element_ns = populate_xml_string(element->Name->Space->Uri);
213 if (element_ns == NULL) goto cleanup;
215 ns_prefix = populate_xml_string(element->Name->Space->PreferredPrefix);
216 if (ns_prefix == NULL) goto cleanup;
218 ret = WsWriteStartElement(writer, ns_prefix, local_name, element_ns, NULL);
219 if (FAILED(ret)) goto cleanup;
221 /* Write attributes */
222 current_attribute = element->FirstAttribute;
224 while (current_attribute != NULL)
226 ret = write_xml_attribute(current_attribute, writer);
227 if (FAILED(ret)) goto cleanup;
228 current_attribute = current_attribute->Next;
231 /* Write child elements */
232 current_child = element->FirstChild;
234 while (current_child != NULL)
236 if (current_child->Type == ElementType)
238 ret = write_xml_element((WSDXML_ELEMENT *)current_child, writer);
239 if (FAILED(ret)) goto cleanup;
241 else if (current_child->Type == TextType)
243 node_as_text = (WSDXML_TEXT *)current_child;
244 text_len = lstrlenW(node_as_text->Text);
246 utf16_text.text.textType = WS_XML_TEXT_TYPE_UTF16;
247 utf16_text.byteCount = min(WSD_MAX_TEXT_LENGTH, text_len) * sizeof(WCHAR);
248 utf16_text.bytes = (BYTE *)node_as_text->Text;
250 ret = WsWriteText(writer, (WS_XML_TEXT *)&utf16_text, NULL);
251 if (FAILED(ret)) goto cleanup;
254 current_child = current_child->Next;
257 /* End the element */
258 ret = WsWriteEndElement(writer, NULL);
260 cleanup:
261 free_xml_string(local_name);
262 free_xml_string(element_ns);
263 free_xml_string(ns_prefix);
265 return ret;
268 static HRESULT add_child_element(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri,
269 LPCWSTR name, LPCWSTR text, WSDXML_ELEMENT **out)
271 WSDXML_ELEMENT *element_obj;
272 WSDXML_NAME *name_obj;
273 HRESULT ret;
275 ret = IWSDXMLContext_AddNameToNamespace(xml_context, ns_uri, name, &name_obj);
276 if (FAILED(ret)) return ret;
278 ret = WSDXMLBuildAnyForSingleElement(name_obj, text, &element_obj);
279 WSDFreeLinkedMemory(name_obj);
281 if (FAILED(ret)) return ret;
283 /* Add the element as a child - this will link the element's memory allocation to the parent's */
284 ret = WSDXMLAddChild(parent, element_obj);
286 if (FAILED(ret))
288 WSDFreeLinkedMemory(element_obj);
289 return ret;
292 if (out != NULL) *out = element_obj;
293 return ret;
296 HRESULT register_namespaces(IWSDXMLContext *xml_context)
298 HRESULT ret;
300 ret = IWSDXMLContext_AddNamespace(xml_context, addressingNsUri, addressingPrefix, NULL);
301 if (FAILED(ret)) return ret;
303 ret = IWSDXMLContext_AddNamespace(xml_context, discoveryNsUri, discoveryPrefix, NULL);
304 if (FAILED(ret)) return ret;
306 return IWSDXMLContext_AddNamespace(xml_context, envelopeNsUri, envelopePrefix, NULL);
309 static BOOL create_guid(LPWSTR buffer)
311 const WCHAR formatString[] = { 'u','r','n',':','u','u','i','d',':','%','s', 0 };
313 WCHAR* uuidString = NULL;
314 UUID uuid;
316 if (UuidCreate(&uuid) != RPC_S_OK)
317 return FALSE;
319 UuidToStringW(&uuid, (RPC_WSTR*)&uuidString);
321 if (uuidString == NULL)
322 return FALSE;
324 wsprintfW(buffer, formatString, uuidString);
325 RpcStringFreeW((RPC_WSTR*)&uuidString);
327 return TRUE;
330 static void populate_soap_header(WSD_SOAP_HEADER *header, LPCWSTR to, LPCWSTR action, LPCWSTR message_id,
331 WSD_APP_SEQUENCE *sequence, const WSDXML_ELEMENT *any_headers)
333 ZeroMemory(header, sizeof(WSD_SOAP_HEADER));
335 header->To = to;
336 header->Action = action;
337 header->MessageID = message_id;
338 header->AppSequence = sequence;
339 header->AnyHeaders = (WSDXML_ELEMENT *)any_headers;
341 /* TODO: Implement RelatesTo, ReplyTo, From, FaultTo */
344 #define MAX_ULONGLONG_STRING_SIZE 25
346 static LPWSTR ulonglong_to_string(void *parent, ULONGLONG value)
348 WCHAR formatString[] = { '%','I','6','4','u', 0 };
349 LPWSTR ret;
351 ret = WSDAllocateLinkedMemory(parent, MAX_ULONGLONG_STRING_SIZE * sizeof(WCHAR));
353 if (ret == NULL)
354 return NULL;
356 wsprintfW(ret, formatString, value);
357 return ret;
360 static WSDXML_ATTRIBUTE *add_attribute(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri, LPCWSTR name)
362 WSDXML_ATTRIBUTE *attribute, *cur_attrib;
363 WSDXML_NAME *name_obj = NULL;
365 if (ns_uri == NULL)
367 name_obj = WSDAllocateLinkedMemory(NULL, sizeof(WSDXML_NAME));
368 name_obj->LocalName = duplicate_string(name_obj, name);
369 name_obj->Space = NULL;
371 else
373 if (FAILED(IWSDXMLContext_AddNameToNamespace(xml_context, ns_uri, name, &name_obj)))
374 return NULL;
377 attribute = WSDAllocateLinkedMemory(parent, sizeof(WSDXML_ATTRIBUTE));
379 if (attribute == NULL)
381 WSDFreeLinkedMemory(name_obj);
382 return NULL;
385 attribute->Element = parent;
386 attribute->Name = name_obj;
387 attribute->Next = NULL;
388 attribute->Value = NULL;
390 if (name_obj != NULL)
391 WSDAttachLinkedMemory(attribute, name_obj);
393 if (parent->FirstAttribute == NULL)
395 /* Make this the first attribute of the parent */
396 parent->FirstAttribute = attribute;
398 else
400 /* Find the last attribute and add this as the next one */
401 cur_attrib = parent->FirstAttribute;
403 while (cur_attrib->Next != NULL)
405 cur_attrib = cur_attrib->Next;
408 cur_attrib->Next = attribute;
411 return attribute;
414 static void remove_attribute(WSDXML_ELEMENT *parent, WSDXML_ATTRIBUTE *attribute)
416 WSDXML_ATTRIBUTE *cur_attrib;
418 /* Find the last attribute and add this as the next one */
419 cur_attrib = parent->FirstAttribute;
421 if (cur_attrib == attribute)
422 parent->FirstAttribute = cur_attrib->Next;
423 else
425 while (cur_attrib != NULL)
427 /* Is our attribute the next attribute? */
428 if (cur_attrib->Next == attribute)
430 /* Remove it from the list */
431 cur_attrib->Next = attribute->Next;
432 break;
435 cur_attrib = cur_attrib->Next;
439 WSDFreeLinkedMemory(attribute);
442 static HRESULT add_ulonglong_attribute(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri, LPCWSTR name,
443 ULONGLONG value)
445 WSDXML_ATTRIBUTE *attribute = add_attribute(xml_context, parent, ns_uri, name);
447 if (attribute == NULL)
448 return E_FAIL;
450 attribute->Value = ulonglong_to_string(attribute, value);
452 if (attribute->Value == NULL)
454 remove_attribute(parent, attribute);
455 return E_FAIL;
458 return S_OK;
461 static HRESULT add_string_attribute(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri, LPCWSTR name,
462 LPCWSTR value)
464 WSDXML_ATTRIBUTE *attribute = add_attribute(xml_context, parent, ns_uri, name);
466 if (attribute == NULL)
467 return E_FAIL;
469 attribute->Value = duplicate_string(attribute, value);
471 if (attribute->Value == NULL)
473 remove_attribute(parent, attribute);
474 return E_FAIL;
477 return S_OK;
480 static BOOL add_discovered_namespace(struct list *namespaces, WSDXML_NAMESPACE *discovered_ns)
482 struct discovered_namespace *ns;
484 LIST_FOR_EACH_ENTRY(ns, namespaces, struct discovered_namespace, entry)
486 if (lstrcmpW(ns->uri, discovered_ns->Uri) == 0)
487 return TRUE; /* Already added */
490 ns = WSDAllocateLinkedMemory(namespaces, sizeof(struct discovered_namespace));
492 if (ns == NULL)
493 return FALSE;
495 ns->prefix = duplicate_string(ns, discovered_ns->PreferredPrefix);
496 ns->uri = duplicate_string(ns, discovered_ns->Uri);
498 if ((ns->prefix == NULL) || (ns->uri == NULL))
499 return FALSE;
501 list_add_tail(namespaces, &ns->entry);
502 return TRUE;
505 static HRESULT build_types_list(LPWSTR buffer, size_t buffer_size, const WSD_NAME_LIST *list, struct list *namespaces)
507 WCHAR format_string[] = { '%', 's', ':', '%', 's', 0 };
508 LPWSTR current_buf_pos = buffer;
509 size_t memory_needed = 0;
510 const WSD_NAME_LIST *cur = list;
514 /* Calculate space needed, including NULL character, colon and potential trailing space */
515 memory_needed = sizeof(WCHAR) * (lstrlenW(cur->Element->LocalName) +
516 lstrlenW(cur->Element->Space->PreferredPrefix) + 3);
518 if (current_buf_pos + memory_needed > buffer + buffer_size)
519 return E_INVALIDARG;
521 if (cur != list)
522 *current_buf_pos++ = ' ';
524 current_buf_pos += wsprintfW(current_buf_pos, format_string, cur->Element->Space->PreferredPrefix,
525 cur->Element->LocalName);
527 /* Record the namespace in the discovered namespaces list */
528 if (!add_discovered_namespace(namespaces, cur->Element->Space))
529 return E_FAIL;
531 cur = cur->Next;
532 } while (cur != NULL);
534 return S_OK;
537 static HRESULT build_uri_list(LPWSTR buffer, size_t buffer_size, const WSD_URI_LIST *list)
539 size_t memory_needed = 0, string_len = 0;
540 const WSD_URI_LIST *cur = list;
541 LPWSTR cur_buf_pos = buffer;
545 /* Calculate space needed, including trailing space */
546 string_len = lstrlenW(cur->Element);
547 memory_needed = (string_len + 1) * sizeof(WCHAR);
549 if (cur_buf_pos + memory_needed > buffer + buffer_size)
550 return E_INVALIDARG;
552 if (cur != list)
553 *cur_buf_pos++ = ' ';
555 memcpy(cur_buf_pos, cur->Element, memory_needed);
556 cur_buf_pos += string_len;
558 cur = cur->Next;
559 } while (cur != NULL);
561 return S_OK;
564 static HRESULT duplicate_element(WSDXML_ELEMENT *parent, const WSDXML_ELEMENT *node, struct list *namespaces)
566 WSDXML_ATTRIBUTE *cur_attribute, *new_attribute, *last_attribute = NULL;
567 WSDXML_ELEMENT *new_element;
568 WSDXML_TEXT *text_node;
569 WSDXML_NODE *cur_node;
570 HRESULT ret;
572 /* First record the namespace in the discovered namespaces list */
573 if (!add_discovered_namespace(namespaces, node->Name->Space))
574 return E_FAIL;
576 ret = WSDXMLBuildAnyForSingleElement(node->Name, NULL, &new_element);
577 if (FAILED(ret)) return ret;
579 /* Duplicate the nodes */
580 cur_node = node->FirstChild;
582 while (cur_node != NULL)
584 if (cur_node->Type == ElementType)
586 ret = duplicate_element(new_element, (WSDXML_ELEMENT *)cur_node, namespaces);
587 if (FAILED(ret)) goto cleanup;
589 else if (cur_node->Type == TextType)
591 text_node = WSDAllocateLinkedMemory(new_element, sizeof(WSDXML_TEXT));
592 if (text_node == NULL) goto failed;
594 text_node->Node.Parent = NULL;
595 text_node->Node.Next = NULL;
596 text_node->Node.Type = TextType;
597 text_node->Text = duplicate_string(text_node, ((WSDXML_TEXT *)cur_node)->Text);
599 if (text_node->Text == NULL) goto failed;
601 ret = WSDXMLAddChild(new_element, (WSDXML_ELEMENT *)text_node);
602 if (FAILED(ret)) goto cleanup;
605 cur_node = cur_node->Next;
608 /* Duplicate the attributes */
609 cur_attribute = node->FirstAttribute;
611 while (cur_attribute != NULL)
613 if ((cur_attribute->Name->Space != NULL) && (!add_discovered_namespace(namespaces, cur_attribute->Name->Space)))
614 goto failed;
616 new_attribute = WSDAllocateLinkedMemory(new_element, sizeof(WSDXML_ATTRIBUTE));
617 if (new_attribute == NULL) goto failed;
619 new_attribute->Element = new_element;
620 new_attribute->Name = duplicate_name(new_attribute, cur_attribute->Name);
621 new_attribute->Value = duplicate_string(new_attribute, cur_attribute->Value);
622 new_attribute->Next = NULL;
624 if ((new_attribute->Name == NULL) || (new_attribute->Value == NULL)) goto failed;
626 if (last_attribute == NULL)
627 new_element->FirstAttribute = new_attribute;
628 else
629 last_attribute->Next = new_attribute;
631 last_attribute = new_attribute;
632 cur_attribute = cur_attribute->Next;
635 ret = WSDXMLAddChild(parent, new_element);
636 if (FAILED(ret)) goto cleanup;
638 return ret;
640 failed:
641 ret = E_FAIL;
643 cleanup:
644 WSDXMLCleanupElement(new_element);
645 return ret;
648 static HRESULT create_soap_header_xml_elements(IWSDXMLContext *xml_context, WSD_SOAP_HEADER *header,
649 struct list *discovered_namespaces, WSDXML_ELEMENT **out_element)
651 WSDXML_ELEMENT *header_element = NULL, *app_sequence_element = NULL, *temp_element;
652 WSDXML_NAME *header_name = NULL;
653 HRESULT ret;
655 /* <s:Header> */
656 ret = IWSDXMLContext_AddNameToNamespace(xml_context, envelopeNsUri, headerString, &header_name);
657 if (FAILED(ret)) goto cleanup;
659 ret = WSDXMLBuildAnyForSingleElement(header_name, NULL, &header_element);
660 if (FAILED(ret)) goto cleanup;
662 WSDFreeLinkedMemory(header_name);
664 /* <a:Action> */
665 ret = add_child_element(xml_context, header_element, addressingNsUri, actionString, header->Action, &temp_element);
666 if (FAILED(ret)) goto cleanup;
668 /* <a:MessageId> */
669 ret = add_child_element(xml_context, header_element, addressingNsUri, messageIdString, header->MessageID, &temp_element);
670 if (FAILED(ret)) goto cleanup;
672 /* <a:To> */
673 ret = add_child_element(xml_context, header_element, addressingNsUri, toString, header->To, &temp_element);
674 if (FAILED(ret)) goto cleanup;
676 /* <a:RelatesTo> */
677 if (header->RelatesTo.MessageID != NULL)
679 ret = add_child_element(xml_context, header_element, addressingNsUri, relatesToString,
680 header->RelatesTo.MessageID, &temp_element);
681 if (FAILED(ret)) goto cleanup;
684 /* <d:AppSequence> */
685 ret = add_child_element(xml_context, header_element, discoveryNsUri, appSequenceString, emptyString, &app_sequence_element);
686 if (FAILED(ret)) goto cleanup;
688 /* InstanceId attribute */
689 ret = add_ulonglong_attribute(xml_context, app_sequence_element, NULL, instanceIdString, min(UINT_MAX,
690 header->AppSequence->InstanceId));
691 if (FAILED(ret)) goto cleanup;
693 /* SequenceID attribute */
694 if (header->AppSequence->SequenceId != NULL)
696 ret = add_string_attribute(xml_context, app_sequence_element, NULL, sequenceIdString, header->AppSequence->SequenceId);
697 if (FAILED(ret)) goto cleanup;
700 /* MessageNumber attribute */
701 ret = add_ulonglong_attribute(xml_context, app_sequence_element, NULL, messageNumberString, min(UINT_MAX,
702 header->AppSequence->MessageNumber));
703 if (FAILED(ret)) goto cleanup;
705 /* </d:AppSequence> */
707 /* Write any headers */
708 if (header->AnyHeaders != NULL)
710 ret = duplicate_element(header_element, header->AnyHeaders, discovered_namespaces);
711 if (FAILED(ret)) goto cleanup;
714 /* </s:Header> */
716 *out_element = header_element;
717 return ret;
719 cleanup:
720 if (header_name != NULL) WSDFreeLinkedMemory(header_name);
721 WSDXMLCleanupElement(header_element);
723 return ret;
726 static HRESULT create_soap_envelope(IWSDXMLContext *xml_context, WSD_SOAP_HEADER *header, WSDXML_ELEMENT *body_element,
727 WS_HEAP **heap, char **output_xml, ULONG *xml_length, struct list *discovered_namespaces)
729 WS_XML_STRING *actual_envelope_prefix = NULL, *envelope_uri_xmlstr = NULL, *tmp_prefix = NULL, *tmp_uri = NULL;
730 WSDXML_NAMESPACE *addressing_ns = NULL, *discovery_ns = NULL, *envelope_ns = NULL;
731 WSDXML_ELEMENT *header_element = NULL;
732 struct discovered_namespace *ns;
733 WS_XML_BUFFER *buffer = NULL;
734 WS_XML_WRITER *writer = NULL;
735 WS_XML_STRING envelope;
736 HRESULT ret = E_OUTOFMEMORY;
737 static BYTE envelopeString[] = "Envelope";
739 /* Create the necessary XML prefixes */
740 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, addressingNsUri, addressingPrefix, &addressing_ns))) goto cleanup;
741 if (!add_discovered_namespace(discovered_namespaces, addressing_ns)) goto cleanup;
743 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, discoveryNsUri, discoveryPrefix, &discovery_ns))) goto cleanup;
744 if (!add_discovered_namespace(discovered_namespaces, discovery_ns)) goto cleanup;
746 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, envelopeNsUri, envelopePrefix, &envelope_ns))) goto cleanup;
747 if (!add_discovered_namespace(discovered_namespaces, envelope_ns)) goto cleanup;
749 envelope.bytes = envelopeString;
750 envelope.length = sizeof(envelopeString) - 1;
751 envelope.dictionary = NULL;
752 envelope.id = 0;
754 actual_envelope_prefix = populate_xml_string(envelope_ns->PreferredPrefix);
755 envelope_uri_xmlstr = populate_xml_string(envelope_ns->Uri);
757 if ((actual_envelope_prefix == NULL) || (envelope_uri_xmlstr == NULL)) goto cleanup;
759 /* Now try to create the appropriate WebServices buffers, etc */
760 ret = WsCreateHeap(16384, 4096, NULL, 0, heap, NULL);
761 if (FAILED(ret)) goto cleanup;
763 ret = WsCreateXmlBuffer(*heap, NULL, 0, &buffer, NULL);
764 if (FAILED(ret)) goto cleanup;
766 ret = WsCreateWriter(NULL, 0, &writer, NULL);
767 if (FAILED(ret)) goto cleanup;
769 ret = WsSetOutputToBuffer(writer, buffer, NULL, 0, NULL);
770 if (FAILED(ret)) goto cleanup;
772 /* Create the header XML elements */
773 ret = create_soap_header_xml_elements(xml_context, header, discovered_namespaces, &header_element);
774 if (FAILED(ret)) goto cleanup;
776 /* <s:Envelope> */
777 ret = WsWriteStartElement(writer, actual_envelope_prefix, &envelope, envelope_uri_xmlstr, NULL);
778 if (FAILED(ret)) goto cleanup;
780 LIST_FOR_EACH_ENTRY(ns, discovered_namespaces, struct discovered_namespace, entry)
782 tmp_prefix = populate_xml_string(ns->prefix);
783 tmp_uri = populate_xml_string(ns->uri);
785 if ((tmp_prefix == NULL) || (tmp_uri == NULL)) goto cleanup;
787 ret = WsWriteXmlnsAttribute(writer, tmp_prefix, tmp_uri, FALSE, NULL);
788 if (FAILED(ret)) goto cleanup;
790 free_xml_string(tmp_prefix);
791 free_xml_string(tmp_uri);
794 tmp_prefix = NULL;
795 tmp_uri = NULL;
797 /* Write the header */
798 ret = write_xml_element(header_element, writer);
799 if (FAILED(ret)) goto cleanup;
801 /* Write the body */
802 ret = write_xml_element(body_element, writer);
803 if (FAILED(ret)) goto cleanup;
805 ret = WsWriteEndElement(writer, NULL);
806 if (FAILED(ret)) goto cleanup;
808 /* </s:Envelope> */
810 /* Generate the bytes of the document */
811 ret = WsWriteXmlBufferToBytes(writer, buffer, NULL, NULL, 0, *heap, (void**)output_xml, xml_length, NULL);
812 if (FAILED(ret)) goto cleanup;
814 cleanup:
815 WSDFreeLinkedMemory(addressing_ns);
816 WSDFreeLinkedMemory(discovery_ns);
817 WSDFreeLinkedMemory(envelope_ns);
819 WSDXMLCleanupElement(header_element);
821 free_xml_string(actual_envelope_prefix);
822 free_xml_string(envelope_uri_xmlstr);
824 if (writer != NULL)
825 WsFreeWriter(writer);
827 /* Don't free the heap unless the operation has failed */
828 if ((FAILED(ret)) && (*heap != NULL))
830 WsFreeHeap(*heap);
831 *heap = NULL;
834 return ret;
837 static HRESULT write_and_send_message(IWSDiscoveryPublisherImpl *impl, WSD_SOAP_HEADER *header, WSDXML_ELEMENT *body_element,
838 struct list *discovered_namespaces, IWSDUdpAddress *remote_address, int max_initial_delay)
840 static const char xml_header[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
841 ULONG xml_length = 0, xml_header_len = sizeof(xml_header) - 1;
842 WS_HEAP *heap = NULL;
843 char *xml = NULL;
844 char *full_xml;
845 HRESULT ret;
847 ret = create_soap_envelope(impl->xmlContext, header, body_element, &heap, &xml, &xml_length, discovered_namespaces);
848 if (ret != S_OK) return ret;
850 /* Prefix the XML header */
851 full_xml = heap_alloc(xml_length + xml_header_len + 1);
853 if (full_xml == NULL)
855 WsFreeHeap(heap);
856 return E_OUTOFMEMORY;
859 memcpy(full_xml, xml_header, xml_header_len);
860 memcpy(full_xml + xml_header_len, xml, xml_length);
861 full_xml[xml_length + xml_header_len] = 0;
863 if (remote_address == NULL)
865 /* Send the message via UDP multicast */
866 ret = send_udp_multicast(impl, full_xml, xml_length + xml_header_len + 1, max_initial_delay) ? S_OK : E_FAIL;
868 else
870 /* TODO: Send the message via UDP unicast */
871 FIXME("TODO: Send the message via UDP unicast\n");
874 heap_free(full_xml);
875 WsFreeHeap(heap);
877 return ret;
880 HRESULT send_hello_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id, ULONGLONG metadata_ver, ULONGLONG instance_id,
881 ULONGLONG msg_num, LPCWSTR session_id, const WSD_NAME_LIST *types_list, const WSD_URI_LIST *scopes_list,
882 const WSD_URI_LIST *xaddrs_list, const WSDXML_ELEMENT *hdr_any, const WSDXML_ELEMENT *ref_param_any,
883 const WSDXML_ELEMENT *endpoint_ref_any, const WSDXML_ELEMENT *any)
885 WSDXML_ELEMENT *body_element = NULL, *hello_element, *endpoint_reference_element;
886 struct list *discoveredNamespaces = NULL;
887 WSDXML_NAME *body_name = NULL;
888 WSD_SOAP_HEADER soapHeader;
889 WSD_APP_SEQUENCE sequence;
890 WCHAR message_id[64];
891 HRESULT ret = E_OUTOFMEMORY;
892 LPWSTR buffer;
894 sequence.InstanceId = instance_id;
895 sequence.MessageNumber = msg_num;
896 sequence.SequenceId = session_id;
898 if (!create_guid(message_id)) goto failed;
900 discoveredNamespaces = WSDAllocateLinkedMemory(NULL, sizeof(struct list));
901 if (!discoveredNamespaces) goto failed;
903 list_init(discoveredNamespaces);
905 populate_soap_header(&soapHeader, discoveryTo, actionHello, message_id, &sequence, hdr_any);
907 ret = IWSDXMLContext_AddNameToNamespace(impl->xmlContext, envelopeNsUri, bodyString, &body_name);
908 if (FAILED(ret)) goto cleanup;
910 /* <soap:Body>, <wsd:Hello> */
911 ret = WSDXMLBuildAnyForSingleElement(body_name, NULL, &body_element);
912 if (FAILED(ret)) goto cleanup;
914 ret = add_child_element(impl->xmlContext, body_element, discoveryNsUri, helloString, NULL, &hello_element);
915 if (FAILED(ret)) goto cleanup;
917 /* <wsa:EndpointReference>, <wsa:Address> */
918 ret = add_child_element(impl->xmlContext, hello_element, addressingNsUri, endpointReferenceString, NULL,
919 &endpoint_reference_element);
920 if (FAILED(ret)) goto cleanup;
922 ret = add_child_element(impl->xmlContext, endpoint_reference_element, addressingNsUri, addressString, id, NULL);
923 if (FAILED(ret)) goto cleanup;
925 /* Write any endpoint reference headers */
926 if (endpoint_ref_any != NULL)
928 ret = duplicate_element(endpoint_reference_element, endpoint_ref_any, discoveredNamespaces);
929 if (FAILED(ret)) goto cleanup;
932 /* <wsd:Types> */
933 if (types_list != NULL)
935 buffer = WSDAllocateLinkedMemory(hello_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
936 if (buffer == NULL) goto failed;
938 ret = build_types_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), types_list, discoveredNamespaces);
939 if (FAILED(ret)) goto cleanup;
941 ret = add_child_element(impl->xmlContext, hello_element, discoveryNsUri, typesString, buffer, NULL);
942 if (FAILED(ret)) goto cleanup;
945 /* <wsd:Scopes> */
946 if (scopes_list != NULL)
948 buffer = WSDAllocateLinkedMemory(hello_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
949 if (buffer == NULL) goto failed;
951 ret = build_uri_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), scopes_list);
952 if (FAILED(ret)) goto cleanup;
954 ret = add_child_element(impl->xmlContext, hello_element, discoveryNsUri, scopesString, buffer, NULL);
955 if (FAILED(ret)) goto cleanup;
958 /* <wsd:XAddrs> */
959 if (xaddrs_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), xaddrs_list);
965 if (FAILED(ret)) goto cleanup;
967 ret = add_child_element(impl->xmlContext, hello_element, discoveryNsUri, xAddrsString, buffer, NULL);
968 if (FAILED(ret)) goto cleanup;
971 /* Write any body elements */
972 if (any != NULL)
974 ret = duplicate_element(hello_element, any, discoveredNamespaces);
975 if (FAILED(ret)) goto cleanup;
978 /* Write and send the message */
979 ret = write_and_send_message(impl, &soapHeader, body_element, discoveredNamespaces, NULL, APP_MAX_DELAY);
980 goto cleanup;
982 failed:
983 ret = E_OUTOFMEMORY;
985 cleanup:
986 WSDFreeLinkedMemory(body_name);
987 WSDFreeLinkedMemory(body_element);
988 WSDFreeLinkedMemory(discoveredNamespaces);
990 return ret;