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
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
[] = {
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
81 static char *wide_to_utf8(LPCWSTR wide_string
, int *length
)
83 char *new_string
= NULL
;
85 if (wide_string
== NULL
)
88 *length
= WideCharToMultiByte(CP_UTF8
, 0, wide_string
, -1, NULL
, 0, NULL
, NULL
);
93 new_string
= heap_alloc(*length
);
94 WideCharToMultiByte(CP_UTF8
, 0, wide_string
, -1, new_string
, *length
, NULL
, NULL
);
99 static WS_XML_STRING
*populate_xml_string(LPCWSTR str
)
101 WS_XML_STRING
*xml
= heap_alloc_zero(sizeof(WS_XML_STRING
));
107 xml
->bytes
= (BYTE
*)wide_to_utf8(str
, &utf8Length
);
109 if (xml
->bytes
== NULL
)
115 xml
->dictionary
= NULL
;
117 xml
->length
= (xml
->bytes
== NULL
) ? 0 : (utf8Length
- 1);
122 static inline void free_xml_string(WS_XML_STRING
*value
)
127 if (value
->bytes
!= NULL
)
128 heap_free(value
->bytes
);
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
;
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
;
193 free_xml_string(local_name
);
194 free_xml_string(element_ns
);
195 free_xml_string(ns_prefix
);
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
)))
209 if (FAILED(WSDXMLBuildAnyForSingleElement(name_obj
, text
, &element_obj
)))
211 WSDFreeLinkedMemory(name_obj
);
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
);
227 static BOOL
create_guid(LPWSTR buffer
)
229 const WCHAR formatString
[] = { 'u','r','n',':','u','u','i','d',':','%','s', 0 };
231 WCHAR
* uuidString
= NULL
;
234 if (UuidCreate(&uuid
) != RPC_S_OK
)
237 UuidToStringW(&uuid
, (RPC_WSTR
*)&uuidString
);
239 if (uuidString
== NULL
)
242 wsprintfW(buffer
, formatString
, uuidString
);
243 RpcStringFreeW((RPC_WSTR
*)&uuidString
);
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
));
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
));
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
))
283 list_add_tail(namespaces
, &ns
->entry
);
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
;
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
);
298 if (add_child_element(xml_context
, header_element
, addressingNsUri
, actionString
, header
->Action
) == NULL
)
302 if (add_child_element(xml_context
, header_element
, addressingNsUri
, messageIdString
, header
->MessageID
) == NULL
)
306 if (add_child_element(xml_context
, header_element
, addressingNsUri
, toString
, header
->To
) == NULL
)
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 */
332 return header_element
;
335 if (header_name
!= NULL
) WSDFreeLinkedMemory(header_name
);
336 WSDXMLCleanupElement(header_element
);
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
;
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
;
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
);
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
;
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
;
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
);
435 WsFreeWriter(writer
);
437 /* Don't free the heap unless the operation has failed */
438 if ((FAILED(ret
)) && (*heap
!= NULL
))
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
;
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
)
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
;
480 /* TODO: Send the message via UDP unicast */
481 FIXME("TODO: Send the message via UDP unicast\n");
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
);
520 WSDFreeLinkedMemory(discoveredNamespaces
);