include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / wsdapi / soap.c
bloba2753c943a76f120840b4fddfee2e57b424f8a61
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 "webservices.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(wsdapi);
32 #define APP_MAX_DELAY 500
34 static const WCHAR *discoveryTo = L"urn:schemas-xmlsoap-org:ws:2005:04:discovery";
36 static const WCHAR *actionProbe = L"http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe";
38 static const WCHAR *addressingNsUri = L"http://schemas.xmlsoap.org/ws/2004/08/addressing";
39 static const WCHAR *discoveryNsUri = L"http://schemas.xmlsoap.org/ws/2005/04/discovery";
40 static const WCHAR *envelopeNsUri = L"http://www.w3.org/2003/05/soap-envelope";
42 static const WCHAR *addressingPrefix = L"wsa";
43 static const WCHAR *discoveryPrefix = L"wsd";
44 static const WCHAR *envelopePrefix = L"soap";
45 static const WCHAR *headerString = L"Header";
46 static const WCHAR *actionString = L"Action";
47 static const WCHAR *messageIdString = L"MessageID";
48 static const WCHAR *toString = L"To";
49 static const WCHAR *relatesToString = L"RelatesTo";
50 static const WCHAR *appSequenceString = L"AppSequence";
51 static const WCHAR *instanceIdString = L"InstanceId";
52 static const WCHAR *messageNumberString = L"MessageNumber";
53 static const WCHAR *sequenceIdString = L"SequenceId";
54 static const WCHAR *bodyString = L"Body";
55 static const WCHAR *helloString = L"Hello";
56 static const WCHAR *probeString = L"Probe";
57 static const WCHAR *probeMatchString = L"ProbeMatch";
58 static const WCHAR *probeMatchesString = L"ProbeMatches";
59 static const WCHAR *byeString = L"Bye";
60 static const WCHAR *endpointReferenceString = L"EndpointReference";
61 static const WCHAR *addressString = L"Address";
62 static const WCHAR *referenceParametersString = L"ReferenceParameters";
63 static const WCHAR *typesString = L"Types";
64 static const WCHAR *scopesString = L"Scopes";
65 static const WCHAR *xAddrsString = L"XAddrs";
66 static const WCHAR *metadataVersionString = L"MetadataVersion";
68 struct discovered_namespace
70 struct list entry;
71 LPCWSTR prefix;
72 LPCWSTR uri;
75 static LPWSTR utf8_to_wide(void *parent, const char *utf8_str, int length)
77 int utf8_str_len = 0, chars_needed = 0, bytes_needed = 0;
78 LPWSTR new_str = NULL;
80 if (utf8_str == NULL) return NULL;
82 utf8_str_len = (length < 0) ? lstrlenA(utf8_str) : length;
83 chars_needed = MultiByteToWideChar(CP_UTF8, 0, utf8_str, utf8_str_len, NULL, 0);
85 if (chars_needed <= 0) return NULL;
87 bytes_needed = sizeof(WCHAR) * (chars_needed + 1);
88 new_str = WSDAllocateLinkedMemory(parent, bytes_needed);
90 MultiByteToWideChar(CP_UTF8, 0, utf8_str, utf8_str_len, new_str, chars_needed);
91 new_str[chars_needed] = 0;
93 return new_str;
96 static char *wide_to_utf8(LPCWSTR wide_string, int *length)
98 char *new_string = NULL;
100 if (wide_string == NULL)
101 return NULL;
103 *length = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, NULL, 0, NULL, NULL);
105 if (*length < 0)
106 return NULL;
108 new_string = malloc(*length);
109 WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, new_string, *length, NULL, NULL);
111 return new_string;
114 static WS_XML_STRING *populate_xml_string(LPCWSTR str)
116 WS_XML_STRING *xml = calloc(1, sizeof(*xml));
117 int utf8Length;
119 if (xml == NULL)
120 return NULL;
122 xml->bytes = (BYTE *)wide_to_utf8(str, &utf8Length);
124 if (xml->bytes == NULL)
126 free(xml);
127 return NULL;
130 xml->dictionary = NULL;
131 xml->id = 0;
132 xml->length = (xml->bytes == NULL) ? 0 : (utf8Length - 1);
134 return xml;
137 static inline void free_xml_string(WS_XML_STRING *value)
139 if (value == NULL)
140 return;
142 free(value->bytes);
144 free(value);
147 static HRESULT write_xml_attribute(WSDXML_ATTRIBUTE *attribute, WS_XML_WRITER *writer)
149 WS_XML_STRING *local_name = NULL, *element_ns = NULL, *ns_prefix = NULL;
150 WS_XML_UTF16_TEXT utf16_text;
151 HRESULT ret = E_OUTOFMEMORY;
152 int text_len;
154 if (attribute == NULL)
155 return S_OK;
157 /* Start the attribute */
158 local_name = populate_xml_string(attribute->Name->LocalName);
159 if (local_name == NULL) goto cleanup;
161 if (attribute->Name->Space == NULL)
163 element_ns = populate_xml_string(L"");
164 if (element_ns == NULL) goto cleanup;
166 ns_prefix = NULL;
168 else
170 element_ns = populate_xml_string(attribute->Name->Space->Uri);
171 if (element_ns == NULL) goto cleanup;
173 ns_prefix = populate_xml_string(attribute->Name->Space->PreferredPrefix);
174 if (ns_prefix == NULL) goto cleanup;
177 ret = WsWriteStartAttribute(writer, ns_prefix, local_name, element_ns, FALSE, NULL);
178 if (FAILED(ret)) goto cleanup;
180 text_len = lstrlenW(attribute->Value);
182 utf16_text.text.textType = WS_XML_TEXT_TYPE_UTF16;
183 utf16_text.bytes = (BYTE *)attribute->Value;
184 utf16_text.byteCount = min(WSD_MAX_TEXT_LENGTH, text_len) * sizeof(WCHAR);
186 ret = WsWriteText(writer, (WS_XML_TEXT *)&utf16_text, NULL);
187 if (FAILED(ret)) goto cleanup;
189 ret = WsWriteEndAttribute(writer, NULL);
190 if (FAILED(ret)) goto cleanup;
192 cleanup:
193 free_xml_string(local_name);
194 free_xml_string(element_ns);
195 free_xml_string(ns_prefix);
197 return ret;
200 static HRESULT write_xml_element(WSDXML_ELEMENT *element, WS_XML_WRITER *writer)
202 WS_XML_STRING *local_name = NULL, *element_ns = NULL, *ns_prefix = NULL;
203 WSDXML_ATTRIBUTE *current_attribute;
204 WS_XML_UTF16_TEXT utf16_text;
205 WSDXML_NODE *current_child;
206 WSDXML_TEXT *node_as_text;
207 int text_len;
208 HRESULT ret = E_OUTOFMEMORY;
210 if (element == NULL)
211 return S_OK;
213 /* Start the element */
214 local_name = populate_xml_string(element->Name->LocalName);
215 if (local_name == NULL) goto cleanup;
217 element_ns = populate_xml_string(element->Name->Space->Uri);
218 if (element_ns == NULL) goto cleanup;
220 ns_prefix = populate_xml_string(element->Name->Space->PreferredPrefix);
221 if (ns_prefix == NULL) goto cleanup;
223 ret = WsWriteStartElement(writer, ns_prefix, local_name, element_ns, NULL);
224 if (FAILED(ret)) goto cleanup;
226 /* Write attributes */
227 current_attribute = element->FirstAttribute;
229 while (current_attribute != NULL)
231 ret = write_xml_attribute(current_attribute, writer);
232 if (FAILED(ret)) goto cleanup;
233 current_attribute = current_attribute->Next;
236 /* Write child elements */
237 current_child = element->FirstChild;
239 while (current_child != NULL)
241 if (current_child->Type == ElementType)
243 ret = write_xml_element((WSDXML_ELEMENT *)current_child, writer);
244 if (FAILED(ret)) goto cleanup;
246 else if (current_child->Type == TextType)
248 node_as_text = (WSDXML_TEXT *)current_child;
249 text_len = lstrlenW(node_as_text->Text);
251 utf16_text.text.textType = WS_XML_TEXT_TYPE_UTF16;
252 utf16_text.byteCount = min(WSD_MAX_TEXT_LENGTH, text_len) * sizeof(WCHAR);
253 utf16_text.bytes = (BYTE *)node_as_text->Text;
255 ret = WsWriteText(writer, (WS_XML_TEXT *)&utf16_text, NULL);
256 if (FAILED(ret)) goto cleanup;
259 current_child = current_child->Next;
262 /* End the element */
263 ret = WsWriteEndElement(writer, NULL);
265 cleanup:
266 free_xml_string(local_name);
267 free_xml_string(element_ns);
268 free_xml_string(ns_prefix);
270 return ret;
273 static HRESULT add_child_element(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri,
274 LPCWSTR name, LPCWSTR text, WSDXML_ELEMENT **out)
276 WSDXML_ELEMENT *element_obj;
277 WSDXML_NAME *name_obj;
278 HRESULT ret;
280 ret = IWSDXMLContext_AddNameToNamespace(xml_context, ns_uri, name, &name_obj);
281 if (FAILED(ret)) return ret;
283 ret = WSDXMLBuildAnyForSingleElement(name_obj, text, &element_obj);
284 WSDFreeLinkedMemory(name_obj);
286 if (FAILED(ret)) return ret;
288 /* Add the element as a child - this will link the element's memory allocation to the parent's */
289 ret = WSDXMLAddChild(parent, element_obj);
291 if (FAILED(ret))
293 WSDFreeLinkedMemory(element_obj);
294 return ret;
297 if (out != NULL) *out = element_obj;
298 return ret;
301 HRESULT register_namespaces(IWSDXMLContext *xml_context)
303 HRESULT ret;
305 ret = IWSDXMLContext_AddNamespace(xml_context, addressingNsUri, addressingPrefix, NULL);
306 if (FAILED(ret)) return ret;
308 ret = IWSDXMLContext_AddNamespace(xml_context, discoveryNsUri, discoveryPrefix, NULL);
309 if (FAILED(ret)) return ret;
311 return IWSDXMLContext_AddNamespace(xml_context, envelopeNsUri, envelopePrefix, NULL);
314 static BOOL create_guid(LPWSTR buffer)
316 WCHAR* uuidString = NULL;
317 UUID uuid;
319 if (UuidCreate(&uuid) != RPC_S_OK)
320 return FALSE;
322 UuidToStringW(&uuid, (RPC_WSTR*)&uuidString);
324 if (uuidString == NULL)
325 return FALSE;
327 wsprintfW(buffer, L"urn:uuid:%s", uuidString);
328 RpcStringFreeW((RPC_WSTR*)&uuidString);
330 return TRUE;
333 static void populate_soap_header(WSD_SOAP_HEADER *header, LPCWSTR to, LPCWSTR action, LPCWSTR message_id,
334 WSD_APP_SEQUENCE *sequence, const WSDXML_ELEMENT *any_headers)
336 ZeroMemory(header, sizeof(WSD_SOAP_HEADER));
338 header->To = to;
339 header->Action = action;
340 header->MessageID = message_id;
341 header->AppSequence = sequence;
342 header->AnyHeaders = (WSDXML_ELEMENT *)any_headers;
344 /* TODO: Implement RelatesTo, ReplyTo, From, FaultTo */
347 #define MAX_ULONGLONG_STRING_SIZE 25
349 static LPWSTR ulonglong_to_string(void *parent, ULONGLONG value)
351 LPWSTR ret;
353 ret = WSDAllocateLinkedMemory(parent, MAX_ULONGLONG_STRING_SIZE * sizeof(WCHAR));
355 if (ret == NULL)
356 return NULL;
358 wsprintfW(ret, L"%I64u", value);
359 return ret;
362 static WSDXML_ATTRIBUTE *add_attribute(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri, LPCWSTR name)
364 WSDXML_ATTRIBUTE *attribute, *cur_attrib;
365 WSDXML_NAME *name_obj = NULL;
367 if (ns_uri == NULL)
369 name_obj = WSDAllocateLinkedMemory(NULL, sizeof(WSDXML_NAME));
370 name_obj->LocalName = duplicate_string(name_obj, name);
371 name_obj->Space = NULL;
373 else
375 if (FAILED(IWSDXMLContext_AddNameToNamespace(xml_context, ns_uri, name, &name_obj)))
376 return NULL;
379 attribute = WSDAllocateLinkedMemory(parent, sizeof(WSDXML_ATTRIBUTE));
381 if (attribute == NULL)
383 WSDFreeLinkedMemory(name_obj);
384 return NULL;
387 attribute->Element = parent;
388 attribute->Name = name_obj;
389 attribute->Next = NULL;
390 attribute->Value = NULL;
392 if (name_obj != NULL)
393 WSDAttachLinkedMemory(attribute, name_obj);
395 if (parent->FirstAttribute == NULL)
397 /* Make this the first attribute of the parent */
398 parent->FirstAttribute = attribute;
400 else
402 /* Find the last attribute and add this as the next one */
403 cur_attrib = parent->FirstAttribute;
405 while (cur_attrib->Next != NULL)
407 cur_attrib = cur_attrib->Next;
410 cur_attrib->Next = attribute;
413 return attribute;
416 static void remove_attribute(WSDXML_ELEMENT *parent, WSDXML_ATTRIBUTE *attribute)
418 WSDXML_ATTRIBUTE *cur_attrib;
420 /* Find the last attribute and add this as the next one */
421 cur_attrib = parent->FirstAttribute;
423 if (cur_attrib == attribute)
424 parent->FirstAttribute = cur_attrib->Next;
425 else
427 while (cur_attrib != NULL)
429 /* Is our attribute the next attribute? */
430 if (cur_attrib->Next == attribute)
432 /* Remove it from the list */
433 cur_attrib->Next = attribute->Next;
434 break;
437 cur_attrib = cur_attrib->Next;
441 WSDFreeLinkedMemory(attribute);
444 static HRESULT add_ulonglong_attribute(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri, LPCWSTR name,
445 ULONGLONG value)
447 WSDXML_ATTRIBUTE *attribute = add_attribute(xml_context, parent, ns_uri, name);
449 if (attribute == NULL)
450 return E_FAIL;
452 attribute->Value = ulonglong_to_string(attribute, value);
454 if (attribute->Value == NULL)
456 remove_attribute(parent, attribute);
457 return E_FAIL;
460 return S_OK;
463 static HRESULT add_string_attribute(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri, LPCWSTR name,
464 LPCWSTR value)
466 WSDXML_ATTRIBUTE *attribute = add_attribute(xml_context, parent, ns_uri, name);
468 if (attribute == NULL)
469 return E_FAIL;
471 attribute->Value = duplicate_string(attribute, value);
473 if (attribute->Value == NULL)
475 remove_attribute(parent, attribute);
476 return E_FAIL;
479 return S_OK;
482 static BOOL add_discovered_namespace(struct list *namespaces, WSDXML_NAMESPACE *discovered_ns)
484 struct discovered_namespace *ns;
486 LIST_FOR_EACH_ENTRY(ns, namespaces, struct discovered_namespace, entry)
488 if (lstrcmpW(ns->uri, discovered_ns->Uri) == 0)
489 return TRUE; /* Already added */
492 ns = WSDAllocateLinkedMemory(namespaces, sizeof(struct discovered_namespace));
494 if (ns == NULL)
495 return FALSE;
497 ns->prefix = duplicate_string(ns, discovered_ns->PreferredPrefix);
498 ns->uri = duplicate_string(ns, discovered_ns->Uri);
500 if ((ns->prefix == NULL) || (ns->uri == NULL))
501 return FALSE;
503 list_add_tail(namespaces, &ns->entry);
504 return TRUE;
507 static HRESULT build_types_list(LPWSTR buffer, size_t buffer_size, const WSD_NAME_LIST *list, struct list *namespaces)
509 LPWSTR current_buf_pos = buffer;
510 size_t memory_needed = 0;
511 const WSD_NAME_LIST *cur = list;
515 /* Calculate space needed, including NULL character, colon and potential trailing space */
516 memory_needed = sizeof(WCHAR) * (lstrlenW(cur->Element->LocalName) +
517 lstrlenW(cur->Element->Space->PreferredPrefix) + 3);
519 if (current_buf_pos + memory_needed > buffer + buffer_size)
520 return E_INVALIDARG;
522 if (cur != list)
523 *current_buf_pos++ = ' ';
525 current_buf_pos += wsprintfW(current_buf_pos, L"%s:%s", cur->Element->Space->PreferredPrefix,
526 cur->Element->LocalName);
528 /* Record the namespace in the discovered namespaces list */
529 if (!add_discovered_namespace(namespaces, cur->Element->Space))
530 return E_FAIL;
532 cur = cur->Next;
533 } while (cur != NULL);
535 return S_OK;
538 static HRESULT build_uri_list(LPWSTR buffer, size_t buffer_size, const WSD_URI_LIST *list)
540 size_t memory_needed = 0, string_len = 0;
541 const WSD_URI_LIST *cur = list;
542 LPWSTR cur_buf_pos = buffer;
546 /* Calculate space needed, including trailing space */
547 string_len = lstrlenW(cur->Element);
548 memory_needed = (string_len + 1) * sizeof(WCHAR);
550 if (cur_buf_pos + memory_needed > buffer + buffer_size)
551 return E_INVALIDARG;
553 if (cur != list)
554 *cur_buf_pos++ = ' ';
556 memcpy(cur_buf_pos, cur->Element, memory_needed);
557 cur_buf_pos += string_len;
559 cur = cur->Next;
560 } while (cur != NULL);
562 return S_OK;
565 static HRESULT duplicate_element(WSDXML_ELEMENT *parent, const WSDXML_ELEMENT *node, struct list *namespaces)
567 WSDXML_ATTRIBUTE *cur_attribute, *new_attribute, *last_attribute = NULL;
568 WSDXML_ELEMENT *new_element;
569 WSDXML_TEXT *text_node;
570 WSDXML_NODE *cur_node;
571 HRESULT ret;
573 /* First record the namespace in the discovered namespaces list */
574 if (!add_discovered_namespace(namespaces, node->Name->Space))
575 return E_FAIL;
577 ret = WSDXMLBuildAnyForSingleElement(node->Name, NULL, &new_element);
578 if (FAILED(ret)) return ret;
580 /* Duplicate the nodes */
581 cur_node = node->FirstChild;
583 while (cur_node != NULL)
585 if (cur_node->Type == ElementType)
587 ret = duplicate_element(new_element, (WSDXML_ELEMENT *)cur_node, namespaces);
588 if (FAILED(ret)) goto cleanup;
590 else if (cur_node->Type == TextType)
592 text_node = WSDAllocateLinkedMemory(new_element, sizeof(WSDXML_TEXT));
593 if (text_node == NULL) goto failed;
595 text_node->Node.Parent = NULL;
596 text_node->Node.Next = NULL;
597 text_node->Node.Type = TextType;
598 text_node->Text = duplicate_string(text_node, ((WSDXML_TEXT *)cur_node)->Text);
600 if (text_node->Text == NULL) goto failed;
602 ret = WSDXMLAddChild(new_element, (WSDXML_ELEMENT *)text_node);
603 if (FAILED(ret)) goto cleanup;
606 cur_node = cur_node->Next;
609 /* Duplicate the attributes */
610 cur_attribute = node->FirstAttribute;
612 while (cur_attribute != NULL)
614 if ((cur_attribute->Name->Space != NULL) && (!add_discovered_namespace(namespaces, cur_attribute->Name->Space)))
615 goto failed;
617 new_attribute = WSDAllocateLinkedMemory(new_element, sizeof(WSDXML_ATTRIBUTE));
618 if (new_attribute == NULL) goto failed;
620 new_attribute->Element = new_element;
621 new_attribute->Name = duplicate_name(new_attribute, cur_attribute->Name);
622 new_attribute->Value = duplicate_string(new_attribute, cur_attribute->Value);
623 new_attribute->Next = NULL;
625 if ((new_attribute->Name == NULL) || (new_attribute->Value == NULL)) goto failed;
627 if (last_attribute == NULL)
628 new_element->FirstAttribute = new_attribute;
629 else
630 last_attribute->Next = new_attribute;
632 last_attribute = new_attribute;
633 cur_attribute = cur_attribute->Next;
636 ret = WSDXMLAddChild(parent, new_element);
637 if (FAILED(ret)) goto cleanup;
639 return ret;
641 failed:
642 ret = E_FAIL;
644 cleanup:
645 WSDXMLCleanupElement(new_element);
646 return ret;
649 static HRESULT create_soap_header_xml_elements(IWSDXMLContext *xml_context, WSD_SOAP_HEADER *header,
650 struct list *discovered_namespaces, WSDXML_ELEMENT **out_element)
652 WSDXML_ELEMENT *header_element = NULL, *app_sequence_element = NULL, *temp_element;
653 WSDXML_NAME *header_name = NULL;
654 HRESULT ret;
656 /* <s:Header> */
657 ret = IWSDXMLContext_AddNameToNamespace(xml_context, envelopeNsUri, headerString, &header_name);
658 if (FAILED(ret)) goto cleanup;
660 ret = WSDXMLBuildAnyForSingleElement(header_name, NULL, &header_element);
661 if (FAILED(ret)) goto cleanup;
663 WSDFreeLinkedMemory(header_name);
665 /* <a:Action> */
666 ret = add_child_element(xml_context, header_element, addressingNsUri, actionString, header->Action, &temp_element);
667 if (FAILED(ret)) goto cleanup;
669 /* <a:MessageId> */
670 ret = add_child_element(xml_context, header_element, addressingNsUri, messageIdString, header->MessageID, &temp_element);
671 if (FAILED(ret)) goto cleanup;
673 /* <a:To> */
674 ret = add_child_element(xml_context, header_element, addressingNsUri, toString, header->To, &temp_element);
675 if (FAILED(ret)) goto cleanup;
677 /* <a:RelatesTo> */
678 if (header->RelatesTo.MessageID != NULL)
680 ret = add_child_element(xml_context, header_element, addressingNsUri, relatesToString,
681 header->RelatesTo.MessageID, &temp_element);
682 if (FAILED(ret)) goto cleanup;
685 /* <d:AppSequence> */
686 ret = add_child_element(xml_context, header_element, discoveryNsUri, appSequenceString, L"", &app_sequence_element);
687 if (FAILED(ret)) goto cleanup;
689 /* InstanceId attribute */
690 ret = add_ulonglong_attribute(xml_context, app_sequence_element, NULL, instanceIdString, min(UINT_MAX,
691 header->AppSequence->InstanceId));
692 if (FAILED(ret)) goto cleanup;
694 /* SequenceID attribute */
695 if (header->AppSequence->SequenceId != NULL)
697 ret = add_string_attribute(xml_context, app_sequence_element, NULL, sequenceIdString, header->AppSequence->SequenceId);
698 if (FAILED(ret)) goto cleanup;
701 /* MessageNumber attribute */
702 ret = add_ulonglong_attribute(xml_context, app_sequence_element, NULL, messageNumberString, min(UINT_MAX,
703 header->AppSequence->MessageNumber));
704 if (FAILED(ret)) goto cleanup;
706 /* </d:AppSequence> */
708 /* Write any headers */
709 if (header->AnyHeaders != NULL)
711 ret = duplicate_element(header_element, header->AnyHeaders, discovered_namespaces);
712 if (FAILED(ret)) goto cleanup;
715 /* </s:Header> */
717 *out_element = header_element;
718 return ret;
720 cleanup:
721 if (header_name != NULL) WSDFreeLinkedMemory(header_name);
722 WSDXMLCleanupElement(header_element);
724 return ret;
727 static HRESULT create_soap_envelope(IWSDXMLContext *xml_context, WSD_SOAP_HEADER *header, WSDXML_ELEMENT *body_element,
728 WS_HEAP **heap, char **output_xml, ULONG *xml_length, struct list *discovered_namespaces)
730 WS_XML_STRING *actual_envelope_prefix = NULL, *envelope_uri_xmlstr = NULL, *tmp_prefix = NULL, *tmp_uri = NULL;
731 WSDXML_NAMESPACE *addressing_ns = NULL, *discovery_ns = NULL, *envelope_ns = NULL;
732 WSDXML_ELEMENT *header_element = NULL;
733 struct discovered_namespace *ns;
734 WS_XML_BUFFER *buffer = NULL;
735 WS_XML_WRITER *writer = NULL;
736 WS_XML_STRING envelope;
737 HRESULT ret = E_OUTOFMEMORY;
738 static BYTE envelopeString[] = "Envelope";
740 /* Create the necessary XML prefixes */
741 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, addressingNsUri, addressingPrefix, &addressing_ns))) goto cleanup;
742 if (!add_discovered_namespace(discovered_namespaces, addressing_ns)) goto cleanup;
744 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, discoveryNsUri, discoveryPrefix, &discovery_ns))) goto cleanup;
745 if (!add_discovered_namespace(discovered_namespaces, discovery_ns)) goto cleanup;
747 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, envelopeNsUri, envelopePrefix, &envelope_ns))) goto cleanup;
748 if (!add_discovered_namespace(discovered_namespaces, envelope_ns)) goto cleanup;
750 envelope.bytes = envelopeString;
751 envelope.length = sizeof(envelopeString) - 1;
752 envelope.dictionary = NULL;
753 envelope.id = 0;
755 actual_envelope_prefix = populate_xml_string(envelope_ns->PreferredPrefix);
756 envelope_uri_xmlstr = populate_xml_string(envelope_ns->Uri);
758 if ((actual_envelope_prefix == NULL) || (envelope_uri_xmlstr == NULL)) goto cleanup;
760 /* Now try to create the appropriate WebServices buffers, etc */
761 ret = WsCreateHeap(16384, 4096, NULL, 0, heap, NULL);
762 if (FAILED(ret)) goto cleanup;
764 ret = WsCreateXmlBuffer(*heap, NULL, 0, &buffer, NULL);
765 if (FAILED(ret)) goto cleanup;
767 ret = WsCreateWriter(NULL, 0, &writer, NULL);
768 if (FAILED(ret)) goto cleanup;
770 ret = WsSetOutputToBuffer(writer, buffer, NULL, 0, NULL);
771 if (FAILED(ret)) goto cleanup;
773 /* Create the header XML elements */
774 ret = create_soap_header_xml_elements(xml_context, header, discovered_namespaces, &header_element);
775 if (FAILED(ret)) goto cleanup;
777 /* <s:Envelope> */
778 ret = WsWriteStartElement(writer, actual_envelope_prefix, &envelope, envelope_uri_xmlstr, NULL);
779 if (FAILED(ret)) goto cleanup;
781 LIST_FOR_EACH_ENTRY(ns, discovered_namespaces, struct discovered_namespace, entry)
783 tmp_prefix = populate_xml_string(ns->prefix);
784 tmp_uri = populate_xml_string(ns->uri);
786 if ((tmp_prefix == NULL) || (tmp_uri == NULL)) goto cleanup;
788 ret = WsWriteXmlnsAttribute(writer, tmp_prefix, tmp_uri, FALSE, NULL);
789 if (FAILED(ret)) goto cleanup;
791 free_xml_string(tmp_prefix);
792 free_xml_string(tmp_uri);
795 tmp_prefix = NULL;
796 tmp_uri = NULL;
798 /* Write the header */
799 ret = write_xml_element(header_element, writer);
800 if (FAILED(ret)) goto cleanup;
802 /* Write the body */
803 ret = write_xml_element(body_element, writer);
804 if (FAILED(ret)) goto cleanup;
806 ret = WsWriteEndElement(writer, NULL);
807 if (FAILED(ret)) goto cleanup;
809 /* </s:Envelope> */
811 /* Generate the bytes of the document */
812 ret = WsWriteXmlBufferToBytes(writer, buffer, NULL, NULL, 0, *heap, (void**)output_xml, xml_length, NULL);
813 if (FAILED(ret)) goto cleanup;
815 cleanup:
816 WSDFreeLinkedMemory(addressing_ns);
817 WSDFreeLinkedMemory(discovery_ns);
818 WSDFreeLinkedMemory(envelope_ns);
820 WSDXMLCleanupElement(header_element);
822 free_xml_string(actual_envelope_prefix);
823 free_xml_string(envelope_uri_xmlstr);
825 if (writer != NULL)
826 WsFreeWriter(writer);
828 /* Don't free the heap unless the operation has failed */
829 if ((FAILED(ret)) && (*heap != NULL))
831 WsFreeHeap(*heap);
832 *heap = NULL;
835 return ret;
838 static HRESULT write_and_send_message(IWSDiscoveryPublisherImpl *impl, WSD_SOAP_HEADER *header, WSDXML_ELEMENT *body_element,
839 struct list *discovered_namespaces, IWSDUdpAddress *remote_address, int max_initial_delay)
841 static const char xml_header[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
842 ULONG xml_length = 0, xml_header_len = sizeof(xml_header) - 1;
843 WS_HEAP *heap = NULL;
844 char *xml = NULL;
845 char *full_xml;
846 HRESULT ret;
848 ret = create_soap_envelope(impl->xmlContext, header, body_element, &heap, &xml, &xml_length, discovered_namespaces);
849 if (ret != S_OK) return ret;
851 /* Prefix the XML header */
852 full_xml = malloc(xml_length + xml_header_len + 1);
854 if (full_xml == NULL)
856 WsFreeHeap(heap);
857 return E_OUTOFMEMORY;
860 memcpy(full_xml, xml_header, xml_header_len);
861 memcpy(full_xml + xml_header_len, xml, xml_length);
862 full_xml[xml_length + xml_header_len] = 0;
864 if (remote_address == NULL)
866 /* Send the message via UDP multicast */
867 ret = send_udp_multicast(impl, full_xml, xml_length + xml_header_len, max_initial_delay) ? S_OK : E_FAIL;
869 else
871 /* Send the message via UDP unicast */
872 ret = send_udp_unicast(full_xml, xml_length + xml_header_len, remote_address, max_initial_delay);
875 free(full_xml);
876 WsFreeHeap(heap);
878 return ret;
881 HRESULT send_hello_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id, ULONGLONG metadata_ver, ULONGLONG instance_id,
882 ULONGLONG msg_num, LPCWSTR session_id, const WSD_NAME_LIST *types_list, const WSD_URI_LIST *scopes_list,
883 const WSD_URI_LIST *xaddrs_list, const WSDXML_ELEMENT *hdr_any, const WSDXML_ELEMENT *ref_param_any,
884 const WSDXML_ELEMENT *endpoint_ref_any, const WSDXML_ELEMENT *any)
886 WSDXML_ELEMENT *body_element = NULL, *hello_element, *endpoint_reference_element, *ref_params_element;
887 struct list *discoveredNamespaces = NULL;
888 WSDXML_NAME *body_name = NULL;
889 WSD_SOAP_HEADER soapHeader;
890 WSD_APP_SEQUENCE sequence;
891 WCHAR message_id[64];
892 HRESULT ret = E_OUTOFMEMORY;
893 LPWSTR buffer;
895 sequence.InstanceId = instance_id;
896 sequence.MessageNumber = msg_num;
897 sequence.SequenceId = session_id;
899 if (!create_guid(message_id)) goto failed;
901 discoveredNamespaces = WSDAllocateLinkedMemory(NULL, sizeof(struct list));
902 if (!discoveredNamespaces) goto failed;
904 list_init(discoveredNamespaces);
906 populate_soap_header(&soapHeader, discoveryTo, L"http://schemas.xmlsoap.org/ws/2005/04/discovery/Hello", message_id, &sequence, hdr_any);
908 ret = IWSDXMLContext_AddNameToNamespace(impl->xmlContext, envelopeNsUri, bodyString, &body_name);
909 if (FAILED(ret)) goto cleanup;
911 /* <soap:Body>, <wsd:Hello> */
912 ret = WSDXMLBuildAnyForSingleElement(body_name, NULL, &body_element);
913 if (FAILED(ret)) goto cleanup;
915 ret = add_child_element(impl->xmlContext, body_element, discoveryNsUri, helloString, NULL, &hello_element);
916 if (FAILED(ret)) goto cleanup;
918 /* <wsa:EndpointReference>, <wsa:Address> */
919 ret = add_child_element(impl->xmlContext, hello_element, addressingNsUri, endpointReferenceString, NULL,
920 &endpoint_reference_element);
921 if (FAILED(ret)) goto cleanup;
923 ret = add_child_element(impl->xmlContext, endpoint_reference_element, addressingNsUri, addressString, id, NULL);
924 if (FAILED(ret)) goto cleanup;
926 /* Write any reference parameters */
927 if (ref_param_any != NULL)
929 ret = add_child_element(impl->xmlContext, endpoint_reference_element, addressingNsUri, referenceParametersString,
930 NULL, &ref_params_element);
931 if (FAILED(ret)) goto cleanup;
933 ret = duplicate_element(ref_params_element, ref_param_any, discoveredNamespaces);
934 if (FAILED(ret)) goto cleanup;
937 /* Write any endpoint reference headers */
938 if (endpoint_ref_any != NULL)
940 ret = duplicate_element(endpoint_reference_element, endpoint_ref_any, discoveredNamespaces);
941 if (FAILED(ret)) goto cleanup;
944 /* <wsd:Types> */
945 if (types_list != NULL)
947 buffer = WSDAllocateLinkedMemory(hello_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
948 if (buffer == NULL) goto failed;
950 ret = build_types_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), types_list, discoveredNamespaces);
951 if (FAILED(ret)) goto cleanup;
953 ret = add_child_element(impl->xmlContext, hello_element, discoveryNsUri, typesString, buffer, NULL);
954 if (FAILED(ret)) goto cleanup;
957 /* <wsd:Scopes> */
958 if (scopes_list != NULL)
960 buffer = WSDAllocateLinkedMemory(hello_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
961 if (buffer == NULL) goto failed;
963 ret = build_uri_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), scopes_list);
964 if (FAILED(ret)) goto cleanup;
966 ret = add_child_element(impl->xmlContext, hello_element, discoveryNsUri, scopesString, buffer, NULL);
967 if (FAILED(ret)) goto cleanup;
970 /* <wsd:XAddrs> */
971 if (xaddrs_list != NULL)
973 buffer = WSDAllocateLinkedMemory(hello_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
974 if (buffer == NULL) goto failed;
976 ret = build_uri_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), xaddrs_list);
977 if (FAILED(ret)) goto cleanup;
979 ret = add_child_element(impl->xmlContext, hello_element, discoveryNsUri, xAddrsString, buffer, NULL);
980 if (FAILED(ret)) goto cleanup;
983 /* <wsd:MetadataVersion> */
984 ret = add_child_element(impl->xmlContext, hello_element, discoveryNsUri, metadataVersionString,
985 ulonglong_to_string(hello_element, min(UINT_MAX, metadata_ver)), NULL);
987 if (FAILED(ret)) goto cleanup;
989 /* Write any body elements */
990 if (any != NULL)
992 ret = duplicate_element(hello_element, any, discoveredNamespaces);
993 if (FAILED(ret)) goto cleanup;
996 /* Write and send the message */
997 ret = write_and_send_message(impl, &soapHeader, body_element, discoveredNamespaces, NULL, APP_MAX_DELAY);
998 goto cleanup;
1000 failed:
1001 ret = E_OUTOFMEMORY;
1003 cleanup:
1004 WSDFreeLinkedMemory(body_name);
1005 WSDFreeLinkedMemory(body_element);
1006 WSDFreeLinkedMemory(discoveredNamespaces);
1008 return ret;
1011 HRESULT send_bye_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id, ULONGLONG instance_id, ULONGLONG msg_num,
1012 LPCWSTR session_id, const WSDXML_ELEMENT *any)
1014 WSDXML_ELEMENT *body_element = NULL, *bye_element, *endpoint_reference_element;
1015 struct list *discovered_namespaces = NULL;
1016 WSDXML_NAME *body_name = NULL;
1017 WSD_SOAP_HEADER soap_header;
1018 WSD_APP_SEQUENCE sequence;
1019 WCHAR message_id[64];
1020 HRESULT ret = E_OUTOFMEMORY;
1022 sequence.InstanceId = instance_id;
1023 sequence.MessageNumber = msg_num;
1024 sequence.SequenceId = session_id;
1026 if (!create_guid(message_id)) goto failed;
1028 discovered_namespaces = WSDAllocateLinkedMemory(NULL, sizeof(struct list));
1029 if (!discovered_namespaces) goto failed;
1031 list_init(discovered_namespaces);
1033 populate_soap_header(&soap_header, discoveryTo, L"http://schemas.xmlsoap.org/ws/2005/04/discovery/Bye", message_id, &sequence, NULL);
1035 ret = IWSDXMLContext_AddNameToNamespace(impl->xmlContext, envelopeNsUri, bodyString, &body_name);
1036 if (FAILED(ret)) goto cleanup;
1038 /* <soap:Body>, <wsd:Bye> */
1039 ret = WSDXMLBuildAnyForSingleElement(body_name, NULL, &body_element);
1040 if (FAILED(ret)) goto cleanup;
1042 ret = add_child_element(impl->xmlContext, body_element, discoveryNsUri, byeString, NULL, &bye_element);
1043 if (FAILED(ret)) goto cleanup;
1045 /* <wsa:EndpointReference>, <wsa:Address> */
1046 ret = add_child_element(impl->xmlContext, bye_element, addressingNsUri, endpointReferenceString, NULL,
1047 &endpoint_reference_element);
1048 if (FAILED(ret)) goto cleanup;
1050 ret = add_child_element(impl->xmlContext, endpoint_reference_element, addressingNsUri, addressString, id, NULL);
1051 if (FAILED(ret)) goto cleanup;
1053 /* Write any body elements */
1054 if (any != NULL)
1056 ret = duplicate_element(bye_element, any, discovered_namespaces);
1057 if (FAILED(ret)) goto cleanup;
1060 /* Write and send the message */
1061 ret = write_and_send_message(impl, &soap_header, body_element, discovered_namespaces, NULL, 0);
1062 goto cleanup;
1064 failed:
1065 ret = E_OUTOFMEMORY;
1067 cleanup:
1068 WSDFreeLinkedMemory(body_name);
1069 WSDFreeLinkedMemory(body_element);
1070 WSDFreeLinkedMemory(discovered_namespaces);
1072 return ret;
1075 HRESULT send_probe_matches_message(IWSDiscoveryPublisherImpl *impl, const WSD_SOAP_MESSAGE *probe_msg,
1076 IWSDMessageParameters *message_params, LPCWSTR id, ULONGLONG metadata_ver, ULONGLONG instance_id,
1077 ULONGLONG msg_num, LPCWSTR session_id, const WSD_NAME_LIST *types_list, const WSD_URI_LIST *scopes_list,
1078 const WSD_URI_LIST *xaddrs_list, const WSDXML_ELEMENT *header_any, const WSDXML_ELEMENT *ref_param_any,
1079 const WSDXML_ELEMENT *endpoint_ref_any, const WSDXML_ELEMENT *any)
1081 WSDXML_ELEMENT *body_element = NULL, *probe_matches_element, *probe_match_element, *endpoint_ref_element;
1082 WSDXML_ELEMENT *ref_params_element = NULL;
1083 struct list *discovered_namespaces = NULL;
1084 IWSDUdpAddress *remote_udp_addr = NULL;
1085 IWSDAddress *remote_addr = NULL;
1086 WSDXML_NAME *body_name = NULL;
1087 WSD_SOAP_HEADER soap_header;
1088 WSD_APP_SEQUENCE sequence;
1089 WCHAR msg_id[64];
1090 LPWSTR buffer;
1091 HRESULT ret;
1093 ret = IWSDMessageParameters_GetRemoteAddress(message_params, &remote_addr);
1095 if (FAILED(ret))
1097 WARN("Unable to retrieve remote address from IWSDMessageParameters\n");
1098 return ret;
1101 ret = IWSDAddress_QueryInterface(remote_addr, &IID_IWSDUdpAddress, (LPVOID *) &remote_udp_addr);
1103 if (FAILED(ret))
1105 WARN("Remote address is not a UDP address\n");
1106 goto cleanup;
1109 sequence.InstanceId = instance_id;
1110 sequence.MessageNumber = msg_num;
1111 sequence.SequenceId = session_id;
1113 if (!create_guid(msg_id)) goto failed;
1115 discovered_namespaces = WSDAllocateLinkedMemory(NULL, sizeof(struct list));
1116 if (!discovered_namespaces) goto failed;
1118 list_init(discovered_namespaces);
1120 populate_soap_header(&soap_header, L"http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous",
1121 L"http://schemas.xmlsoap.org/ws/2005/04/discovery/ProbeMatches", msg_id, &sequence, header_any);
1122 soap_header.RelatesTo.MessageID = probe_msg->Header.MessageID;
1124 ret = IWSDXMLContext_AddNameToNamespace(impl->xmlContext, envelopeNsUri, bodyString, &body_name);
1125 if (FAILED(ret)) goto cleanup;
1127 /* <soap:Body>, <wsd:ProbeMatches> */
1128 ret = WSDXMLBuildAnyForSingleElement(body_name, NULL, &body_element);
1129 if (FAILED(ret)) goto cleanup;
1131 ret = add_child_element(impl->xmlContext, body_element, discoveryNsUri, probeMatchesString, NULL,
1132 &probe_matches_element);
1133 if (FAILED(ret)) goto cleanup;
1135 /* <wsd:ProbeMatch> */
1136 ret = add_child_element(impl->xmlContext, probe_matches_element, discoveryNsUri, probeMatchString, NULL,
1137 &probe_match_element);
1138 if (FAILED(ret)) goto cleanup;
1140 /* <wsa:EndpointReference>, <wsa:Address> */
1141 ret = add_child_element(impl->xmlContext, probe_match_element, addressingNsUri, endpointReferenceString, NULL,
1142 &endpoint_ref_element);
1143 if (FAILED(ret)) goto cleanup;
1145 ret = add_child_element(impl->xmlContext, endpoint_ref_element, addressingNsUri, addressString, id, NULL);
1146 if (FAILED(ret)) goto cleanup;
1148 /* Write any reference parameters */
1149 if (ref_param_any != NULL)
1151 ret = add_child_element(impl->xmlContext, endpoint_ref_element, addressingNsUri, referenceParametersString,
1152 NULL, &ref_params_element);
1153 if (FAILED(ret)) goto cleanup;
1155 ret = duplicate_element(ref_params_element, ref_param_any, discovered_namespaces);
1156 if (FAILED(ret)) goto cleanup;
1159 /* Write any endpoint reference headers */
1160 if (endpoint_ref_any != NULL)
1162 ret = duplicate_element(endpoint_ref_element, endpoint_ref_any, discovered_namespaces);
1163 if (FAILED(ret)) goto cleanup;
1166 /* <wsd:Types> */
1167 if (types_list != NULL)
1169 buffer = WSDAllocateLinkedMemory(probe_match_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
1170 if (buffer == NULL) goto failed;
1172 ret = build_types_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), types_list, discovered_namespaces);
1173 if (FAILED(ret)) goto cleanup;
1175 ret = add_child_element(impl->xmlContext, probe_match_element, discoveryNsUri, typesString, buffer, NULL);
1176 if (FAILED(ret)) goto cleanup;
1179 /* <wsd:Scopes> */
1180 if (scopes_list != NULL)
1182 buffer = WSDAllocateLinkedMemory(probe_match_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
1183 if (buffer == NULL) goto failed;
1185 ret = build_uri_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), scopes_list);
1186 if (FAILED(ret)) goto cleanup;
1188 ret = add_child_element(impl->xmlContext, probe_match_element, discoveryNsUri, scopesString, buffer, NULL);
1189 if (FAILED(ret)) goto cleanup;
1192 /* <wsd:XAddrs> */
1193 if (xaddrs_list != NULL)
1195 buffer = WSDAllocateLinkedMemory(probe_match_element, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR));
1196 if (buffer == NULL) goto failed;
1198 ret = build_uri_list(buffer, WSD_MAX_TEXT_LENGTH * sizeof(WCHAR), xaddrs_list);
1199 if (FAILED(ret)) goto cleanup;
1201 ret = add_child_element(impl->xmlContext, probe_match_element, discoveryNsUri, xAddrsString, buffer, NULL);
1202 if (FAILED(ret)) goto cleanup;
1205 /* <wsd:MetadataVersion> */
1206 ret = add_child_element(impl->xmlContext, probe_match_element, discoveryNsUri, metadataVersionString,
1207 ulonglong_to_string(probe_match_element, min(UINT_MAX, metadata_ver)), NULL);
1208 if (FAILED(ret)) goto cleanup;
1210 /* Write any body elements */
1211 if (any != NULL)
1213 ret = duplicate_element(probe_match_element, any, discovered_namespaces);
1214 if (FAILED(ret)) goto cleanup;
1217 /* Write and send the message */
1218 ret = write_and_send_message(impl, &soap_header, body_element, discovered_namespaces, remote_udp_addr, APP_MAX_DELAY);
1219 goto cleanup;
1221 failed:
1222 ret = E_FAIL;
1224 cleanup:
1225 WSDFreeLinkedMemory(body_name);
1226 WSDFreeLinkedMemory(body_element);
1227 WSDFreeLinkedMemory(discovered_namespaces);
1229 if (remote_udp_addr != NULL) IWSDUdpAddress_Release(remote_udp_addr);
1230 if (remote_addr != NULL) IWSDAddress_Release(remote_addr);
1232 return ret;
1235 static LPWSTR xml_text_to_wide_string(void *parent_memory, WS_XML_TEXT *text)
1237 if (text->textType == WS_XML_TEXT_TYPE_UTF8)
1239 WS_XML_UTF8_TEXT *utf8_text = (WS_XML_UTF8_TEXT *) text;
1240 return utf8_to_wide(parent_memory, (const char *) utf8_text->value.bytes, utf8_text->value.length);
1242 else if (text->textType == WS_XML_TEXT_TYPE_UTF16)
1244 WS_XML_UTF16_TEXT *utf_16_text = (WS_XML_UTF16_TEXT *) text;
1245 return duplicate_string(parent_memory, (LPCWSTR) utf_16_text->bytes);
1248 FIXME("Support for text type %d not implemented.\n", text->textType);
1249 return NULL;
1252 static inline BOOL read_isspace(unsigned int ch)
1254 return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
1257 static HRESULT str_to_uint64(const unsigned char *str, ULONG len, UINT64 max, UINT64 *ret)
1259 const unsigned char *ptr = str;
1261 *ret = 0;
1262 while (len && read_isspace(*ptr)) { ptr++; len--; }
1263 while (len && read_isspace(ptr[len - 1])) { len--; }
1264 if (!len) return WS_E_INVALID_FORMAT;
1266 while (len--)
1268 unsigned int val;
1270 if (!isdigit(*ptr)) return WS_E_INVALID_FORMAT;
1271 val = *ptr - '0';
1273 if ((*ret > max / 10 || *ret * 10 > max - val)) return WS_E_NUMERIC_OVERFLOW;
1274 *ret = *ret * 10 + val;
1275 ptr++;
1278 return S_OK;
1281 #define MAX_UINT64 (((UINT64)0xffffffff << 32) | 0xffffffff)
1283 static HRESULT wide_text_to_ulonglong(LPCWSTR text, ULONGLONG *value)
1285 char *utf8_text;
1286 int utf8_length;
1287 HRESULT ret;
1289 utf8_text = wide_to_utf8(text, &utf8_length);
1291 if (utf8_text == NULL) return E_OUTOFMEMORY;
1292 if (utf8_length == 1) return E_FAIL;
1294 ret = str_to_uint64((const unsigned char *) utf8_text, utf8_length - 1, MAX_UINT64, value);
1295 free(utf8_text);
1297 return ret;
1300 static HRESULT move_to_element(WS_XML_READER *reader, const char *element_name, WS_XML_STRING *uri)
1302 WS_XML_STRING envelope;
1303 BOOL found = FALSE;
1304 HRESULT ret;
1306 envelope.bytes = (BYTE *) element_name;
1307 envelope.length = strlen(element_name);
1308 envelope.dictionary = NULL;
1309 envelope.id = 0;
1311 ret = WsReadToStartElement(reader, &envelope, uri, &found, NULL);
1312 if (FAILED(ret)) return ret;
1314 return found ? ret : E_FAIL;
1317 static void trim_trailing_slash(LPWSTR uri)
1319 /* Trim trailing slash from URI */
1320 int uri_len = lstrlenW(uri);
1321 if (uri_len > 0 && uri[uri_len - 1] == '/') uri[uri_len - 1] = 0;
1324 static HRESULT ws_element_to_wsdxml_element(WS_XML_READER *reader, IWSDXMLContext *context, WSDXML_ELEMENT *parent_element)
1326 WSDXML_ATTRIBUTE *cur_wsd_attrib = NULL, *new_wsd_attrib = NULL;
1327 const WS_XML_ELEMENT_NODE *element_node = NULL;
1328 WSDXML_ELEMENT *cur_element = parent_element;
1329 const WS_XML_TEXT_NODE *text_node = NULL;
1330 LPWSTR uri = NULL, element_name = NULL;
1331 WS_XML_STRING *ns_string = NULL;
1332 WS_XML_ATTRIBUTE *attrib = NULL;
1333 WSDXML_ELEMENT *element = NULL;
1334 const WS_XML_NODE *node = NULL;
1335 WSDXML_NAME *name = NULL;
1336 WSDXML_TEXT *text = NULL;
1337 HRESULT ret;
1338 int i;
1340 for (;;)
1342 if (cur_element == NULL) break;
1344 ret = WsReadNode(reader, NULL);
1345 if (FAILED(ret)) goto cleanup;
1347 ret = WsGetReaderNode(reader, &node, NULL);
1348 if (FAILED(ret)) goto cleanup;
1350 switch (node->nodeType)
1352 case WS_XML_NODE_TYPE_ELEMENT:
1353 element_node = (const WS_XML_ELEMENT_NODE *) node;
1355 uri = utf8_to_wide(NULL, (const char *) element_node->ns->bytes, element_node->ns->length);
1356 if (uri == NULL) goto outofmemory;
1358 /* Link element_name to uri so they will be freed at the same time */
1359 element_name = utf8_to_wide(uri, (const char *) element_node->localName->bytes,
1360 element_node->localName->length);
1361 if (element_name == NULL) goto outofmemory;
1363 trim_trailing_slash(uri);
1365 ret = IWSDXMLContext_AddNameToNamespace(context, uri, element_name, &name);
1366 if (FAILED(ret)) goto cleanup;
1368 WSDFreeLinkedMemory(uri);
1369 uri = NULL;
1371 ret = WSDXMLBuildAnyForSingleElement(name, NULL, &element);
1372 if (FAILED(ret)) goto cleanup;
1373 WSDXMLAddChild(cur_element, element);
1375 WSDFreeLinkedMemory(name);
1376 name = NULL;
1378 cur_wsd_attrib = NULL;
1380 /* Add attributes */
1381 for (i = 0; i < element_node->attributeCount; i++)
1383 attrib = element_node->attributes[i];
1384 if (attrib->isXmlNs) continue;
1386 new_wsd_attrib = WSDAllocateLinkedMemory(element, sizeof(WSDXML_ATTRIBUTE));
1387 if (new_wsd_attrib == NULL) goto outofmemory;
1389 ns_string = attrib->ns;
1390 if (ns_string->length == 0) ns_string = element_node->ns;
1392 uri = utf8_to_wide(NULL, (const char *) ns_string->bytes, ns_string->length);
1393 if (uri == NULL) goto outofmemory;
1395 trim_trailing_slash(uri);
1397 /* Link element_name to uri so they will be freed at the same time */
1398 element_name = utf8_to_wide(uri, (const char *) attrib->localName->bytes, attrib->localName->length);
1399 if (element_name == NULL) goto outofmemory;
1401 ret = IWSDXMLContext_AddNameToNamespace(context, uri, element_name, &name);
1402 if (FAILED(ret)) goto cleanup;
1404 WSDFreeLinkedMemory(uri);
1405 uri = NULL;
1407 new_wsd_attrib->Value = xml_text_to_wide_string(new_wsd_attrib, attrib->value);
1408 if (new_wsd_attrib->Value == NULL) goto outofmemory;
1410 new_wsd_attrib->Name = name;
1411 new_wsd_attrib->Element = cur_element;
1412 new_wsd_attrib->Next = NULL;
1414 WSDAttachLinkedMemory(new_wsd_attrib, name);
1415 name = NULL;
1417 if (cur_wsd_attrib == NULL)
1418 element->FirstAttribute = new_wsd_attrib;
1419 else
1420 cur_wsd_attrib->Next = new_wsd_attrib;
1422 cur_wsd_attrib = new_wsd_attrib;
1425 cur_element = element;
1426 break;
1428 case WS_XML_NODE_TYPE_TEXT:
1429 text_node = (const WS_XML_TEXT_NODE *) node;
1431 if (cur_element == NULL)
1433 WARN("No parent element open but encountered text element!\n");
1434 continue;
1437 if (cur_element->FirstChild != NULL)
1439 WARN("Text node encountered but parent already has child!\n");
1440 continue;
1443 text = WSDAllocateLinkedMemory(element, sizeof(WSDXML_TEXT));
1444 if (text == NULL) goto outofmemory;
1446 text->Node.Parent = element;
1447 text->Node.Next = NULL;
1448 text->Node.Type = TextType;
1449 text->Text = xml_text_to_wide_string(text, text_node->text);
1451 if (text->Text == NULL)
1453 WARN("Text node returned null string.\n");
1454 WSDFreeLinkedMemory(text);
1455 continue;
1458 cur_element->FirstChild = (WSDXML_NODE *) text;
1459 break;
1461 case WS_XML_NODE_TYPE_END_ELEMENT:
1462 /* Go up a level to the parent element */
1463 cur_element = cur_element->Node.Parent;
1464 break;
1466 default:
1467 break;
1471 return S_OK;
1473 outofmemory:
1474 ret = E_OUTOFMEMORY;
1476 cleanup:
1477 /* Free uri and element_name if applicable */
1478 WSDFreeLinkedMemory(uri);
1479 WSDFreeLinkedMemory(name);
1480 return ret;
1483 static WSDXML_ELEMENT *find_element(WSDXML_ELEMENT *parent, LPCWSTR name, LPCWSTR ns_uri)
1485 WSDXML_ELEMENT *cur = (WSDXML_ELEMENT *) parent->FirstChild;
1487 while (cur != NULL)
1489 if ((lstrcmpW(cur->Name->LocalName, name) == 0) && (lstrcmpW(cur->Name->Space->Uri, ns_uri) == 0))
1490 return cur;
1492 cur = (WSDXML_ELEMENT *) cur->Node.Next;
1495 return NULL;
1498 static void remove_element(WSDXML_ELEMENT *element)
1500 WSDXML_NODE *cur;
1502 if (element == NULL)
1503 return;
1505 if (element->Node.Parent->FirstChild == (WSDXML_NODE *) element)
1506 element->Node.Parent->FirstChild = element->Node.Next;
1507 else
1509 cur = element->Node.Parent->FirstChild;
1511 while (cur != NULL)
1513 if (cur->Next == (WSDXML_NODE *) element)
1515 cur->Next = element->Node.Next;
1516 break;
1519 cur = cur->Next;
1523 WSDDetachLinkedMemory(element);
1524 WSDFreeLinkedMemory(element);
1527 static WSD_NAME_LIST *build_types_list_from_string(IWSDXMLContext *context, LPCWSTR buffer, void *parent)
1529 WSD_NAME_LIST *list = NULL, *cur_list = NULL, *prev_list = NULL;
1530 LPWSTR name_start = NULL, temp_buffer = NULL;
1531 LPCWSTR prefix_start = buffer;
1532 WSDXML_NAMESPACE *ns;
1533 WSDXML_NAME *name;
1534 int buffer_len, i;
1536 if (buffer == NULL)
1537 return NULL;
1539 temp_buffer = duplicate_string(parent, buffer);
1540 if (temp_buffer == NULL) goto cleanup;
1542 buffer_len = lstrlenW(temp_buffer);
1544 list = WSDAllocateLinkedMemory(parent, sizeof(WSD_NAME_LIST));
1545 if (list == NULL) goto cleanup;
1547 ZeroMemory(list, sizeof(WSD_NAME_LIST));
1548 prefix_start = temp_buffer;
1550 for (i = 0; i < buffer_len; i++)
1552 if (temp_buffer[i] == ':')
1554 temp_buffer[i] = 0;
1555 name_start = &temp_buffer[i + 1];
1557 else if ((temp_buffer[i] == ' ') || (i == buffer_len - 1))
1559 WSDXML_NAMESPACE *known_ns;
1561 if (temp_buffer[i] == ' ')
1562 temp_buffer[i] = 0;
1564 if (cur_list == NULL)
1565 cur_list = list;
1566 else
1568 cur_list = WSDAllocateLinkedMemory(parent, sizeof(WSD_NAME_LIST));
1569 if (cur_list == NULL) goto cleanup;
1571 prev_list->Next = cur_list;
1574 name = WSDAllocateLinkedMemory(cur_list, sizeof(WSDXML_NAME));
1575 if (name == NULL) goto cleanup;
1577 ns = WSDAllocateLinkedMemory(cur_list, sizeof(WSDXML_NAMESPACE));
1578 if (ns == NULL) goto cleanup;
1580 ZeroMemory(ns, sizeof(WSDXML_NAMESPACE));
1581 ns->PreferredPrefix = duplicate_string(ns, prefix_start);
1583 known_ns = xml_context_find_namespace_by_prefix(context, ns->PreferredPrefix);
1585 if (known_ns != NULL)
1586 ns->Uri = duplicate_string(ns, known_ns->Uri);
1588 name->Space = ns;
1589 name->LocalName = duplicate_string(name, name_start);
1591 cur_list->Element = name;
1592 prefix_start = &temp_buffer[i + 1];
1593 name_start = NULL;
1597 WSDFreeLinkedMemory(temp_buffer);
1598 return list;
1600 cleanup:
1601 WSDFreeLinkedMemory(list);
1602 WSDFreeLinkedMemory(temp_buffer);
1604 return NULL;
1607 static WSDXML_TYPE *generate_type(LPCWSTR uri, void *parent)
1609 WSDXML_TYPE *type = WSDAllocateLinkedMemory(parent, sizeof(WSDXML_TYPE));
1611 if (type == NULL)
1612 return NULL;
1614 type->Uri = duplicate_string(parent, uri);
1615 type->Table = NULL;
1617 return type;
1620 static BOOL is_duplicate_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id)
1622 struct message_id *msg_id, *msg_id_cursor;
1623 BOOL ret = FALSE;
1624 int len;
1626 EnterCriticalSection(&impl->message_ids_critical_section);
1628 LIST_FOR_EACH_ENTRY_SAFE(msg_id, msg_id_cursor, &impl->message_ids, struct message_id, entry)
1630 if (lstrcmpW(msg_id->id, id) == 0)
1632 ret = TRUE;
1633 goto end;
1637 msg_id = malloc(sizeof(*msg_id));
1638 if (!msg_id) goto end;
1640 len = (lstrlenW(id) + 1) * sizeof(WCHAR);
1641 msg_id->id = malloc(len);
1643 if (!msg_id->id)
1645 free(msg_id);
1646 goto end;
1649 memcpy(msg_id->id, id, len);
1650 list_add_tail(&impl->message_ids, &msg_id->entry);
1652 end:
1653 LeaveCriticalSection(&impl->message_ids_critical_section);
1654 return ret;
1657 HRESULT read_message(IWSDiscoveryPublisherImpl *impl, const char *xml, int xml_length, WSD_SOAP_MESSAGE **out_msg, int *msg_type)
1659 WSDXML_ELEMENT *envelope = NULL, *header_element, *appsequence_element, *body_element;
1660 WS_XML_READER_TEXT_ENCODING encoding;
1661 WS_XML_ELEMENT_NODE *envelope_node;
1662 WSD_SOAP_MESSAGE *soap_msg = NULL;
1663 WS_XML_READER_BUFFER_INPUT input;
1664 WS_XML_ATTRIBUTE *attrib = NULL;
1665 IWSDXMLContext *context = NULL;
1666 WS_XML_STRING *soap_uri = NULL;
1667 const WS_XML_NODE *node;
1668 WS_XML_READER *reader = NULL;
1669 LPCWSTR value = NULL;
1670 LPWSTR uri, prefix;
1671 WS_HEAP *heap = NULL;
1672 HRESULT ret;
1673 int i;
1675 *msg_type = MSGTYPE_UNKNOWN;
1677 ret = WsCreateHeap(16384, 4096, NULL, 0, &heap, NULL);
1678 if (FAILED(ret)) goto cleanup;
1680 ret = WsCreateReader(NULL, 0, &reader, NULL);
1681 if (FAILED(ret)) goto cleanup;
1683 encoding.encoding.encodingType = WS_XML_READER_ENCODING_TYPE_TEXT;
1684 encoding.charSet = WS_CHARSET_AUTO;
1686 input.input.inputType = WS_XML_READER_INPUT_TYPE_BUFFER;
1687 input.encodedData = (char *) xml;
1688 input.encodedDataSize = xml_length;
1690 ret = WsSetInput(reader, (WS_XML_READER_ENCODING *) &encoding, (WS_XML_READER_INPUT *) &input, NULL, 0, NULL);
1691 if (FAILED(ret)) goto cleanup;
1693 soap_uri = populate_xml_string(envelopeNsUri);
1694 if (soap_uri == NULL) goto outofmemory;
1696 ret = move_to_element(reader, "Envelope", soap_uri);
1697 if (FAILED(ret)) goto cleanup;
1699 ret = WsGetReaderNode(reader, &node, NULL);
1700 if (FAILED(ret)) goto cleanup;
1702 if (node->nodeType != WS_XML_NODE_TYPE_ELEMENT)
1704 WARN("Unexpected node type (%d)\n", node->nodeType);
1705 ret = E_FAIL;
1706 goto cleanup;
1709 envelope_node = (WS_XML_ELEMENT_NODE *) node;
1711 ret = WSDXMLCreateContext(&context);
1712 if (FAILED(ret)) goto cleanup;
1714 /* Find XML namespaces from the envelope element's attributes */
1715 for (i = 0; i < envelope_node->attributeCount; i++)
1717 attrib = envelope_node->attributes[i];
1719 if (attrib->isXmlNs)
1721 uri = utf8_to_wide(NULL, (const char *) attrib->ns->bytes, attrib->ns->length);
1722 if (uri == NULL) continue;
1724 trim_trailing_slash(uri);
1726 prefix = utf8_to_wide(uri, (const char *) attrib->localName->bytes, attrib->localName->length);
1728 if (prefix == NULL)
1730 WSDFreeLinkedMemory(uri);
1731 continue;
1734 IWSDXMLContext_AddNamespace(context, uri, prefix, NULL);
1735 WSDFreeLinkedMemory(uri);
1739 /* Create the SOAP message to return to the caller */
1740 soap_msg = WSDAllocateLinkedMemory(NULL, sizeof(WSD_SOAP_MESSAGE));
1741 if (soap_msg == NULL) goto outofmemory;
1743 ZeroMemory(soap_msg, sizeof(WSD_SOAP_MESSAGE));
1745 envelope = WSDAllocateLinkedMemory(soap_msg, sizeof(WSDXML_ELEMENT));
1746 if (envelope == NULL) goto outofmemory;
1748 ZeroMemory(envelope, sizeof(WSDXML_ELEMENT));
1750 ret = ws_element_to_wsdxml_element(reader, context, envelope);
1751 if (FAILED(ret)) goto cleanup;
1753 /* Find the header element */
1754 header_element = find_element(envelope, headerString, envelopeNsUri);
1756 if (header_element == NULL)
1758 WARN("Unable to find header element in received SOAP message\n");
1759 ret = E_FAIL;
1760 goto cleanup;
1763 ret = WSDXMLGetValueFromAny(addressingNsUri, actionString, (WSDXML_ELEMENT *) header_element->FirstChild, &value);
1764 if (FAILED(ret)) goto cleanup;
1765 soap_msg->Header.Action = duplicate_string(soap_msg, value);
1766 if (soap_msg->Header.Action == NULL) goto outofmemory;
1768 ret = WSDXMLGetValueFromAny(addressingNsUri, toString, (WSDXML_ELEMENT *) header_element->FirstChild, &value);
1769 if (FAILED(ret)) goto cleanup;
1770 soap_msg->Header.To = duplicate_string(soap_msg, value);
1771 if (soap_msg->Header.To == NULL) goto outofmemory;
1773 ret = WSDXMLGetValueFromAny(addressingNsUri, messageIdString, (WSDXML_ELEMENT *) header_element->FirstChild, &value);
1774 if (FAILED(ret)) goto cleanup;
1776 /* Detect duplicate messages */
1777 if (is_duplicate_message(impl, value))
1779 ret = E_FAIL;
1780 goto cleanup;
1783 soap_msg->Header.MessageID = duplicate_string(soap_msg, value);
1784 if (soap_msg->Header.MessageID == NULL) goto outofmemory;
1786 /* Look for optional AppSequence element */
1787 appsequence_element = find_element(header_element, appSequenceString, discoveryNsUri);
1789 if (appsequence_element != NULL)
1791 WSDXML_ATTRIBUTE *current_attrib;
1793 soap_msg->Header.AppSequence = WSDAllocateLinkedMemory(soap_msg, sizeof(WSD_APP_SEQUENCE));
1794 if (soap_msg->Header.AppSequence == NULL) goto outofmemory;
1796 ZeroMemory(soap_msg->Header.AppSequence, sizeof(WSD_APP_SEQUENCE));
1798 current_attrib = appsequence_element->FirstAttribute;
1800 while (current_attrib != NULL)
1802 if (lstrcmpW(current_attrib->Name->Space->Uri, discoveryNsUri) != 0)
1804 current_attrib = current_attrib->Next;
1805 continue;
1808 if (lstrcmpW(current_attrib->Name->LocalName, instanceIdString) == 0)
1810 ret = wide_text_to_ulonglong(current_attrib->Value, &soap_msg->Header.AppSequence->InstanceId);
1811 if (FAILED(ret)) goto cleanup;
1813 else if (lstrcmpW(current_attrib->Name->LocalName, messageNumberString) == 0)
1815 ret = wide_text_to_ulonglong(current_attrib->Value, &soap_msg->Header.AppSequence->MessageNumber);
1816 if (FAILED(ret)) goto cleanup;
1818 else if (lstrcmpW(current_attrib->Name->LocalName, sequenceIdString) == 0)
1820 soap_msg->Header.AppSequence->SequenceId = duplicate_string(soap_msg, current_attrib->Value);
1821 if (soap_msg->Header.AppSequence->SequenceId == NULL) goto outofmemory;
1824 current_attrib = current_attrib->Next;
1828 /* Now detach and free known headers to leave the "any" elements */
1829 remove_element(find_element(header_element, actionString, addressingNsUri));
1830 remove_element(find_element(header_element, toString, addressingNsUri));
1831 remove_element(find_element(header_element, messageIdString, addressingNsUri));
1832 remove_element(find_element(header_element, appSequenceString, discoveryNsUri));
1834 soap_msg->Header.AnyHeaders = (WSDXML_ELEMENT *) header_element->FirstChild;
1836 if (soap_msg->Header.AnyHeaders != NULL)
1837 soap_msg->Header.AnyHeaders->Node.Parent = NULL;
1839 /* Find the body element */
1840 body_element = find_element(envelope, bodyString, envelopeNsUri);
1842 if (body_element == NULL)
1844 WARN("Unable to find body element in received SOAP message\n");
1845 ret = E_FAIL;
1846 goto cleanup;
1849 /* Now figure out which message we've been sent */
1850 if (lstrcmpW(soap_msg->Header.Action, actionProbe) == 0)
1852 WSDXML_ELEMENT *probe_element;
1853 WSD_PROBE *probe = NULL;
1855 probe_element = find_element(body_element, probeString, discoveryNsUri);
1856 if (probe_element == NULL) goto cleanup;
1858 probe = WSDAllocateLinkedMemory(soap_msg, sizeof(WSD_PROBE));
1859 if (probe == NULL) goto cleanup;
1861 ZeroMemory(probe, sizeof(WSD_PROBE));
1863 /* Check for the "types" element */
1864 ret = WSDXMLGetValueFromAny(discoveryNsUri, typesString, (WSDXML_ELEMENT *) probe_element->FirstChild, &value);
1866 if (FAILED(ret))
1868 WARN("Unable to find Types element in received Probe message\n");
1869 goto cleanup;
1872 probe->Types = build_types_list_from_string(context, value, probe);
1874 /* Now detach and free known headers to leave the "any" elements */
1875 remove_element(find_element(probe_element, typesString, discoveryNsUri));
1876 remove_element(find_element(probe_element, scopesString, discoveryNsUri));
1878 probe->Any = (WSDXML_ELEMENT *) probe_element->FirstChild;
1880 if (probe->Any != NULL)
1881 probe->Any->Node.Parent = NULL;
1883 soap_msg->Body = probe;
1884 soap_msg->BodyType = generate_type(actionProbe, soap_msg);
1885 if (soap_msg->BodyType == NULL) goto cleanup;
1887 *out_msg = soap_msg;
1888 soap_msg = NULL; /* caller will clean this up */
1889 *msg_type = MSGTYPE_PROBE;
1892 goto cleanup;
1894 outofmemory:
1895 ret = E_OUTOFMEMORY;
1897 cleanup:
1898 free_xml_string(soap_uri);
1899 WSDFreeLinkedMemory(soap_msg);
1900 if (context != NULL) IWSDXMLContext_Release(context);
1901 if (reader != NULL) WsFreeReader(reader);
1902 if (heap != NULL) WsFreeHeap(heap);
1904 return ret;