wsdapi: Implement writing of XML elements.
[wine.git] / dlls / wsdapi / soap.c
blob834005eedb527574f00bf129d73c1fd748823f18
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 };
67 static char *wide_to_utf8(LPCWSTR wide_string, int *length)
69 char *new_string = NULL;
71 if (wide_string == NULL)
72 return NULL;
74 *length = WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, NULL, 0, NULL, NULL);
76 if (*length < 0)
77 return NULL;
79 new_string = heap_alloc(*length);
80 WideCharToMultiByte(CP_UTF8, 0, wide_string, -1, new_string, *length, NULL, NULL);
82 return new_string;
85 static WS_XML_STRING *populate_xml_string(LPCWSTR str)
87 WS_XML_STRING *xml = heap_alloc_zero(sizeof(WS_XML_STRING));
88 int utf8Length;
90 if (xml == NULL)
91 return NULL;
93 xml->bytes = (BYTE *)wide_to_utf8(str, &utf8Length);
95 if (xml->bytes == NULL)
97 heap_free(xml);
98 return NULL;
101 xml->dictionary = NULL;
102 xml->id = 0;
103 xml->length = (xml->bytes == NULL) ? 0 : (utf8Length - 1);
105 return xml;
108 static inline void free_xml_string(WS_XML_STRING *value)
110 if (value == NULL)
111 return;
113 if (value->bytes != NULL)
114 heap_free(value->bytes);
116 heap_free(value);
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;
125 BOOL retVal = FALSE;
126 int text_len;
127 HRESULT ret;
129 if (element == NULL)
130 return TRUE;
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;
176 retVal = TRUE;
178 cleanup:
179 free_xml_string(local_name);
180 free_xml_string(element_ns);
181 free_xml_string(ns_prefix);
183 return retVal;
186 static BOOL create_guid(LPWSTR buffer)
188 const WCHAR formatString[] = { 'u','r','n',':','u','u','i','d',':','%','s', 0 };
190 WCHAR* uuidString = NULL;
191 UUID uuid;
193 if (UuidCreate(&uuid) != RPC_S_OK)
194 return FALSE;
196 UuidToStringW(&uuid, (RPC_WSTR*)&uuidString);
198 if (uuidString == NULL)
199 return FALSE;
201 wsprintfW(buffer, formatString, uuidString);
202 RpcStringFreeW((RPC_WSTR*)&uuidString);
204 return TRUE;
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));
212 header->To = to;
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 */
224 return NULL;
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;
249 envelope.id = 0;
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;
273 /* <s:Envelope> */
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;
283 /* </s:Envelope> */
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;
289 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);
299 if (writer != NULL)
300 WsFreeWriter(writer);
302 /* Don't free the heap unless the operation has failed */
303 if ((FAILED(ret)) && (*heap != NULL))
305 WsFreeHeap(*heap);
306 *heap = NULL;
309 return ret;
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;
318 char *xml = NULL;
319 char *full_xml;
320 HRESULT ret;
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)
330 WsFreeHeap(heap);
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;
343 else
345 /* TODO: Send the message via UDP unicast */
346 FIXME("TODO: Send the message via UDP unicast\n");
349 heap_free(full_xml);
350 WsFreeHeap(heap);
352 return ret;
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);
378 cleanup:
379 return ret;