wsdapi/tests: Add test for Hello message Any body element.
[wine.git] / dlls / wsdapi / tests / discovery.c
blob608edb557bbb5584df281192edfc4d4692ddaed2
1 /*
2 * Web Services on Devices
3 * Discovery tests
5 * Copyright 2017-2018 Owen Rudge for CodeWeavers
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #define COBJMACROS
24 #include <winsock2.h>
25 #include <ws2tcpip.h>
26 #include <windows.h>
28 #include "wine/test.h"
29 #include "wine/heap.h"
30 #include "initguid.h"
31 #include "objbase.h"
32 #include "wsdapi.h"
33 #include <netfw.h>
34 #include <iphlpapi.h>
35 #include <stdio.h>
37 #define SEND_ADDRESS_IPV4 "239.255.255.250"
38 #define SEND_ADDRESS_IPV6 "FF02::C"
39 #define SEND_PORT "3702"
41 static const char *publisherId = "urn:uuid:3AE5617D-790F-408A-9374-359A77F924A3";
42 static const char *sequenceId = "urn:uuid:b14de351-72fc-4453-96f9-e58b0c9faf38";
44 #define MAX_CACHED_MESSAGES 5
45 #define MAX_LISTENING_THREADS 20
47 typedef struct messageStorage {
48 BOOL running;
49 CRITICAL_SECTION criticalSection;
50 char* messages[MAX_CACHED_MESSAGES];
51 int messageCount;
52 HANDLE threadHandles[MAX_LISTENING_THREADS];
53 int numThreadHandles;
54 } messageStorage;
56 static LPWSTR utf8_to_wide(const char *utf8String)
58 int sizeNeeded = 0, utf8StringLength = 0, memLength = 0;
59 LPWSTR newString = NULL;
61 if (utf8String == NULL) return NULL;
62 utf8StringLength = lstrlenA(utf8String);
64 sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, utf8String, utf8StringLength, NULL, 0);
65 if (sizeNeeded <= 0) return NULL;
67 memLength = sizeof(WCHAR) * (sizeNeeded + 1);
68 newString = heap_alloc_zero(memLength);
70 MultiByteToWideChar(CP_UTF8, 0, utf8String, utf8StringLength, newString, sizeNeeded);
71 return newString;
74 static int join_multicast_group(SOCKET s, struct addrinfo *group, struct addrinfo *iface)
76 int level, optname, optlen;
77 struct ipv6_mreq mreqv6;
78 struct ip_mreq mreqv4;
79 char *optval;
81 if (group->ai_family == AF_INET6)
83 level = IPPROTO_IPV6;
84 optname = IPV6_ADD_MEMBERSHIP;
85 optval = (char *)&mreqv6;
86 optlen = sizeof(mreqv6);
88 mreqv6.ipv6mr_multiaddr = ((SOCKADDR_IN6 *)group->ai_addr)->sin6_addr;
89 mreqv6.ipv6mr_interface = ((SOCKADDR_IN6 *)iface->ai_addr)->sin6_scope_id;
91 else
93 level = IPPROTO_IP;
94 optname = IP_ADD_MEMBERSHIP;
95 optval = (char *)&mreqv4;
96 optlen = sizeof(mreqv4);
98 mreqv4.imr_multiaddr.s_addr = ((SOCKADDR_IN *)group->ai_addr)->sin_addr.s_addr;
99 mreqv4.imr_interface.s_addr = ((SOCKADDR_IN *)iface->ai_addr)->sin_addr.s_addr;
102 return setsockopt(s, level, optname, optval, optlen);
105 static int set_send_interface(SOCKET s, struct addrinfo *iface)
107 int level, optname, optlen;
108 char *optval = NULL;
110 if (iface->ai_family == AF_INET6)
112 level = IPPROTO_IPV6;
113 optname = IPV6_MULTICAST_IF;
114 optval = (char *) &((SOCKADDR_IN6 *)iface->ai_addr)->sin6_scope_id;
115 optlen = sizeof(((SOCKADDR_IN6 *)iface->ai_addr)->sin6_scope_id);
117 else
119 level = IPPROTO_IP;
120 optname = IP_MULTICAST_IF;
121 optval = (char *) &((SOCKADDR_IN *)iface->ai_addr)->sin_addr.s_addr;
122 optlen = sizeof(((SOCKADDR_IN *)iface->ai_addr)->sin_addr.s_addr);
125 return setsockopt(s, level, optname, optval, optlen);
128 static struct addrinfo *resolve_address(const char *address, const char *port, int family, int type, int protocol)
130 struct addrinfo hints, *result = NULL;
132 ZeroMemory(&hints, sizeof(hints));
134 hints.ai_flags = AI_PASSIVE;
135 hints.ai_family = family;
136 hints.ai_socktype = type;
137 hints.ai_protocol = protocol;
139 return getaddrinfo(address, port, &hints, &result) == 0 ? result : NULL;
142 typedef struct listenerThreadParams
144 messageStorage *msgStorage;
145 SOCKET listeningSocket;
146 } listenerThreadParams;
148 #define RECEIVE_BUFFER_SIZE 65536
150 static DWORD WINAPI listening_thread(LPVOID lpParam)
152 listenerThreadParams *parameter = (listenerThreadParams *)lpParam;
153 messageStorage *msgStorage = parameter->msgStorage;
154 int bytesReceived;
155 char *buffer;
157 buffer = heap_alloc(RECEIVE_BUFFER_SIZE);
159 while (parameter->msgStorage->running)
161 ZeroMemory(buffer, RECEIVE_BUFFER_SIZE);
162 bytesReceived = recv(parameter->listeningSocket, buffer, RECEIVE_BUFFER_SIZE, 0);
164 if (bytesReceived == SOCKET_ERROR)
166 if (WSAGetLastError() != WSAETIMEDOUT)
167 return 0;
169 else
171 EnterCriticalSection(&msgStorage->criticalSection);
173 if (msgStorage->messageCount < MAX_CACHED_MESSAGES)
175 msgStorage->messages[msgStorage->messageCount] = heap_alloc(bytesReceived);
177 if (msgStorage->messages[msgStorage->messageCount] != NULL)
179 memcpy(msgStorage->messages[msgStorage->messageCount], buffer, bytesReceived);
180 msgStorage->messageCount++;
184 LeaveCriticalSection(&msgStorage->criticalSection);
186 if (msgStorage->messageCount >= MAX_CACHED_MESSAGES)
188 /* Stop all threads */
189 msgStorage->running = FALSE;
190 break;
195 closesocket(parameter->listeningSocket);
197 heap_free(buffer);
198 heap_free(parameter);
200 return 0;
203 static void start_listening(messageStorage *msgStorage, const char *multicastAddress, const char *bindAddress)
205 struct addrinfo *multicastAddr = NULL, *bindAddr = NULL, *interfaceAddr = NULL;
206 listenerThreadParams *parameter = NULL;
207 const DWORD receiveTimeout = 5000;
208 const UINT reuseAddr = 1;
209 HANDLE hThread;
210 SOCKET s = 0;
212 /* Resolve the multicast address */
213 multicastAddr = resolve_address(multicastAddress, SEND_PORT, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP);
214 if (multicastAddr == NULL) goto cleanup;
216 /* Resolve the binding address */
217 bindAddr = resolve_address(bindAddress, SEND_PORT, multicastAddr->ai_family, multicastAddr->ai_socktype, multicastAddr->ai_protocol);
218 if (bindAddr == NULL) goto cleanup;
220 /* Resolve the multicast interface */
221 interfaceAddr = resolve_address(bindAddress, "0", multicastAddr->ai_family, multicastAddr->ai_socktype, multicastAddr->ai_protocol);
222 if (interfaceAddr == NULL) goto cleanup;
224 /* Create the socket */
225 s = socket(multicastAddr->ai_family, multicastAddr->ai_socktype, multicastAddr->ai_protocol);
226 if (s == INVALID_SOCKET) goto cleanup;
228 /* Ensure the socket can be reused */
229 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseAddr, sizeof(reuseAddr)) == SOCKET_ERROR) goto cleanup;
231 /* Bind the socket to the local interface so we can receive data */
232 if (bind(s, bindAddr->ai_addr, bindAddr->ai_addrlen) == SOCKET_ERROR) goto cleanup;
234 /* Join the multicast group */
235 if (join_multicast_group(s, multicastAddr, interfaceAddr) == SOCKET_ERROR) goto cleanup;
237 /* Set the outgoing interface */
238 if (set_send_interface(s, interfaceAddr) == SOCKET_ERROR) goto cleanup;
240 /* For IPv6, ensure the scope ID is zero */
241 if (multicastAddr->ai_family == AF_INET6)
242 ((SOCKADDR_IN6 *)multicastAddr->ai_addr)->sin6_scope_id = 0;
244 /* Set a 5-second receive timeout */
245 if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char *)&receiveTimeout, sizeof(receiveTimeout)) == SOCKET_ERROR) goto cleanup;
247 /* Allocate memory for thread parameters */
248 parameter = heap_alloc(sizeof(listenerThreadParams));
250 parameter->msgStorage = msgStorage;
251 parameter->listeningSocket = s;
253 hThread = CreateThread(NULL, 0, listening_thread, parameter, 0, NULL);
254 if (hThread == NULL) goto cleanup;
256 msgStorage->threadHandles[msgStorage->numThreadHandles] = hThread;
257 msgStorage->numThreadHandles++;
259 goto cleanup_addresses;
261 cleanup:
262 closesocket(s);
263 heap_free(parameter);
265 cleanup_addresses:
266 freeaddrinfo(multicastAddr);
267 freeaddrinfo(bindAddr);
268 freeaddrinfo(interfaceAddr);
271 static BOOL start_listening_on_all_addresses(messageStorage *msgStorage, ULONG family)
273 IP_ADAPTER_ADDRESSES *adapterAddresses = NULL, *adapterAddress;
274 ULONG bufferSize = 0;
275 LPSOCKADDR sockaddr;
276 DWORD addressLength;
277 char address[64];
278 BOOL ret = FALSE;
279 ULONG retVal;
281 retVal = GetAdaptersAddresses(family, 0, NULL, NULL, &bufferSize); /* family should be AF_INET or AF_INET6 */
282 if (retVal != ERROR_BUFFER_OVERFLOW) goto cleanup;
284 /* Get size of buffer for adapters */
285 adapterAddresses = (IP_ADAPTER_ADDRESSES *)heap_alloc(bufferSize);
286 if (adapterAddresses == NULL) goto cleanup;
288 /* Get list of adapters */
289 retVal = GetAdaptersAddresses(family, 0, NULL, adapterAddresses, &bufferSize);
290 if (retVal != ERROR_SUCCESS) goto cleanup;
292 for (adapterAddress = adapterAddresses; adapterAddress != NULL; adapterAddress = adapterAddress->Next)
294 if (msgStorage->numThreadHandles >= MAX_LISTENING_THREADS)
296 ret = TRUE;
297 goto cleanup;
300 if (adapterAddress->FirstUnicastAddress == NULL) continue;
302 sockaddr = adapterAddress->FirstUnicastAddress->Address.lpSockaddr;
303 addressLength = sizeof(address);
304 WSAAddressToStringA(sockaddr, adapterAddress->FirstUnicastAddress->Address.iSockaddrLength, NULL, address, &addressLength);
306 start_listening(msgStorage, adapterAddress->FirstUnicastAddress->Address.lpSockaddr->sa_family == AF_INET ? SEND_ADDRESS_IPV4 : SEND_ADDRESS_IPV6, address);
309 ret = TRUE;
311 cleanup:
312 heap_free(adapterAddresses);
313 return ret;
316 typedef struct IWSDiscoveryPublisherNotifyImpl {
317 IWSDiscoveryPublisherNotify IWSDiscoveryPublisherNotify_iface;
318 LONG ref;
319 } IWSDiscoveryPublisherNotifyImpl;
321 static inline IWSDiscoveryPublisherNotifyImpl *impl_from_IWSDiscoveryPublisherNotify(IWSDiscoveryPublisherNotify *iface)
323 return CONTAINING_RECORD(iface, IWSDiscoveryPublisherNotifyImpl, IWSDiscoveryPublisherNotify_iface);
326 static HRESULT WINAPI IWSDiscoveryPublisherNotifyImpl_QueryInterface(IWSDiscoveryPublisherNotify *iface, REFIID riid, void **ppv)
328 IWSDiscoveryPublisherNotifyImpl *This = impl_from_IWSDiscoveryPublisherNotify(iface);
330 if (!ppv)
332 return E_INVALIDARG;
335 *ppv = NULL;
337 if (IsEqualIID(riid, &IID_IUnknown) ||
338 IsEqualIID(riid, &IID_IWSDiscoveryPublisherNotify))
340 *ppv = &This->IWSDiscoveryPublisherNotify_iface;
342 else
344 return E_NOINTERFACE;
347 IUnknown_AddRef((IUnknown*)*ppv);
348 return S_OK;
351 static ULONG WINAPI IWSDiscoveryPublisherNotifyImpl_AddRef(IWSDiscoveryPublisherNotify *iface)
353 IWSDiscoveryPublisherNotifyImpl *This = impl_from_IWSDiscoveryPublisherNotify(iface);
354 ULONG ref = InterlockedIncrement(&This->ref);
356 trace("IWSDiscoveryPublisherNotifyImpl_AddRef called (%p, ref = %d)\n", This, ref);
357 return ref;
360 static ULONG WINAPI IWSDiscoveryPublisherNotifyImpl_Release(IWSDiscoveryPublisherNotify *iface)
362 IWSDiscoveryPublisherNotifyImpl *This = impl_from_IWSDiscoveryPublisherNotify(iface);
363 ULONG ref = InterlockedDecrement(&This->ref);
365 trace("IWSDiscoveryPublisherNotifyImpl_Release called (%p, ref = %d)\n", This, ref);
367 if (ref == 0)
369 HeapFree(GetProcessHeap(), 0, This);
372 return ref;
375 static HRESULT WINAPI IWSDiscoveryPublisherNotifyImpl_ProbeHandler(IWSDiscoveryPublisherNotify *This, const WSD_SOAP_MESSAGE *pSoap, IWSDMessageParameters *pMessageParameters)
377 trace("IWSDiscoveryPublisherNotifyImpl_ProbeHandler called (%p, %p, %p)\n", This, pSoap, pMessageParameters);
378 return S_OK;
381 static HRESULT WINAPI IWSDiscoveryPublisherNotifyImpl_ResolveHandler(IWSDiscoveryPublisherNotify *This, const WSD_SOAP_MESSAGE *pSoap, IWSDMessageParameters *pMessageParameters)
383 trace("IWSDiscoveryPublisherNotifyImpl_ResolveHandler called (%p, %p, %p)\n", This, pSoap, pMessageParameters);
384 return S_OK;
387 static const IWSDiscoveryPublisherNotifyVtbl publisherNotify_vtbl =
389 IWSDiscoveryPublisherNotifyImpl_QueryInterface,
390 IWSDiscoveryPublisherNotifyImpl_AddRef,
391 IWSDiscoveryPublisherNotifyImpl_Release,
392 IWSDiscoveryPublisherNotifyImpl_ProbeHandler,
393 IWSDiscoveryPublisherNotifyImpl_ResolveHandler
396 static BOOL create_discovery_publisher_notify(IWSDiscoveryPublisherNotify **publisherNotify)
398 IWSDiscoveryPublisherNotifyImpl *obj;
400 *publisherNotify = NULL;
402 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj));
404 if (!obj)
406 trace("Out of memory creating IWSDiscoveryPublisherNotify\n");
407 return FALSE;
410 obj->IWSDiscoveryPublisherNotify_iface.lpVtbl = &publisherNotify_vtbl;
411 obj->ref = 1;
413 *publisherNotify = &obj->IWSDiscoveryPublisherNotify_iface;
415 return TRUE;
418 static void CreateDiscoveryPublisher_tests(void)
420 IWSDiscoveryPublisher *publisher = NULL;
421 IWSDiscoveryPublisher *publisher2;
422 IUnknown *unknown;
423 HRESULT rc;
424 ULONG ref;
426 rc = WSDCreateDiscoveryPublisher(NULL, NULL);
427 ok((rc == E_POINTER) || (rc == E_INVALIDARG), "WSDCreateDiscoveryPublisher(NULL, NULL) failed: %08x\n", rc);
429 rc = WSDCreateDiscoveryPublisher(NULL, &publisher);
430 ok(rc == S_OK, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: %08x\n", rc);
431 ok(publisher != NULL, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: publisher == NULL\n");
433 /* Try to query for objects */
434 rc = IWSDiscoveryPublisher_QueryInterface(publisher, &IID_IUnknown, (LPVOID*)&unknown);
435 ok(rc == S_OK,"IWSDiscoveryPublisher_QueryInterface(IID_IUnknown) failed: %08x\n", rc);
437 if (rc == S_OK)
438 IUnknown_Release(unknown);
440 rc = IWSDiscoveryPublisher_QueryInterface(publisher, &IID_IWSDiscoveryPublisher, (LPVOID*)&publisher2);
441 ok(rc == S_OK,"IWSDiscoveryPublisher_QueryInterface(IID_IWSDiscoveryPublisher) failed: %08x\n", rc);
443 if (rc == S_OK)
444 IWSDiscoveryPublisher_Release(publisher2);
446 ref = IWSDiscoveryPublisher_Release(publisher);
447 ok(ref == 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref);
450 static void CreateDiscoveryPublisher_XMLContext_tests(void)
452 IWSDiscoveryPublisher *publisher = NULL;
453 IWSDXMLContext *xmlContext, *returnedContext;
454 HRESULT rc;
455 int ref;
457 /* Test creating an XML context and supplying it to WSDCreateDiscoveryPublisher */
458 rc = WSDXMLCreateContext(&xmlContext);
459 ok(rc == S_OK, "WSDXMLCreateContext failed: %08x\n", rc);
461 rc = WSDCreateDiscoveryPublisher(xmlContext, &publisher);
462 ok(rc == S_OK, "WSDCreateDiscoveryPublisher(xmlContext, &publisher) failed: %08x\n", rc);
463 ok(publisher != NULL, "WSDCreateDiscoveryPublisher(xmlContext, &publisher) failed: publisher == NULL\n");
465 rc = IWSDiscoveryPublisher_GetXMLContext(publisher, NULL);
466 ok(rc == E_INVALIDARG, "GetXMLContext returned unexpected value with NULL argument: %08x\n", rc);
468 rc = IWSDiscoveryPublisher_GetXMLContext(publisher, &returnedContext);
469 ok(rc == S_OK, "GetXMLContext failed: %08x\n", rc);
471 ok(xmlContext == returnedContext, "GetXMLContext returned unexpected value: returnedContext == %p\n", returnedContext);
473 ref = IWSDXMLContext_Release(returnedContext);
474 ok(ref == 2, "IWSDXMLContext_Release() has %d references, should have 2\n", ref);
476 ref = IWSDiscoveryPublisher_Release(publisher);
477 ok(ref == 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref);
479 ref = IWSDXMLContext_Release(returnedContext);
480 ok(ref == 0, "IWSDXMLContext_Release() has %d references, should have 0\n", ref);
482 /* Test using a default XML context */
483 publisher = NULL;
484 returnedContext = NULL;
486 rc = WSDCreateDiscoveryPublisher(NULL, &publisher);
487 ok(rc == S_OK, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: %08x\n", rc);
488 ok(publisher != NULL, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: publisher == NULL\n");
490 rc = IWSDiscoveryPublisher_GetXMLContext(publisher, &returnedContext);
491 ok(rc == S_OK, "GetXMLContext failed: %08x\n", rc);
493 ref = IWSDXMLContext_Release(returnedContext);
494 ok(ref == 1, "IWSDXMLContext_Release() has %d references, should have 1\n", ref);
496 ref = IWSDiscoveryPublisher_Release(publisher);
497 ok(ref == 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref);
500 static void Publish_tests(void)
502 IWSDiscoveryPublisher *publisher = NULL;
503 IWSDiscoveryPublisherNotify *sink1 = NULL, *sink2 = NULL;
504 IWSDiscoveryPublisherNotifyImpl *sink1Impl = NULL, *sink2Impl = NULL;
505 char endpointReferenceString[MAX_PATH], app_sequence_string[MAX_PATH];
506 LPWSTR publisherIdW = NULL, sequenceIdW = NULL;
507 messageStorage *msgStorage;
508 WSADATA wsaData;
509 BOOL messageOK, hello_message_seen = FALSE, endpoint_reference_seen = FALSE, app_sequence_seen = FALSE;
510 BOOL metadata_version_seen = FALSE, any_header_seen = FALSE, wine_ns_seen = FALSE, body_hello_seen = FALSE;
511 BOOL any_body_seen = FALSE;
512 int ret, i;
513 HRESULT rc;
514 ULONG ref;
515 char *msg;
516 WSDXML_ELEMENT *header_any_element, *body_any_element;
517 WSDXML_NAME header_any_name;
518 WSDXML_NAMESPACE ns;
519 WCHAR header_any_name_text[] = {'B','e','e','r',0};
520 static const WCHAR header_any_text[] = {'P','u','b','l','i','s','h','T','e','s','t',0};
521 static const WCHAR body_any_text[] = {'B','o','d','y','T','e','s','t',0};
522 static const WCHAR uri[] = {'h','t','t','p',':','/','/','w','i','n','e','.','t','e','s','t','/',0};
523 static const WCHAR prefix[] = {'w','i','n','e',0};
525 rc = WSDCreateDiscoveryPublisher(NULL, &publisher);
526 ok(rc == S_OK, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: %08x\n", rc);
527 ok(publisher != NULL, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: publisher == NULL\n");
529 /* Test SetAddressFamily */
530 rc = IWSDiscoveryPublisher_SetAddressFamily(publisher, 12345);
531 ok(rc == E_INVALIDARG, "IWSDiscoveryPublisher_SetAddressFamily(12345) returned unexpected result: %08x\n", rc);
533 rc = IWSDiscoveryPublisher_SetAddressFamily(publisher, WSDAPI_ADDRESSFAMILY_IPV4);
534 ok(rc == S_OK, "IWSDiscoveryPublisher_SetAddressFamily(WSDAPI_ADDRESSFAMILY_IPV4) failed: %08x\n", rc);
536 /* Try to update the address family after already setting it */
537 rc = IWSDiscoveryPublisher_SetAddressFamily(publisher, WSDAPI_ADDRESSFAMILY_IPV6);
538 ok(rc == STG_E_INVALIDFUNCTION, "IWSDiscoveryPublisher_SetAddressFamily(WSDAPI_ADDRESSFAMILY_IPV6) returned unexpected result: %08x\n", rc);
540 /* Create notification sinks */
541 ok(create_discovery_publisher_notify(&sink1) == TRUE, "create_discovery_publisher_notify failed\n");
542 ok(create_discovery_publisher_notify(&sink2) == TRUE, "create_discovery_publisher_notify failed\n");
544 /* Get underlying implementation so we can check the ref count */
545 sink1Impl = impl_from_IWSDiscoveryPublisherNotify(sink1);
546 sink2Impl = impl_from_IWSDiscoveryPublisherNotify(sink2);
548 /* Attempt to unregister sink before registering it */
549 rc = IWSDiscoveryPublisher_UnRegisterNotificationSink(publisher, sink1);
550 ok(rc == E_FAIL, "IWSDiscoveryPublisher_UnRegisterNotificationSink returned unexpected result: %08x\n", rc);
552 /* Register notification sinks */
553 rc = IWSDiscoveryPublisher_RegisterNotificationSink(publisher, sink1);
554 ok(rc == S_OK, "IWSDiscoveryPublisher_RegisterNotificationSink failed: %08x\n", rc);
555 ok(sink1Impl->ref == 2, "Ref count for sink 1 is not as expected: %d\n", sink1Impl->ref);
557 rc = IWSDiscoveryPublisher_RegisterNotificationSink(publisher, sink2);
558 ok(rc == S_OK, "IWSDiscoveryPublisher_RegisterNotificationSink failed: %08x\n", rc);
559 ok(sink2Impl->ref == 2, "Ref count for sink 2 is not as expected: %d\n", sink2Impl->ref);
561 /* Unregister the first sink */
562 rc = IWSDiscoveryPublisher_UnRegisterNotificationSink(publisher, sink1);
563 ok(rc == S_OK, "IWSDiscoveryPublisher_UnRegisterNotificationSink failed: %08x\n", rc);
564 ok(sink1Impl->ref == 1, "Ref count for sink 1 is not as expected: %d\n", sink1Impl->ref);
566 /* Set up network listener */
567 publisherIdW = utf8_to_wide(publisherId);
568 if (publisherIdW == NULL) goto after_publish_test;
570 sequenceIdW = utf8_to_wide(sequenceId);
571 if (sequenceIdW == NULL) goto after_publish_test;
573 msgStorage = heap_alloc_zero(sizeof(messageStorage));
574 if (msgStorage == NULL) goto after_publish_test;
576 msgStorage->running = TRUE;
577 InitializeCriticalSection(&msgStorage->criticalSection);
579 ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
580 ok(ret == 0, "WSAStartup failed (ret = %d)\n", ret);
582 ret = start_listening_on_all_addresses(msgStorage, AF_INET);
583 ok(ret == TRUE, "Unable to listen on IPv4 addresses (ret == %d)\n", ret);
585 /* Create "any" elements for header */
586 ns.Uri = uri;
587 ns.PreferredPrefix = prefix;
589 header_any_name.LocalName = header_any_name_text;
590 header_any_name.Space = &ns;
592 rc = WSDXMLBuildAnyForSingleElement(&header_any_name, header_any_text, &header_any_element);
593 ok(rc == S_OK, "WSDXMLBuildAnyForSingleElement failed with %08x\n", rc);
595 rc = WSDXMLBuildAnyForSingleElement(&header_any_name, body_any_text, &body_any_element);
596 ok(rc == S_OK, "WSDXMLBuildAnyForSingleElement failed with %08x\n", rc);
598 /* Publish the service */
599 rc = IWSDiscoveryPublisher_PublishEx(publisher, publisherIdW, 1, 1, 1, sequenceIdW, NULL, NULL, NULL,
600 header_any_element, NULL, NULL, NULL, body_any_element);
602 WSDFreeLinkedMemory(header_any_element);
603 WSDFreeLinkedMemory(body_any_element);
605 ok(rc == S_OK, "Publish failed: %08x\n", rc);
607 /* Wait up to 2 seconds for messages to be received */
608 if (WaitForMultipleObjects(msgStorage->numThreadHandles, msgStorage->threadHandles, TRUE, 2000) == WAIT_TIMEOUT)
610 /* Wait up to 1 more second for threads to terminate */
611 msgStorage->running = FALSE;
612 WaitForMultipleObjects(msgStorage->numThreadHandles, msgStorage->threadHandles, TRUE, 1000);
615 DeleteCriticalSection(&msgStorage->criticalSection);
617 /* Verify we've received a message */
618 ok(msgStorage->messageCount >= 1, "No messages received\n");
620 sprintf(endpointReferenceString, "<wsa:EndpointReference><wsa:Address>%s</wsa:Address></wsa:EndpointReference>", publisherId);
621 sprintf(app_sequence_string, "<wsd:AppSequence InstanceId=\"1\" SequenceId=\"%s\" MessageNumber=\"1\"></wsd:AppSequence>",
622 sequenceId);
624 messageOK = FALSE;
626 /* Check we're received the correct message */
627 for (i = 0; i < msgStorage->messageCount; i++)
629 msg = msgStorage->messages[i];
630 messageOK = FALSE;
632 hello_message_seen = (strstr(msg, "<wsa:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Hello</wsa:Action>") != NULL);
633 endpoint_reference_seen = (strstr(msg, endpointReferenceString) != NULL);
634 app_sequence_seen = (strstr(msg, app_sequence_string) != NULL);
635 metadata_version_seen = (strstr(msg, "<wsd:MetadataVersion>1</wsd:MetadataVersion>") != NULL);
636 any_header_seen = (strstr(msg, "<wine:Beer>PublishTest</wine:Beer>") != NULL);
637 wine_ns_seen = (strstr(msg, "xmlns:wine=\"http://wine.test/\"") != NULL);
638 body_hello_seen = (strstr(msg, "<soap:Body><wsd:Hello") != NULL);
639 any_body_seen = (strstr(msg, "<wine:Beer>BodyTest</wine:Beer>") != NULL);
640 messageOK = hello_message_seen && endpoint_reference_seen && app_sequence_seen && metadata_version_seen &&
641 any_header_seen && wine_ns_seen && body_hello_seen && any_body_seen;
643 if (messageOK) break;
646 for (i = 0; i < msgStorage->messageCount; i++)
648 heap_free(msgStorage->messages[i]);
651 heap_free(msgStorage);
653 ok(hello_message_seen == TRUE, "Hello message not received\n");
654 todo_wine ok(endpoint_reference_seen == TRUE, "EndpointReference not received\n");
655 ok(app_sequence_seen == TRUE, "AppSequence not received\n");
656 todo_wine ok(metadata_version_seen == TRUE, "MetadataVersion not received\n");
657 todo_wine ok(messageOK == TRUE, "Hello message metadata not received\n");
658 ok(any_header_seen == TRUE, "Custom header not received\n");
659 ok(wine_ns_seen == TRUE, "Wine namespace not received\n");
660 ok(body_hello_seen == TRUE, "Body and Hello elements not received\n");
661 ok(any_body_seen == TRUE, "Custom body element not received\n");
663 after_publish_test:
665 heap_free(publisherIdW);
666 heap_free(sequenceIdW);
668 ref = IWSDiscoveryPublisher_Release(publisher);
669 ok(ref == 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref);
671 /* Check that the sinks have been released by the publisher */
672 ok(sink1Impl->ref == 1, "Ref count for sink 1 is not as expected: %d\n", sink1Impl->ref);
673 ok(sink2Impl->ref == 1, "Ref count for sink 2 is not as expected: %d\n", sink2Impl->ref);
675 /* Release the sinks */
676 IWSDiscoveryPublisherNotify_Release(sink1);
677 IWSDiscoveryPublisherNotify_Release(sink2);
679 WSACleanup();
682 enum firewall_op
684 APP_ADD,
685 APP_REMOVE
688 static BOOL is_process_elevated(void)
690 HANDLE token;
691 if (OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &token ))
693 TOKEN_ELEVATION_TYPE type;
694 DWORD size;
695 BOOL ret;
697 ret = GetTokenInformation( token, TokenElevationType, &type, sizeof(type), &size );
698 CloseHandle( token );
699 return (ret && type == TokenElevationTypeFull);
701 return FALSE;
704 static BOOL is_firewall_enabled(void)
706 HRESULT hr, init;
707 INetFwMgr *mgr = NULL;
708 INetFwPolicy *policy = NULL;
709 INetFwProfile *profile = NULL;
710 VARIANT_BOOL enabled = VARIANT_FALSE;
712 init = CoInitializeEx( 0, COINIT_APARTMENTTHREADED );
714 hr = CoCreateInstance( &CLSID_NetFwMgr, NULL, CLSCTX_INPROC_SERVER, &IID_INetFwMgr,
715 (void **)&mgr );
716 ok( hr == S_OK, "got %08x\n", hr );
717 if (hr != S_OK) goto done;
719 hr = INetFwMgr_get_LocalPolicy( mgr, &policy );
720 ok( hr == S_OK, "got %08x\n", hr );
721 if (hr != S_OK) goto done;
723 hr = INetFwPolicy_get_CurrentProfile( policy, &profile );
724 if (hr != S_OK) goto done;
726 hr = INetFwProfile_get_FirewallEnabled( profile, &enabled );
727 ok( hr == S_OK, "got %08x\n", hr );
729 done:
730 if (policy) INetFwPolicy_Release( policy );
731 if (profile) INetFwProfile_Release( profile );
732 if (mgr) INetFwMgr_Release( mgr );
733 if (SUCCEEDED( init )) CoUninitialize();
734 return (enabled == VARIANT_TRUE);
737 static HRESULT set_firewall( enum firewall_op op )
739 static const WCHAR testW[] = {'w','s','d','a','p','i','_','t','e','s','t',0};
740 HRESULT hr, init;
741 INetFwMgr *mgr = NULL;
742 INetFwPolicy *policy = NULL;
743 INetFwProfile *profile = NULL;
744 INetFwAuthorizedApplication *app = NULL;
745 INetFwAuthorizedApplications *apps = NULL;
746 BSTR name, image = SysAllocStringLen( NULL, MAX_PATH );
748 if (!GetModuleFileNameW( NULL, image, MAX_PATH ))
750 SysFreeString( image );
751 return E_FAIL;
753 init = CoInitializeEx( 0, COINIT_APARTMENTTHREADED );
755 hr = CoCreateInstance( &CLSID_NetFwMgr, NULL, CLSCTX_INPROC_SERVER, &IID_INetFwMgr,
756 (void **)&mgr );
757 ok( hr == S_OK, "got %08x\n", hr );
758 if (hr != S_OK) goto done;
760 hr = INetFwMgr_get_LocalPolicy( mgr, &policy );
761 ok( hr == S_OK, "got %08x\n", hr );
762 if (hr != S_OK) goto done;
764 hr = INetFwPolicy_get_CurrentProfile( policy, &profile );
765 if (hr != S_OK) goto done;
767 hr = INetFwProfile_get_AuthorizedApplications( profile, &apps );
768 ok( hr == S_OK, "got %08x\n", hr );
769 if (hr != S_OK) goto done;
771 hr = CoCreateInstance( &CLSID_NetFwAuthorizedApplication, NULL, CLSCTX_INPROC_SERVER,
772 &IID_INetFwAuthorizedApplication, (void **)&app );
773 ok( hr == S_OK, "got %08x\n", hr );
774 if (hr != S_OK) goto done;
776 hr = INetFwAuthorizedApplication_put_ProcessImageFileName( app, image );
777 if (hr != S_OK) goto done;
779 name = SysAllocString( testW );
780 hr = INetFwAuthorizedApplication_put_Name( app, name );
781 SysFreeString( name );
782 ok( hr == S_OK, "got %08x\n", hr );
783 if (hr != S_OK) goto done;
785 if (op == APP_ADD)
786 hr = INetFwAuthorizedApplications_Add( apps, app );
787 else if (op == APP_REMOVE)
788 hr = INetFwAuthorizedApplications_Remove( apps, image );
789 else
790 hr = E_INVALIDARG;
792 done:
793 if (app) INetFwAuthorizedApplication_Release( app );
794 if (apps) INetFwAuthorizedApplications_Release( apps );
795 if (policy) INetFwPolicy_Release( policy );
796 if (profile) INetFwProfile_Release( profile );
797 if (mgr) INetFwMgr_Release( mgr );
798 if (SUCCEEDED( init )) CoUninitialize();
799 SysFreeString( image );
800 return hr;
803 START_TEST(discovery)
805 BOOL firewall_enabled = is_firewall_enabled();
806 HRESULT hr;
808 if (firewall_enabled)
810 if (!is_process_elevated())
812 skip("no privileges, skipping tests to avoid firewall dialog\n");
813 return;
815 if ((hr = set_firewall(APP_ADD)) != S_OK)
817 skip("can't authorize app in firewall %08x\n", hr);
818 return;
822 CoInitialize(NULL);
824 CreateDiscoveryPublisher_tests();
825 CreateDiscoveryPublisher_XMLContext_tests();
826 Publish_tests();
828 CoUninitialize();
829 if (firewall_enabled) set_firewall(APP_REMOVE);