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 HRESULT
write_xml_attribute(WSDXML_ATTRIBUTE
*attribute
, 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 HRESULT ret
= E_OUTOFMEMORY
;
140 if (attribute
== NULL
)
143 /* Start the attribute */
144 local_name
= populate_xml_string(attribute
->Name
->LocalName
);
145 if (local_name
== NULL
) goto cleanup
;
147 if (attribute
->Name
->Space
== NULL
)
149 element_ns
= populate_xml_string(emptyString
);
150 if (element_ns
== NULL
) goto cleanup
;
156 element_ns
= populate_xml_string(attribute
->Name
->Space
->Uri
);
157 if (element_ns
== NULL
) goto cleanup
;
159 ns_prefix
= populate_xml_string(attribute
->Name
->Space
->PreferredPrefix
);
160 if (ns_prefix
== NULL
) goto cleanup
;
163 ret
= WsWriteStartAttribute(writer
, ns_prefix
, local_name
, element_ns
, FALSE
, NULL
);
164 if (FAILED(ret
)) goto cleanup
;
166 text_len
= lstrlenW(attribute
->Value
);
168 utf16_text
.text
.textType
= WS_XML_TEXT_TYPE_UTF16
;
169 utf16_text
.bytes
= (BYTE
*)attribute
->Value
;
170 utf16_text
.byteCount
= min(WSD_MAX_TEXT_LENGTH
, text_len
) * sizeof(WCHAR
);
172 ret
= WsWriteText(writer
, (WS_XML_TEXT
*)&utf16_text
, NULL
);
173 if (FAILED(ret
)) goto cleanup
;
175 ret
= WsWriteEndAttribute(writer
, NULL
);
176 if (FAILED(ret
)) goto cleanup
;
179 free_xml_string(local_name
);
180 free_xml_string(element_ns
);
181 free_xml_string(ns_prefix
);
186 static HRESULT
write_xml_element(WSDXML_ELEMENT
*element
, WS_XML_WRITER
*writer
)
188 WS_XML_STRING
*local_name
= NULL
, *element_ns
= NULL
, *ns_prefix
= NULL
;
189 WSDXML_ATTRIBUTE
*current_attribute
;
190 WS_XML_UTF16_TEXT utf16_text
;
191 WSDXML_NODE
*current_child
;
192 WSDXML_TEXT
*node_as_text
;
194 HRESULT ret
= E_OUTOFMEMORY
;
199 /* Start the element */
200 local_name
= populate_xml_string(element
->Name
->LocalName
);
201 if (local_name
== NULL
) goto cleanup
;
203 element_ns
= populate_xml_string(element
->Name
->Space
->Uri
);
204 if (element_ns
== NULL
) goto cleanup
;
206 ns_prefix
= populate_xml_string(element
->Name
->Space
->PreferredPrefix
);
207 if (ns_prefix
== NULL
) goto cleanup
;
209 ret
= WsWriteStartElement(writer
, ns_prefix
, local_name
, element_ns
, NULL
);
210 if (FAILED(ret
)) goto cleanup
;
212 /* Write attributes */
213 current_attribute
= element
->FirstAttribute
;
215 while (current_attribute
!= NULL
)
217 ret
= write_xml_attribute(current_attribute
, writer
);
218 if (FAILED(ret
)) goto cleanup
;
219 current_attribute
= current_attribute
->Next
;
222 /* Write child elements */
223 current_child
= element
->FirstChild
;
225 while (current_child
!= NULL
)
227 if (current_child
->Type
== ElementType
)
229 ret
= write_xml_element((WSDXML_ELEMENT
*)current_child
, writer
);
230 if (FAILED(ret
)) goto cleanup
;
232 else if (current_child
->Type
== TextType
)
234 node_as_text
= (WSDXML_TEXT
*)current_child
;
235 text_len
= lstrlenW(node_as_text
->Text
);
237 utf16_text
.text
.textType
= WS_XML_TEXT_TYPE_UTF16
;
238 utf16_text
.byteCount
= min(WSD_MAX_TEXT_LENGTH
, text_len
) * sizeof(WCHAR
);
239 utf16_text
.bytes
= (BYTE
*)node_as_text
->Text
;
241 ret
= WsWriteText(writer
, (WS_XML_TEXT
*)&utf16_text
, NULL
);
242 if (FAILED(ret
)) goto cleanup
;
245 current_child
= current_child
->Next
;
248 /* End the element */
249 ret
= WsWriteEndElement(writer
, NULL
);
252 free_xml_string(local_name
);
253 free_xml_string(element_ns
);
254 free_xml_string(ns_prefix
);
259 static WSDXML_ELEMENT
*add_child_element(IWSDXMLContext
*xml_context
, WSDXML_ELEMENT
*parent
, LPCWSTR ns_uri
,
260 LPCWSTR name
, LPCWSTR text
)
262 WSDXML_ELEMENT
*element_obj
;
263 WSDXML_NAME
*name_obj
;
265 if (FAILED(IWSDXMLContext_AddNameToNamespace(xml_context
, ns_uri
, name
, &name_obj
)))
268 if (FAILED(WSDXMLBuildAnyForSingleElement(name_obj
, text
, &element_obj
)))
270 WSDFreeLinkedMemory(name_obj
);
274 WSDFreeLinkedMemory(name_obj
);
276 /* Add the element as a child - this will link the element's memory allocation to the parent's */
277 if (FAILED(WSDXMLAddChild(parent
, element_obj
)))
279 WSDFreeLinkedMemory(element_obj
);
286 static BOOL
create_guid(LPWSTR buffer
)
288 const WCHAR formatString
[] = { 'u','r','n',':','u','u','i','d',':','%','s', 0 };
290 WCHAR
* uuidString
= NULL
;
293 if (UuidCreate(&uuid
) != RPC_S_OK
)
296 UuidToStringW(&uuid
, (RPC_WSTR
*)&uuidString
);
298 if (uuidString
== NULL
)
301 wsprintfW(buffer
, formatString
, uuidString
);
302 RpcStringFreeW((RPC_WSTR
*)&uuidString
);
307 static void populate_soap_header(WSD_SOAP_HEADER
*header
, LPCWSTR to
, LPCWSTR action
, LPCWSTR message_id
,
308 WSD_APP_SEQUENCE
*sequence
, const WSDXML_ELEMENT
*any_headers
)
310 ZeroMemory(header
, sizeof(WSD_SOAP_HEADER
));
313 header
->Action
= action
;
314 header
->MessageID
= message_id
;
315 header
->AppSequence
= sequence
;
316 header
->AnyHeaders
= (WSDXML_ELEMENT
*)any_headers
;
318 /* TODO: Implement RelatesTo, ReplyTo, From, FaultTo */
321 static BOOL
add_discovered_namespace(struct list
*namespaces
, WSDXML_NAMESPACE
*discovered_ns
)
323 struct discovered_namespace
*ns
;
325 LIST_FOR_EACH_ENTRY(ns
, namespaces
, struct discovered_namespace
, entry
)
327 if (lstrcmpW(ns
->uri
, discovered_ns
->Uri
) == 0)
328 return TRUE
; /* Already added */
331 ns
= WSDAllocateLinkedMemory(namespaces
, sizeof(struct discovered_namespace
));
336 ns
->prefix
= duplicate_string(ns
, discovered_ns
->PreferredPrefix
);
337 ns
->uri
= duplicate_string(ns
, discovered_ns
->Uri
);
339 if ((ns
->prefix
== NULL
) || (ns
->uri
== NULL
))
342 list_add_tail(namespaces
, &ns
->entry
);
346 static WSDXML_ELEMENT
*create_soap_header_xml_elements(IWSDXMLContext
*xml_context
, WSD_SOAP_HEADER
*header
)
348 WSDXML_ELEMENT
*header_element
= NULL
, *app_sequence_element
= NULL
;
349 WSDXML_NAME
*header_name
= NULL
;
352 if (FAILED(IWSDXMLContext_AddNameToNamespace(xml_context
, envelopeNsUri
, headerString
, &header_name
))) goto cleanup
;
353 if (FAILED(WSDXMLBuildAnyForSingleElement(header_name
, NULL
, &header_element
))) goto cleanup
;
354 WSDFreeLinkedMemory(header_name
);
357 if (add_child_element(xml_context
, header_element
, addressingNsUri
, actionString
, header
->Action
) == NULL
)
361 if (add_child_element(xml_context
, header_element
, addressingNsUri
, messageIdString
, header
->MessageID
) == NULL
)
365 if (add_child_element(xml_context
, header_element
, addressingNsUri
, toString
, header
->To
) == NULL
)
369 if (header
->RelatesTo
.MessageID
!= NULL
)
371 if (add_child_element(xml_context
, header_element
, addressingNsUri
, relatesToString
,
372 header
->RelatesTo
.MessageID
) == NULL
) goto cleanup
;
375 /* <d:AppSequence> */
376 app_sequence_element
= add_child_element(xml_context
, header_element
, discoveryNsUri
, appSequenceString
, emptyString
);
377 if (app_sequence_element
== NULL
) goto cleanup
;
379 /* TODO: InstanceId attribute */
381 /* TODO: MessageNumber attribute */
383 /* TODO: SequenceID attribute */
385 /* </d:AppSequence> */
387 /* TODO: Write any headers */
391 return header_element
;
394 if (header_name
!= NULL
) WSDFreeLinkedMemory(header_name
);
395 WSDXMLCleanupElement(header_element
);
400 static HRESULT
create_soap_envelope(IWSDXMLContext
*xml_context
, WSD_SOAP_HEADER
*header
, WSDXML_ELEMENT
*body_element
,
401 WS_HEAP
**heap
, char **output_xml
, ULONG
*xml_length
, struct list
*discovered_namespaces
)
403 WS_XML_STRING
*actual_envelope_prefix
= NULL
, *envelope_uri_xmlstr
= NULL
, *tmp_prefix
= NULL
, *tmp_uri
= NULL
;
404 WSDXML_NAMESPACE
*addressing_ns
= NULL
, *discovery_ns
= NULL
, *envelope_ns
= NULL
;
405 WSDXML_ELEMENT
*header_element
= NULL
;
406 struct discovered_namespace
*ns
;
407 WS_XML_BUFFER
*buffer
= NULL
;
408 WS_XML_WRITER
*writer
= NULL
;
409 WS_XML_STRING envelope
;
410 HRESULT ret
= E_OUTOFMEMORY
;
411 static BYTE envelopeString
[] = "Envelope";
413 /* Create the necessary XML prefixes */
414 if (FAILED(IWSDXMLContext_AddNamespace(xml_context
, addressingNsUri
, addressingPrefix
, &addressing_ns
))) goto cleanup
;
415 if (!add_discovered_namespace(discovered_namespaces
, addressing_ns
)) goto cleanup
;
417 if (FAILED(IWSDXMLContext_AddNamespace(xml_context
, discoveryNsUri
, discoveryPrefix
, &discovery_ns
))) goto cleanup
;
418 if (!add_discovered_namespace(discovered_namespaces
, discovery_ns
)) goto cleanup
;
420 if (FAILED(IWSDXMLContext_AddNamespace(xml_context
, envelopeNsUri
, envelopePrefix
, &envelope_ns
))) goto cleanup
;
421 if (!add_discovered_namespace(discovered_namespaces
, envelope_ns
)) goto cleanup
;
423 envelope
.bytes
= envelopeString
;
424 envelope
.length
= sizeof(envelopeString
) - 1;
425 envelope
.dictionary
= NULL
;
428 actual_envelope_prefix
= populate_xml_string(envelope_ns
->PreferredPrefix
);
429 envelope_uri_xmlstr
= populate_xml_string(envelope_ns
->Uri
);
431 if ((actual_envelope_prefix
== NULL
) || (envelope_uri_xmlstr
== NULL
)) goto cleanup
;
433 /* Now try to create the appropriate WebServices buffers, etc */
434 ret
= WsCreateHeap(16384, 4096, NULL
, 0, heap
, NULL
);
435 if (FAILED(ret
)) goto cleanup
;
437 ret
= WsCreateXmlBuffer(*heap
, NULL
, 0, &buffer
, NULL
);
438 if (FAILED(ret
)) goto cleanup
;
440 ret
= WsCreateWriter(NULL
, 0, &writer
, NULL
);
441 if (FAILED(ret
)) goto cleanup
;
443 ret
= WsSetOutputToBuffer(writer
, buffer
, NULL
, 0, NULL
);
444 if (FAILED(ret
)) goto cleanup
;
446 /* Create the header XML elements */
447 header_element
= create_soap_header_xml_elements(xml_context
, header
);
448 if (header_element
== NULL
) goto cleanup
;
451 ret
= WsWriteStartElement(writer
, actual_envelope_prefix
, &envelope
, envelope_uri_xmlstr
, NULL
);
452 if (FAILED(ret
)) goto cleanup
;
454 LIST_FOR_EACH_ENTRY(ns
, discovered_namespaces
, struct discovered_namespace
, entry
)
456 tmp_prefix
= populate_xml_string(ns
->prefix
);
457 tmp_uri
= populate_xml_string(ns
->uri
);
459 if ((tmp_prefix
== NULL
) || (tmp_uri
== NULL
)) goto cleanup
;
461 ret
= WsWriteXmlnsAttribute(writer
, tmp_prefix
, tmp_uri
, FALSE
, NULL
);
462 if (FAILED(ret
)) goto cleanup
;
464 free_xml_string(tmp_prefix
);
465 free_xml_string(tmp_uri
);
471 /* Write the header */
472 ret
= write_xml_element(header_element
, writer
);
473 if (FAILED(ret
)) goto cleanup
;
475 ret
= WsWriteEndElement(writer
, NULL
);
476 if (FAILED(ret
)) goto cleanup
;
480 /* Generate the bytes of the document */
481 ret
= WsWriteXmlBufferToBytes(writer
, buffer
, NULL
, NULL
, 0, *heap
, (void**)output_xml
, xml_length
, NULL
);
482 if (FAILED(ret
)) goto cleanup
;
485 WSDFreeLinkedMemory(addressing_ns
);
486 WSDFreeLinkedMemory(discovery_ns
);
487 WSDFreeLinkedMemory(envelope_ns
);
489 WSDXMLCleanupElement(header_element
);
491 free_xml_string(actual_envelope_prefix
);
492 free_xml_string(envelope_uri_xmlstr
);
495 WsFreeWriter(writer
);
497 /* Don't free the heap unless the operation has failed */
498 if ((FAILED(ret
)) && (*heap
!= NULL
))
507 static HRESULT
write_and_send_message(IWSDiscoveryPublisherImpl
*impl
, WSD_SOAP_HEADER
*header
, WSDXML_ELEMENT
*body_element
,
508 struct list
*discovered_namespaces
, IWSDUdpAddress
*remote_address
, int max_initial_delay
)
510 static const char xml_header
[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
511 ULONG xml_length
= 0, xml_header_len
= sizeof(xml_header
) - 1;
512 WS_HEAP
*heap
= NULL
;
517 ret
= create_soap_envelope(impl
->xmlContext
, header
, NULL
, &heap
, &xml
, &xml_length
, discovered_namespaces
);
518 if (ret
!= S_OK
) return ret
;
520 /* Prefix the XML header */
521 full_xml
= heap_alloc(xml_length
+ xml_header_len
+ 1);
523 if (full_xml
== NULL
)
526 return E_OUTOFMEMORY
;
529 memcpy(full_xml
, xml_header
, xml_header_len
);
530 memcpy(full_xml
+ xml_header_len
, xml
, xml_length
);
531 full_xml
[xml_length
+ xml_header_len
] = 0;
533 if (remote_address
== NULL
)
535 /* Send the message via UDP multicast */
536 ret
= send_udp_multicast(impl
, full_xml
, xml_length
+ xml_header_len
+ 1, max_initial_delay
) ? S_OK
: E_FAIL
;
540 /* TODO: Send the message via UDP unicast */
541 FIXME("TODO: Send the message via UDP unicast\n");
550 HRESULT
send_hello_message(IWSDiscoveryPublisherImpl
*impl
, LPCWSTR id
, ULONGLONG metadata_ver
, ULONGLONG instance_id
,
551 ULONGLONG msg_num
, LPCWSTR session_id
, const WSD_NAME_LIST
*types_list
, const WSD_URI_LIST
*scopes_list
,
552 const WSD_URI_LIST
*xaddrs_list
, const WSDXML_ELEMENT
*hdr_any
, const WSDXML_ELEMENT
*ref_param_any
,
553 const WSDXML_ELEMENT
*endpoint_ref_any
, const WSDXML_ELEMENT
*any
)
555 struct list
*discoveredNamespaces
= NULL
;
556 WSD_SOAP_HEADER soapHeader
;
557 WSD_APP_SEQUENCE sequence
;
558 WCHAR message_id
[64];
559 HRESULT ret
= E_OUTOFMEMORY
;
561 sequence
.InstanceId
= instance_id
;
562 sequence
.MessageNumber
= msg_num
;
563 sequence
.SequenceId
= session_id
;
565 if (!create_guid(message_id
)) goto cleanup
;
567 discoveredNamespaces
= WSDAllocateLinkedMemory(NULL
, sizeof(struct list
));
568 if (!discoveredNamespaces
) goto cleanup
;
570 list_init(discoveredNamespaces
);
572 populate_soap_header(&soapHeader
, discoveryTo
, actionHello
, message_id
, &sequence
, hdr_any
);
574 /* TODO: Populate message body */
576 /* Write and send the message */
577 ret
= write_and_send_message(impl
, &soapHeader
, NULL
, discoveredNamespaces
, NULL
, APP_MAX_DELAY
);
580 WSDFreeLinkedMemory(discoveredNamespaces
);