wsdapi: Store discovered namespaces and write xmlns attributes for them.
[wine.git] / dlls / wsdapi / soap.c
blob40edd154634cdca442f96299b4523c46fa779a19
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 emptyString[] = { 0 };
74 struct discovered_namespace
76 struct list entry;
77 LPCWSTR prefix;
78 LPCWSTR uri;
81 static char *wide_to_utf8(LPCWSTR wide_string, int *length)
83 char *new_string = NULL;
85 if (wide_string == NULL)
86 return NULL;
88 *length = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, NULL, 0, NULL, NULL);
90 if (*length < 0)
91 return NULL;
93 new_string = heap_alloc(*length);
94 WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, new_string, *length, NULL, NULL);
96 return new_string;
99 static WS_XML_STRING *populate_xml_string(LPCWSTR str)
101 WS_XML_STRING *xml = heap_alloc_zero(sizeof(WS_XML_STRING));
102 int utf8Length;
104 if (xml == NULL)
105 return NULL;
107 xml->bytes = (BYTE *)wide_to_utf8(str, &utf8Length);
109 if (xml->bytes == NULL)
111 heap_free(xml);
112 return NULL;
115 xml->dictionary = NULL;
116 xml->id = 0;
117 xml->length = (xml->bytes == NULL) ? 0 : (utf8Length - 1);
119 return xml;
122 static inline void free_xml_string(WS_XML_STRING *value)
124 if (value == NULL)
125 return;
127 if (value->bytes != NULL)
128 heap_free(value->bytes);
130 heap_free(value);
133 static BOOL write_xml_element(WSDXML_ELEMENT *element, WS_XML_WRITER *writer)
135 WS_XML_STRING *local_name = NULL, *element_ns = NULL, *ns_prefix = NULL;
136 WS_XML_UTF16_TEXT utf16_text;
137 WSDXML_NODE *current_child;
138 WSDXML_TEXT *node_as_text;
139 BOOL retVal = FALSE;
140 int text_len;
141 HRESULT ret;
143 if (element == NULL)
144 return TRUE;
146 /* Start the element */
147 local_name = populate_xml_string(element->Name->LocalName);
148 if (local_name == NULL) goto cleanup;
150 element_ns = populate_xml_string(element->Name->Space->Uri);
151 if (element_ns == NULL) goto cleanup;
153 ns_prefix = populate_xml_string(element->Name->Space->PreferredPrefix);
154 if (ns_prefix == NULL) goto cleanup;
156 ret = WsWriteStartElement(writer, ns_prefix, local_name, element_ns, NULL);
157 if (FAILED(ret)) goto cleanup;
159 /* TODO: Write attributes */
161 /* Write child elements */
162 current_child = element->FirstChild;
164 while (current_child != NULL)
166 if (current_child->Type == ElementType)
168 if (!write_xml_element((WSDXML_ELEMENT *)current_child, writer)) goto cleanup;
170 else if (current_child->Type == TextType)
172 node_as_text = (WSDXML_TEXT *)current_child;
173 text_len = lstrlenW(node_as_text->Text);
175 utf16_text.text.textType = WS_XML_TEXT_TYPE_UTF16;
176 utf16_text.byteCount = min(WSD_MAX_TEXT_LENGTH, text_len) * sizeof(WCHAR);
177 utf16_text.bytes = (BYTE *)node_as_text->Text;
179 ret = WsWriteText(writer, (WS_XML_TEXT *)&utf16_text, NULL);
180 if (FAILED(ret)) goto cleanup;
183 current_child = current_child->Next;
186 /* End the element */
187 ret = WsWriteEndElement(writer, NULL);
188 if (FAILED(ret)) goto cleanup;
190 retVal = TRUE;
192 cleanup:
193 free_xml_string(local_name);
194 free_xml_string(element_ns);
195 free_xml_string(ns_prefix);
197 return retVal;
200 static WSDXML_ELEMENT *add_child_element(IWSDXMLContext *xml_context, WSDXML_ELEMENT *parent, LPCWSTR ns_uri,
201 LPCWSTR name, LPCWSTR text)
203 WSDXML_ELEMENT *element_obj;
204 WSDXML_NAME *name_obj;
206 if (FAILED(IWSDXMLContext_AddNameToNamespace(xml_context, ns_uri, name, &name_obj)))
207 return NULL;
209 if (FAILED(WSDXMLBuildAnyForSingleElement(name_obj, text, &element_obj)))
211 WSDFreeLinkedMemory(name_obj);
212 return NULL;
215 WSDFreeLinkedMemory(name_obj);
217 /* Add the element as a child - this will link the element's memory allocation to the parent's */
218 if (FAILED(WSDXMLAddChild(parent, element_obj)))
220 WSDFreeLinkedMemory(element_obj);
221 return NULL;
224 return element_obj;
227 static BOOL create_guid(LPWSTR buffer)
229 const WCHAR formatString[] = { 'u','r','n',':','u','u','i','d',':','%','s', 0 };
231 WCHAR* uuidString = NULL;
232 UUID uuid;
234 if (UuidCreate(&uuid) != RPC_S_OK)
235 return FALSE;
237 UuidToStringW(&uuid, (RPC_WSTR*)&uuidString);
239 if (uuidString == NULL)
240 return FALSE;
242 wsprintfW(buffer, formatString, uuidString);
243 RpcStringFreeW((RPC_WSTR*)&uuidString);
245 return TRUE;
248 static void populate_soap_header(WSD_SOAP_HEADER *header, LPCWSTR to, LPCWSTR action, LPCWSTR message_id,
249 WSD_APP_SEQUENCE *sequence, const WSDXML_ELEMENT *any_headers)
251 ZeroMemory(header, sizeof(WSD_SOAP_HEADER));
253 header->To = to;
254 header->Action = action;
255 header->MessageID = message_id;
256 header->AppSequence = sequence;
257 header->AnyHeaders = (WSDXML_ELEMENT *)any_headers;
259 /* TODO: Implement RelatesTo, ReplyTo, From, FaultTo */
262 static BOOL add_discovered_namespace(struct list *namespaces, WSDXML_NAMESPACE *discovered_ns)
264 struct discovered_namespace *ns;
266 LIST_FOR_EACH_ENTRY(ns, namespaces, struct discovered_namespace, entry)
268 if (lstrcmpW(ns->uri, discovered_ns->Uri) == 0)
269 return TRUE; /* Already added */
272 ns = WSDAllocateLinkedMemory(namespaces, sizeof(struct discovered_namespace));
274 if (ns == NULL)
275 return FALSE;
277 ns->prefix = duplicate_string(ns, discovered_ns->PreferredPrefix);
278 ns->uri = duplicate_string(ns, discovered_ns->Uri);
280 if ((ns->prefix == NULL) || (ns->uri == NULL))
281 return FALSE;
283 list_add_tail(namespaces, &ns->entry);
284 return TRUE;
287 static WSDXML_ELEMENT *create_soap_header_xml_elements(IWSDXMLContext *xml_context, WSD_SOAP_HEADER *header)
289 WSDXML_ELEMENT *header_element = NULL, *app_sequence_element = NULL;
290 WSDXML_NAME *header_name = NULL;
292 /* <s:Header> */
293 if (FAILED(IWSDXMLContext_AddNameToNamespace(xml_context, envelopeNsUri, headerString, &header_name))) goto cleanup;
294 if (FAILED(WSDXMLBuildAnyForSingleElement(header_name, NULL, &header_element))) goto cleanup;
295 WSDFreeLinkedMemory(header_name);
297 /* <a:Action> */
298 if (add_child_element(xml_context, header_element, addressingNsUri, actionString, header->Action) == NULL)
299 goto cleanup;
301 /* <a:MessageId> */
302 if (add_child_element(xml_context, header_element, addressingNsUri, messageIdString, header->MessageID) == NULL)
303 goto cleanup;
305 /* <a:To> */
306 if (add_child_element(xml_context, header_element, addressingNsUri, toString, header->To) == NULL)
307 goto cleanup;
309 /* <a:RelatesTo> */
310 if (header->RelatesTo.MessageID != NULL)
312 if (add_child_element(xml_context, header_element, addressingNsUri, relatesToString,
313 header->RelatesTo.MessageID) == NULL) goto cleanup;
316 /* <d:AppSequence> */
317 app_sequence_element = add_child_element(xml_context, header_element, discoveryNsUri, appSequenceString, emptyString);
318 if (app_sequence_element == NULL) goto cleanup;
320 /* TODO: InstanceId attribute */
322 /* TODO: MessageNumber attribute */
324 /* TODO: SequenceID attribute */
326 /* </d:AppSequence> */
328 /* TODO: Write any headers */
330 /* </s:Header> */
332 return header_element;
334 cleanup:
335 if (header_name != NULL) WSDFreeLinkedMemory(header_name);
336 WSDXMLCleanupElement(header_element);
338 return NULL;
341 static HRESULT create_soap_envelope(IWSDXMLContext *xml_context, WSD_SOAP_HEADER *header, WSDXML_ELEMENT *body_element,
342 WS_HEAP **heap, char **output_xml, ULONG *xml_length, struct list *discovered_namespaces)
344 WS_XML_STRING *actual_envelope_prefix = NULL, *envelope_uri_xmlstr = NULL, *tmp_prefix = NULL, *tmp_uri = NULL;
345 WSDXML_NAMESPACE *addressing_ns = NULL, *discovery_ns = NULL, *envelope_ns = NULL;
346 WSDXML_ELEMENT *header_element = NULL;
347 struct discovered_namespace *ns;
348 WS_XML_BUFFER *buffer = NULL;
349 WS_XML_WRITER *writer = NULL;
350 WS_XML_STRING envelope;
351 HRESULT ret = E_OUTOFMEMORY;
352 static BYTE envelopeString[] = "Envelope";
354 /* Create the necessary XML prefixes */
355 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, addressingNsUri, addressingPrefix, &addressing_ns))) goto cleanup;
356 if (!add_discovered_namespace(discovered_namespaces, addressing_ns)) goto cleanup;
358 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, discoveryNsUri, discoveryPrefix, &discovery_ns))) goto cleanup;
359 if (!add_discovered_namespace(discovered_namespaces, discovery_ns)) goto cleanup;
361 if (FAILED(IWSDXMLContext_AddNamespace(xml_context, envelopeNsUri, envelopePrefix, &envelope_ns))) goto cleanup;
362 if (!add_discovered_namespace(discovered_namespaces, envelope_ns)) goto cleanup;
364 envelope.bytes = envelopeString;
365 envelope.length = sizeof(envelopeString) - 1;
366 envelope.dictionary = NULL;
367 envelope.id = 0;
369 actual_envelope_prefix = populate_xml_string(envelope_ns->PreferredPrefix);
370 envelope_uri_xmlstr = populate_xml_string(envelope_ns->Uri);
372 if ((actual_envelope_prefix == NULL) || (envelope_uri_xmlstr == NULL)) goto cleanup;
374 /* Now try to create the appropriate WebServices buffers, etc */
375 ret = WsCreateHeap(16384, 4096, NULL, 0, heap, NULL);
376 if (FAILED(ret)) goto cleanup;
378 ret = WsCreateXmlBuffer(*heap, NULL, 0, &buffer, NULL);
379 if (FAILED(ret)) goto cleanup;
381 ret = WsCreateWriter(NULL, 0, &writer, NULL);
382 if (FAILED(ret)) goto cleanup;
384 ret = WsSetOutputToBuffer(writer, buffer, NULL, 0, NULL);
385 if (FAILED(ret)) goto cleanup;
387 /* Create the header XML elements */
388 header_element = create_soap_header_xml_elements(xml_context, header);
389 if (header_element == NULL) goto cleanup;
391 /* <s:Envelope> */
392 ret = WsWriteStartElement(writer, actual_envelope_prefix, &envelope, envelope_uri_xmlstr, NULL);
393 if (FAILED(ret)) goto cleanup;
395 LIST_FOR_EACH_ENTRY(ns, discovered_namespaces, struct discovered_namespace, entry)
397 tmp_prefix = populate_xml_string(ns->prefix);
398 tmp_uri = populate_xml_string(ns->uri);
400 if ((tmp_prefix == NULL) || (tmp_uri == NULL)) goto cleanup;
402 ret = WsWriteXmlnsAttribute(writer, tmp_prefix, tmp_uri, FALSE, NULL);
403 if (FAILED(ret)) goto cleanup;
405 free_xml_string(tmp_prefix);
406 free_xml_string(tmp_uri);
409 tmp_prefix = NULL;
410 tmp_uri = NULL;
412 /* Write the header */
413 if (!write_xml_element(header_element, writer)) goto cleanup;
415 ret = WsWriteEndElement(writer, NULL);
416 if (FAILED(ret)) goto cleanup;
418 /* </s:Envelope> */
420 /* Generate the bytes of the document */
421 ret = WsWriteXmlBufferToBytes(writer, buffer, NULL, NULL, 0, *heap, (void**)output_xml, xml_length, NULL);
422 if (FAILED(ret)) goto cleanup;
424 cleanup:
425 WSDFreeLinkedMemory(addressing_ns);
426 WSDFreeLinkedMemory(discovery_ns);
427 WSDFreeLinkedMemory(envelope_ns);
429 WSDXMLCleanupElement(header_element);
431 free_xml_string(actual_envelope_prefix);
432 free_xml_string(envelope_uri_xmlstr);
434 if (writer != NULL)
435 WsFreeWriter(writer);
437 /* Don't free the heap unless the operation has failed */
438 if ((FAILED(ret)) && (*heap != NULL))
440 WsFreeHeap(*heap);
441 *heap = NULL;
444 return ret;
447 static HRESULT write_and_send_message(IWSDiscoveryPublisherImpl *impl, WSD_SOAP_HEADER *header, WSDXML_ELEMENT *body_element,
448 struct list *discovered_namespaces, IWSDUdpAddress *remote_address, int max_initial_delay)
450 static const char xml_header[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
451 ULONG xml_length = 0, xml_header_len = sizeof(xml_header) - 1;
452 WS_HEAP *heap = NULL;
453 char *xml = NULL;
454 char *full_xml;
455 HRESULT ret;
457 ret = create_soap_envelope(impl->xmlContext, header, NULL, &heap, &xml, &xml_length, discovered_namespaces);
458 if (ret != S_OK) return ret;
460 /* Prefix the XML header */
461 full_xml = heap_alloc(xml_length + xml_header_len + 1);
463 if (full_xml == NULL)
465 WsFreeHeap(heap);
466 return E_OUTOFMEMORY;
469 memcpy(full_xml, xml_header, xml_header_len);
470 memcpy(full_xml + xml_header_len, xml, xml_length);
471 full_xml[xml_length + xml_header_len] = 0;
473 if (remote_address == NULL)
475 /* Send the message via UDP multicast */
476 ret = send_udp_multicast(impl, full_xml, xml_length + xml_header_len + 1, max_initial_delay) ? S_OK : E_FAIL;
478 else
480 /* TODO: Send the message via UDP unicast */
481 FIXME("TODO: Send the message via UDP unicast\n");
484 heap_free(full_xml);
485 WsFreeHeap(heap);
487 return ret;
490 HRESULT send_hello_message(IWSDiscoveryPublisherImpl *impl, LPCWSTR id, ULONGLONG metadata_ver, ULONGLONG instance_id,
491 ULONGLONG msg_num, LPCWSTR session_id, const WSD_NAME_LIST *types_list, const WSD_URI_LIST *scopes_list,
492 const WSD_URI_LIST *xaddrs_list, const WSDXML_ELEMENT *hdr_any, const WSDXML_ELEMENT *ref_param_any,
493 const WSDXML_ELEMENT *endpoint_ref_any, const WSDXML_ELEMENT *any)
495 struct list *discoveredNamespaces = NULL;
496 WSD_SOAP_HEADER soapHeader;
497 WSD_APP_SEQUENCE sequence;
498 WCHAR message_id[64];
499 HRESULT ret = E_OUTOFMEMORY;
501 sequence.InstanceId = instance_id;
502 sequence.MessageNumber = msg_num;
503 sequence.SequenceId = session_id;
505 if (!create_guid(message_id)) goto cleanup;
507 discoveredNamespaces = WSDAllocateLinkedMemory(NULL, sizeof(struct list));
508 if (!discoveredNamespaces) goto cleanup;
510 list_init(discoveredNamespaces);
512 populate_soap_header(&soapHeader, discoveryTo, actionHello, message_id, &sequence, hdr_any);
514 /* TODO: Populate message body */
516 /* Write and send the message */
517 ret = write_and_send_message(impl, &soapHeader, NULL, discoveredNamespaces, NULL, APP_MAX_DELAY);
519 cleanup:
520 WSDFreeLinkedMemory(discoveredNamespaces);
522 return ret;