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 };
67 static char *wide_to_utf8(LPCWSTR wide_string
, int *length
)
69 char *new_string
= NULL
;
71 if (wide_string
== NULL
)
74 *length
= WideCharToMultiByte(CP_UTF8
, 0, wide_string
, -1, NULL
, 0, NULL
, NULL
);
79 new_string
= heap_alloc(*length
);
80 WideCharToMultiByte(CP_UTF8
, 0, wide_string
, -1, new_string
, *length
, NULL
, NULL
);
85 static WS_XML_STRING
*populate_xml_string(LPCWSTR str
)
87 WS_XML_STRING
*xml
= heap_alloc_zero(sizeof(WS_XML_STRING
));
93 xml
->bytes
= (BYTE
*)wide_to_utf8(str
, &utf8Length
);
95 if (xml
->bytes
== NULL
)
101 xml
->dictionary
= NULL
;
103 xml
->length
= (xml
->bytes
== NULL
) ? 0 : (utf8Length
- 1);
108 static inline void free_xml_string(WS_XML_STRING
*value
)
113 if (value
->bytes
!= NULL
)
114 heap_free(value
->bytes
);
119 static BOOL
write_xml_element(WSDXML_ELEMENT
*element
, WS_XML_WRITER
*writer
)
121 WS_XML_STRING
*local_name
= NULL
, *element_ns
= NULL
, *ns_prefix
= NULL
;
122 WS_XML_UTF16_TEXT utf16_text
;
123 WSDXML_NODE
*current_child
;
124 WSDXML_TEXT
*node_as_text
;
132 /* Start the element */
133 local_name
= populate_xml_string(element
->Name
->LocalName
);
134 if (local_name
== NULL
) goto cleanup
;
136 element_ns
= populate_xml_string(element
->Name
->Space
->Uri
);
137 if (element_ns
== NULL
) goto cleanup
;
139 ns_prefix
= populate_xml_string(element
->Name
->Space
->PreferredPrefix
);
140 if (ns_prefix
== NULL
) goto cleanup
;
142 ret
= WsWriteStartElement(writer
, ns_prefix
, local_name
, element_ns
, NULL
);
143 if (FAILED(ret
)) goto cleanup
;
145 /* TODO: Write attributes */
147 /* Write child elements */
148 current_child
= element
->FirstChild
;
150 while (current_child
!= NULL
)
152 if (current_child
->Type
== ElementType
)
154 if (!write_xml_element((WSDXML_ELEMENT
*)current_child
, writer
)) goto cleanup
;
156 else if (current_child
->Type
== TextType
)
158 node_as_text
= (WSDXML_TEXT
*)current_child
;
159 text_len
= lstrlenW(node_as_text
->Text
);
161 utf16_text
.text
.textType
= WS_XML_TEXT_TYPE_UTF16
;
162 utf16_text
.byteCount
= min(WSD_MAX_TEXT_LENGTH
, text_len
) * sizeof(WCHAR
);
163 utf16_text
.bytes
= (BYTE
*)node_as_text
->Text
;
165 ret
= WsWriteText(writer
, (WS_XML_TEXT
*)&utf16_text
, NULL
);
166 if (FAILED(ret
)) goto cleanup
;
169 current_child
= current_child
->Next
;
172 /* End the element */
173 ret
= WsWriteEndElement(writer
, NULL
);
174 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 BOOL
create_guid(LPWSTR buffer
)
188 const WCHAR formatString
[] = { 'u','r','n',':','u','u','i','d',':','%','s', 0 };
190 WCHAR
* uuidString
= NULL
;
193 if (UuidCreate(&uuid
) != RPC_S_OK
)
196 UuidToStringW(&uuid
, (RPC_WSTR
*)&uuidString
);
198 if (uuidString
== NULL
)
201 wsprintfW(buffer
, formatString
, uuidString
);
202 RpcStringFreeW((RPC_WSTR
*)&uuidString
);
207 static void populate_soap_header(WSD_SOAP_HEADER
*header
, LPCWSTR to
, LPCWSTR action
, LPCWSTR message_id
,
208 WSD_APP_SEQUENCE
*sequence
, const WSDXML_ELEMENT
*any_headers
)
210 ZeroMemory(header
, sizeof(WSD_SOAP_HEADER
));
213 header
->Action
= action
;
214 header
->MessageID
= message_id
;
215 header
->AppSequence
= sequence
;
216 header
->AnyHeaders
= (WSDXML_ELEMENT
*)any_headers
;
218 /* TODO: Implement RelatesTo, ReplyTo, From, FaultTo */
221 static WSDXML_ELEMENT
*create_soap_header_xml_elements(IWSDXMLContext
*xml_context
, WSD_SOAP_HEADER
*header
)
223 /* TODO: Implement header generation */
227 static HRESULT
create_soap_envelope(IWSDXMLContext
*xml_context
, WSD_SOAP_HEADER
*header
, WSDXML_ELEMENT
*body_element
,
228 WS_HEAP
**heap
, char **output_xml
, ULONG
*xml_length
, struct list
*discovered_namespaces
)
230 WS_XML_STRING
*actual_envelope_prefix
= NULL
, *envelope_uri_xmlstr
= NULL
;
231 WSDXML_NAMESPACE
*addressing_ns
= NULL
, *discovery_ns
= NULL
, *envelope_ns
= NULL
;
232 WSDXML_ELEMENT
*header_element
= NULL
;
233 WS_XML_BUFFER
*buffer
= NULL
;
234 WS_XML_WRITER
*writer
= NULL
;
235 WS_XML_STRING envelope
;
236 HRESULT ret
= E_OUTOFMEMORY
;
237 static BYTE envelopeString
[] = "Envelope";
239 /* Create the necessary XML prefixes */
240 if (FAILED(IWSDXMLContext_AddNamespace(xml_context
, addressingNsUri
, addressingPrefix
, &addressing_ns
))) goto cleanup
;
242 if (FAILED(IWSDXMLContext_AddNamespace(xml_context
, discoveryNsUri
, discoveryPrefix
, &discovery_ns
))) goto cleanup
;
244 if (FAILED(IWSDXMLContext_AddNamespace(xml_context
, envelopeNsUri
, envelopePrefix
, &envelope_ns
))) goto cleanup
;
246 envelope
.bytes
= envelopeString
;
247 envelope
.length
= sizeof(envelopeString
) - 1;
248 envelope
.dictionary
= NULL
;
251 actual_envelope_prefix
= populate_xml_string(envelope_ns
->PreferredPrefix
);
252 envelope_uri_xmlstr
= populate_xml_string(envelope_ns
->Uri
);
254 if ((actual_envelope_prefix
== NULL
) || (envelope_uri_xmlstr
== NULL
)) goto cleanup
;
256 /* Now try to create the appropriate WebServices buffers, etc */
257 ret
= WsCreateHeap(16384, 4096, NULL
, 0, heap
, NULL
);
258 if (FAILED(ret
)) goto cleanup
;
260 ret
= WsCreateXmlBuffer(*heap
, NULL
, 0, &buffer
, NULL
);
261 if (FAILED(ret
)) goto cleanup
;
263 ret
= WsCreateWriter(NULL
, 0, &writer
, NULL
);
264 if (FAILED(ret
)) goto cleanup
;
266 ret
= WsSetOutputToBuffer(writer
, buffer
, NULL
, 0, NULL
);
267 if (FAILED(ret
)) goto cleanup
;
269 /* Create the header XML elements */
270 header_element
= create_soap_header_xml_elements(xml_context
, header
);
271 if (header_element
== NULL
) goto cleanup
;
274 ret
= WsWriteStartElement(writer
, actual_envelope_prefix
, &envelope
, envelope_uri_xmlstr
, NULL
);
275 if (FAILED(ret
)) goto cleanup
;
277 /* Write the header */
278 if (!write_xml_element(header_element
, writer
)) goto cleanup
;
280 ret
= WsWriteEndElement(writer
, NULL
);
281 if (FAILED(ret
)) goto cleanup
;
285 /* Generate the bytes of the document */
286 ret
= WsWriteXmlBufferToBytes(writer
, buffer
, NULL
, NULL
, 0, *heap
, (void**)output_xml
, xml_length
, NULL
);
287 if (FAILED(ret
)) goto cleanup
;
290 WSDFreeLinkedMemory(addressing_ns
);
291 WSDFreeLinkedMemory(discovery_ns
);
292 WSDFreeLinkedMemory(envelope_ns
);
294 WSDXMLCleanupElement(header_element
);
296 free_xml_string(actual_envelope_prefix
);
297 free_xml_string(envelope_uri_xmlstr
);
300 WsFreeWriter(writer
);
302 /* Don't free the heap unless the operation has failed */
303 if ((FAILED(ret
)) && (*heap
!= NULL
))
312 static HRESULT
write_and_send_message(IWSDiscoveryPublisherImpl
*impl
, WSD_SOAP_HEADER
*header
, WSDXML_ELEMENT
*body_element
,
313 struct list
*discovered_namespaces
, IWSDUdpAddress
*remote_address
, int max_initial_delay
)
315 static const char xml_header
[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
316 ULONG xml_length
= 0, xml_header_len
= sizeof(xml_header
) - 1;
317 WS_HEAP
*heap
= NULL
;
322 ret
= create_soap_envelope(impl
->xmlContext
, header
, NULL
, &heap
, &xml
, &xml_length
, NULL
);
323 if (ret
!= S_OK
) return ret
;
325 /* Prefix the XML header */
326 full_xml
= heap_alloc(xml_length
+ xml_header_len
+ 1);
328 if (full_xml
== NULL
)
331 return E_OUTOFMEMORY
;
334 memcpy(full_xml
, xml_header
, xml_header_len
);
335 memcpy(full_xml
+ xml_header_len
, xml
, xml_length
);
336 full_xml
[xml_length
+ xml_header_len
] = 0;
338 if (remote_address
== NULL
)
340 /* Send the message via UDP multicast */
341 ret
= send_udp_multicast(impl
, full_xml
, xml_length
+ xml_header_len
+ 1, max_initial_delay
) ? S_OK
: E_FAIL
;
345 /* TODO: Send the message via UDP unicast */
346 FIXME("TODO: Send the message via UDP unicast\n");
355 HRESULT
send_hello_message(IWSDiscoveryPublisherImpl
*impl
, LPCWSTR id
, ULONGLONG metadata_ver
, ULONGLONG instance_id
,
356 ULONGLONG msg_num
, LPCWSTR session_id
, const WSD_NAME_LIST
*types_list
, const WSD_URI_LIST
*scopes_list
,
357 const WSD_URI_LIST
*xaddrs_list
, const WSDXML_ELEMENT
*hdr_any
, const WSDXML_ELEMENT
*ref_param_any
,
358 const WSDXML_ELEMENT
*endpoint_ref_any
, const WSDXML_ELEMENT
*any
)
360 WSD_SOAP_HEADER soapHeader
;
361 WSD_APP_SEQUENCE sequence
;
362 WCHAR message_id
[64];
363 HRESULT ret
= E_OUTOFMEMORY
;
365 sequence
.InstanceId
= instance_id
;
366 sequence
.MessageNumber
= msg_num
;
367 sequence
.SequenceId
= session_id
;
369 if (!create_guid(message_id
)) goto cleanup
;
371 populate_soap_header(&soapHeader
, discoveryTo
, actionHello
, message_id
, &sequence
, hdr_any
);
373 /* TODO: Populate message body */
375 /* Write and send the message */
376 ret
= write_and_send_message(impl
, &soapHeader
, NULL
, NULL
, NULL
, APP_MAX_DELAY
);