wsdapi: Implement probe message parsing.
[wine.git] / dlls / wsdapi / tests / discovery.c
blob94a5ebfa8e06b410ac66c41d1f42a9187a47c378
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 static const char testProbeMessage[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
45 "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" "
46 "xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" "
47 "xmlns:wsd=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\" "
48 "xmlns:grog=\"http://more.tests/\"><soap:Header><wsa:To>urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>"
49 "<wsa:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</wsa:Action>"
50 "<wsa:MessageID>urn:uuid:%s</wsa:MessageID>"
51 "<grog:Perry>ExtraInfo</grog:Perry></soap:Header>"
52 "<soap:Body><wsd:Probe><wsd:Types>grog:Cider</wsd:Types><grog:Lager>MoreInfo</grog:Lager></wsd:Probe></soap:Body></soap:Envelope>";
54 static const WCHAR discoveryTo[] = {
55 'u','r','n',':',
56 's','c','h','e','m','a','s','-','x','m','l','s','o','a','p','-','o','r','g',':',
57 'w','s',':','2','0','0','5',':','0','4',':',
58 'd','i','s','c','o','v','e','r','y', 0 };
60 static const WCHAR actionProbe[] = {
61 'h','t','t','p',':','/','/',
62 's','c','h','e','m','a','s','.','x','m','l','s','o','a','p','.','o','r','g','/',
63 'w','s','/','2','0','0','5','/','0','4','/',
64 'd','i','s','c','o','v','e','r','y','/',
65 'P','r','o','b','e', 0 };
67 static const WCHAR uri_more_tests[] = { 'h','t','t','p',':','/','/','m','o','r','e','.','t','e','s','t','s','/', 0 };
68 static const WCHAR uri_more_tests_no_slash[] = { 'h','t','t','p',':','/','/','m','o','r','e','.','t','e','s','t','s', 0 };
69 static const WCHAR prefix_grog[] = { 'g','r','o','g', 0 };
70 static const WCHAR name_cider[] = { 'C','i','d','e','r', 0 };
72 static HANDLE probe_event = NULL;
73 static UUID probe_message_id;
75 #define MAX_CACHED_MESSAGES 5
76 #define MAX_LISTENING_THREADS 20
78 typedef struct messageStorage {
79 BOOL running;
80 CRITICAL_SECTION criticalSection;
81 char* messages[MAX_CACHED_MESSAGES];
82 int messageCount;
83 HANDLE threadHandles[MAX_LISTENING_THREADS];
84 int numThreadHandles;
85 } messageStorage;
87 static LPWSTR utf8_to_wide(const char *utf8String)
89 int sizeNeeded = 0, utf8StringLength = 0, memLength = 0;
90 LPWSTR newString = NULL;
92 if (utf8String == NULL) return NULL;
93 utf8StringLength = lstrlenA(utf8String);
95 sizeNeeded = MultiByteToWideChar(CP_UTF8, 0, utf8String, utf8StringLength, NULL, 0);
96 if (sizeNeeded <= 0) return NULL;
98 memLength = sizeof(WCHAR) * (sizeNeeded + 1);
99 newString = heap_alloc_zero(memLength);
101 MultiByteToWideChar(CP_UTF8, 0, utf8String, utf8StringLength, newString, sizeNeeded);
102 return newString;
105 static int join_multicast_group(SOCKET s, struct addrinfo *group, struct addrinfo *iface)
107 int level, optname, optlen;
108 struct ipv6_mreq mreqv6;
109 struct ip_mreq mreqv4;
110 char *optval;
112 if (group->ai_family == AF_INET6)
114 level = IPPROTO_IPV6;
115 optname = IPV6_ADD_MEMBERSHIP;
116 optval = (char *)&mreqv6;
117 optlen = sizeof(mreqv6);
119 mreqv6.ipv6mr_multiaddr = ((SOCKADDR_IN6 *)group->ai_addr)->sin6_addr;
120 mreqv6.ipv6mr_interface = ((SOCKADDR_IN6 *)iface->ai_addr)->sin6_scope_id;
122 else
124 level = IPPROTO_IP;
125 optname = IP_ADD_MEMBERSHIP;
126 optval = (char *)&mreqv4;
127 optlen = sizeof(mreqv4);
129 mreqv4.imr_multiaddr.s_addr = ((SOCKADDR_IN *)group->ai_addr)->sin_addr.s_addr;
130 mreqv4.imr_interface.s_addr = ((SOCKADDR_IN *)iface->ai_addr)->sin_addr.s_addr;
133 return setsockopt(s, level, optname, optval, optlen);
136 static int set_send_interface(SOCKET s, struct addrinfo *iface)
138 int level, optname, optlen;
139 char *optval = NULL;
141 if (iface->ai_family == AF_INET6)
143 level = IPPROTO_IPV6;
144 optname = IPV6_MULTICAST_IF;
145 optval = (char *) &((SOCKADDR_IN6 *)iface->ai_addr)->sin6_scope_id;
146 optlen = sizeof(((SOCKADDR_IN6 *)iface->ai_addr)->sin6_scope_id);
148 else
150 level = IPPROTO_IP;
151 optname = IP_MULTICAST_IF;
152 optval = (char *) &((SOCKADDR_IN *)iface->ai_addr)->sin_addr.s_addr;
153 optlen = sizeof(((SOCKADDR_IN *)iface->ai_addr)->sin_addr.s_addr);
156 return setsockopt(s, level, optname, optval, optlen);
159 static struct addrinfo *resolve_address(const char *address, const char *port, int family, int type, int protocol)
161 struct addrinfo hints, *result = NULL;
163 ZeroMemory(&hints, sizeof(hints));
165 hints.ai_flags = AI_PASSIVE;
166 hints.ai_family = family;
167 hints.ai_socktype = type;
168 hints.ai_protocol = protocol;
170 return getaddrinfo(address, port, &hints, &result) == 0 ? result : NULL;
173 typedef struct listenerThreadParams
175 messageStorage *msgStorage;
176 SOCKET listeningSocket;
177 } listenerThreadParams;
179 #define RECEIVE_BUFFER_SIZE 65536
181 static DWORD WINAPI listening_thread(LPVOID lpParam)
183 listenerThreadParams *parameter = (listenerThreadParams *)lpParam;
184 messageStorage *msgStorage = parameter->msgStorage;
185 int bytesReceived;
186 char *buffer;
188 buffer = heap_alloc(RECEIVE_BUFFER_SIZE);
190 while (parameter->msgStorage->running)
192 ZeroMemory(buffer, RECEIVE_BUFFER_SIZE);
193 bytesReceived = recv(parameter->listeningSocket, buffer, RECEIVE_BUFFER_SIZE, 0);
195 if (bytesReceived == SOCKET_ERROR)
197 if (WSAGetLastError() != WSAETIMEDOUT)
198 return 0;
200 else
202 EnterCriticalSection(&msgStorage->criticalSection);
204 if (msgStorage->messageCount < MAX_CACHED_MESSAGES)
206 msgStorage->messages[msgStorage->messageCount] = heap_alloc(bytesReceived);
208 if (msgStorage->messages[msgStorage->messageCount] != NULL)
210 memcpy(msgStorage->messages[msgStorage->messageCount], buffer, bytesReceived);
211 msgStorage->messageCount++;
215 LeaveCriticalSection(&msgStorage->criticalSection);
217 if (msgStorage->messageCount >= MAX_CACHED_MESSAGES)
219 /* Stop all threads */
220 msgStorage->running = FALSE;
221 break;
226 closesocket(parameter->listeningSocket);
228 heap_free(buffer);
229 heap_free(parameter);
231 return 0;
234 static void start_listening(messageStorage *msgStorage, const char *multicastAddress, const char *bindAddress)
236 struct addrinfo *multicastAddr = NULL, *bindAddr = NULL, *interfaceAddr = NULL;
237 listenerThreadParams *parameter = NULL;
238 const DWORD receiveTimeout = 500;
239 const UINT reuseAddr = 1;
240 HANDLE hThread;
241 SOCKET s = 0;
243 /* Resolve the multicast address */
244 multicastAddr = resolve_address(multicastAddress, SEND_PORT, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP);
245 if (multicastAddr == NULL) goto cleanup;
247 /* Resolve the binding address */
248 bindAddr = resolve_address(bindAddress, SEND_PORT, multicastAddr->ai_family, multicastAddr->ai_socktype, multicastAddr->ai_protocol);
249 if (bindAddr == NULL) goto cleanup;
251 /* Resolve the multicast interface */
252 interfaceAddr = resolve_address(bindAddress, "0", multicastAddr->ai_family, multicastAddr->ai_socktype, multicastAddr->ai_protocol);
253 if (interfaceAddr == NULL) goto cleanup;
255 /* Create the socket */
256 s = socket(multicastAddr->ai_family, multicastAddr->ai_socktype, multicastAddr->ai_protocol);
257 if (s == INVALID_SOCKET) goto cleanup;
259 /* Ensure the socket can be reused */
260 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&reuseAddr, sizeof(reuseAddr)) == SOCKET_ERROR) goto cleanup;
262 /* Bind the socket to the local interface so we can receive data */
263 if (bind(s, bindAddr->ai_addr, bindAddr->ai_addrlen) == SOCKET_ERROR) goto cleanup;
265 /* Join the multicast group */
266 if (join_multicast_group(s, multicastAddr, interfaceAddr) == SOCKET_ERROR) goto cleanup;
268 /* Set the outgoing interface */
269 if (set_send_interface(s, interfaceAddr) == SOCKET_ERROR) goto cleanup;
271 /* For IPv6, ensure the scope ID is zero */
272 if (multicastAddr->ai_family == AF_INET6)
273 ((SOCKADDR_IN6 *)multicastAddr->ai_addr)->sin6_scope_id = 0;
275 /* Set a 500ms receive timeout */
276 if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char *)&receiveTimeout, sizeof(receiveTimeout)) == SOCKET_ERROR) goto cleanup;
278 /* Allocate memory for thread parameters */
279 parameter = heap_alloc(sizeof(listenerThreadParams));
281 parameter->msgStorage = msgStorage;
282 parameter->listeningSocket = s;
284 hThread = CreateThread(NULL, 0, listening_thread, parameter, 0, NULL);
285 if (hThread == NULL) goto cleanup;
287 msgStorage->threadHandles[msgStorage->numThreadHandles] = hThread;
288 msgStorage->numThreadHandles++;
290 goto cleanup_addresses;
292 cleanup:
293 closesocket(s);
294 heap_free(parameter);
296 cleanup_addresses:
297 freeaddrinfo(multicastAddr);
298 freeaddrinfo(bindAddr);
299 freeaddrinfo(interfaceAddr);
302 static BOOL start_listening_on_all_addresses(messageStorage *msgStorage, ULONG family)
304 IP_ADAPTER_ADDRESSES *adapterAddresses = NULL, *adapterAddress;
305 ULONG bufferSize = 0;
306 LPSOCKADDR sockaddr;
307 DWORD addressLength;
308 char address[64];
309 BOOL ret = FALSE;
310 ULONG retVal;
312 retVal = GetAdaptersAddresses(family, 0, NULL, NULL, &bufferSize); /* family should be AF_INET or AF_INET6 */
313 if (retVal != ERROR_BUFFER_OVERFLOW) goto cleanup;
315 /* Get size of buffer for adapters */
316 adapterAddresses = (IP_ADAPTER_ADDRESSES *)heap_alloc(bufferSize);
317 if (adapterAddresses == NULL) goto cleanup;
319 /* Get list of adapters */
320 retVal = GetAdaptersAddresses(family, 0, NULL, adapterAddresses, &bufferSize);
321 if (retVal != ERROR_SUCCESS) goto cleanup;
323 for (adapterAddress = adapterAddresses; adapterAddress != NULL; adapterAddress = adapterAddress->Next)
325 if (msgStorage->numThreadHandles >= MAX_LISTENING_THREADS)
327 ret = TRUE;
328 goto cleanup;
331 if (adapterAddress->FirstUnicastAddress == NULL) continue;
333 sockaddr = adapterAddress->FirstUnicastAddress->Address.lpSockaddr;
334 addressLength = sizeof(address);
335 WSAAddressToStringA(sockaddr, adapterAddress->FirstUnicastAddress->Address.iSockaddrLength, NULL, address, &addressLength);
337 start_listening(msgStorage, adapterAddress->FirstUnicastAddress->Address.lpSockaddr->sa_family == AF_INET ? SEND_ADDRESS_IPV4 : SEND_ADDRESS_IPV6, address);
340 ret = TRUE;
342 cleanup:
343 heap_free(adapterAddresses);
344 return ret;
347 static BOOL send_udp_multicast_of_type(const char *data, int length, ULONG family)
349 IP_ADAPTER_ADDRESSES *adapter_addresses = NULL, *adapter_addr;
350 static const struct in6_addr i_addr_zero;
351 struct addrinfo *multi_address;
352 ULONG bufferSize = 0;
353 LPSOCKADDR sockaddr;
354 BOOL ret = FALSE;
355 const char ttl = 8;
356 ULONG retval;
357 SOCKET s;
359 /* Resolve the multicast address */
360 if (family == AF_INET6)
361 multi_address = resolve_address(SEND_ADDRESS_IPV6, SEND_PORT, AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
362 else
363 multi_address = resolve_address(SEND_ADDRESS_IPV4, SEND_PORT, AF_INET, SOCK_DGRAM, IPPROTO_UDP);
365 if (multi_address == NULL)
366 return FALSE;
368 /* Get size of buffer for adapters */
369 retval = GetAdaptersAddresses(family, 0, NULL, NULL, &bufferSize);
370 if (retval != ERROR_BUFFER_OVERFLOW) goto cleanup;
372 adapter_addresses = (IP_ADAPTER_ADDRESSES *) heap_alloc(bufferSize);
373 if (adapter_addresses == NULL) goto cleanup;
375 /* Get list of adapters */
376 retval = GetAdaptersAddresses(family, 0, NULL, adapter_addresses, &bufferSize);
377 if (retval != ERROR_SUCCESS) goto cleanup;
379 for (adapter_addr = adapter_addresses; adapter_addr != NULL; adapter_addr = adapter_addr->Next)
381 if (adapter_addr->FirstUnicastAddress == NULL) continue;
383 sockaddr = adapter_addr->FirstUnicastAddress->Address.lpSockaddr;
385 /* Create a socket and bind to the adapter address */
386 s = socket(family, SOCK_DGRAM, IPPROTO_UDP);
387 if (s == INVALID_SOCKET) continue;
389 if (bind(s, sockaddr, adapter_addr->FirstUnicastAddress->Address.iSockaddrLength) == SOCKET_ERROR)
391 closesocket(s);
392 continue;
395 /* Set the multicast interface and TTL value */
396 setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, (char *) &i_addr_zero,
397 (family == AF_INET6) ? sizeof(struct in6_addr) : sizeof(struct in_addr));
398 setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl));
400 sendto(s, data, length, 0, (SOCKADDR *) multi_address->ai_addr, multi_address->ai_addrlen);
401 closesocket(s);
404 ret = TRUE;
406 cleanup:
407 freeaddrinfo(multi_address);
408 heap_free(adapter_addresses);
409 return ret;
412 typedef struct IWSDiscoveryPublisherNotifyImpl {
413 IWSDiscoveryPublisherNotify IWSDiscoveryPublisherNotify_iface;
414 LONG ref;
415 } IWSDiscoveryPublisherNotifyImpl;
417 static inline IWSDiscoveryPublisherNotifyImpl *impl_from_IWSDiscoveryPublisherNotify(IWSDiscoveryPublisherNotify *iface)
419 return CONTAINING_RECORD(iface, IWSDiscoveryPublisherNotifyImpl, IWSDiscoveryPublisherNotify_iface);
422 static HRESULT WINAPI IWSDiscoveryPublisherNotifyImpl_QueryInterface(IWSDiscoveryPublisherNotify *iface, REFIID riid, void **ppv)
424 IWSDiscoveryPublisherNotifyImpl *This = impl_from_IWSDiscoveryPublisherNotify(iface);
426 if (!ppv)
428 return E_INVALIDARG;
431 *ppv = NULL;
433 if (IsEqualIID(riid, &IID_IUnknown) ||
434 IsEqualIID(riid, &IID_IWSDiscoveryPublisherNotify))
436 *ppv = &This->IWSDiscoveryPublisherNotify_iface;
438 else
440 return E_NOINTERFACE;
443 IUnknown_AddRef((IUnknown*)*ppv);
444 return S_OK;
447 static ULONG WINAPI IWSDiscoveryPublisherNotifyImpl_AddRef(IWSDiscoveryPublisherNotify *iface)
449 IWSDiscoveryPublisherNotifyImpl *This = impl_from_IWSDiscoveryPublisherNotify(iface);
450 ULONG ref = InterlockedIncrement(&This->ref);
452 trace("IWSDiscoveryPublisherNotifyImpl_AddRef called (%p, ref = %d)\n", This, ref);
453 return ref;
456 static ULONG WINAPI IWSDiscoveryPublisherNotifyImpl_Release(IWSDiscoveryPublisherNotify *iface)
458 IWSDiscoveryPublisherNotifyImpl *This = impl_from_IWSDiscoveryPublisherNotify(iface);
459 ULONG ref = InterlockedDecrement(&This->ref);
461 trace("IWSDiscoveryPublisherNotifyImpl_Release called (%p, ref = %d)\n", This, ref);
463 if (ref == 0)
465 HeapFree(GetProcessHeap(), 0, This);
468 return ref;
471 static void verify_wsdxml_name(const char *debug_prefix, WSDXML_NAME *name, LPCWSTR uri, LPCWSTR prefix,
472 LPCWSTR local_name)
474 ok(name != NULL, "%s: name == NULL\n", debug_prefix);
475 if (name == NULL) return;
477 ok(name->LocalName != NULL && lstrcmpW(name->LocalName, local_name) == 0,
478 "%s: Local name = '%s'\n", debug_prefix, wine_dbgstr_w(name->LocalName));
480 ok(name->Space != NULL, "%s: Space == NULL\n", debug_prefix);
481 if (name->Space == NULL) return;
483 ok(name->Space->Uri != NULL && lstrcmpW(name->Space->Uri, uri) == 0,
484 "%s: URI == '%s'\n", debug_prefix, wine_dbgstr_w(name->Space->Uri));
485 ok(name->Space->PreferredPrefix != NULL && lstrcmpW(name->Space->PreferredPrefix, prefix) == 0,
486 "%s: Prefix = '%s'\n", debug_prefix, wine_dbgstr_w(name->Space->PreferredPrefix));
489 static void verify_wsdxml_any_text(const char *debug_prefix, WSDXML_ELEMENT *any, LPCWSTR uri, LPCWSTR prefix,
490 LPCWSTR local_name, LPCWSTR value)
492 WSDXML_TEXT *child;
494 ok(any != NULL, "%s: any == NULL\n", debug_prefix);
495 if (any == NULL) return;
497 child = (WSDXML_TEXT *) any->FirstChild;
499 ok(any->Node.Type == ElementType, "%s: Node type == %d\n", debug_prefix, any->Node.Type);
500 ok(any->Node.Parent == NULL, "%s: Parent == %p\n", debug_prefix, any->Node.Parent);
501 ok(any->Node.Next == NULL, "%s: Next == %p\n", debug_prefix, any->Node.Next);
502 verify_wsdxml_name(debug_prefix, any->Name, uri, prefix, local_name);
504 ok(child != NULL, "%s: First child == NULL\n", debug_prefix);
506 if (child != NULL)
508 ok(child->Node.Type == TextType, "%s: Node type == %d\n", debug_prefix, child->Node.Type);
509 ok(child->Node.Parent == any, "%s: Parent == %p\n", debug_prefix, child->Node.Parent);
510 ok(child->Node.Next == NULL, "%s: Next == %p\n", debug_prefix, child->Node.Next);
512 if (child->Node.Type == TextType)
513 ok(child->Text != NULL && lstrcmpW(child->Text, value) == 0,
514 "%s: Text == '%s'\n", debug_prefix, wine_dbgstr_w(child->Text));
518 static HRESULT WINAPI IWSDiscoveryPublisherNotifyImpl_ProbeHandler(IWSDiscoveryPublisherNotify *This, const WSD_SOAP_MESSAGE *pSoap, IWSDMessageParameters *pMessageParameters)
520 trace("IWSDiscoveryPublisherNotifyImpl_ProbeHandler called (%p, %p, %p)\n", This, pSoap, pMessageParameters);
522 if (probe_event == NULL)
524 /* We may have received an unrelated probe on the network */
525 return S_OK;
528 ok(pSoap != NULL, "pSoap == NULL\n");
529 ok(pMessageParameters != NULL, "pMessageParameters == NULL\n");
531 if (pSoap != NULL)
533 static const WCHAR perry[] = {'P','e','r','r','y',0};
534 static const WCHAR extra_info[] = {'E','x','t','r','a','I','n','f','o',0};
535 WSD_PROBE *probe_msg = (WSD_PROBE *) pSoap->Body;
537 ok(pSoap->Body != NULL, "pSoap->Body == NULL\n");
538 ok(pSoap->Header.To != NULL && lstrcmpW(pSoap->Header.To, discoveryTo) == 0,
539 "pSoap->Header.To == '%s'\n", wine_dbgstr_w(pSoap->Header.To));
540 ok(pSoap->Header.Action != NULL && lstrcmpW(pSoap->Header.Action, actionProbe) == 0,
541 "pSoap->Header.Action == '%s'\n", wine_dbgstr_w(pSoap->Header.Action));
543 ok(pSoap->Header.MessageID != NULL, "pSoap->Header.MessageID == NULL\n");
545 /* Ensure the message ID is at least 9 characters long (to skip past the 'urn:uuid:' prefix) */
546 if ((pSoap->Header.MessageID != NULL) && (lstrlenW(pSoap->Header.MessageID) > 9))
548 UUID uuid;
549 RPC_STATUS ret = UuidFromStringW((LPWSTR)pSoap->Header.MessageID + 9, &uuid);
551 trace("Received message with UUID '%s' (expected UUID '%s')\n", wine_dbgstr_guid(&uuid),
552 wine_dbgstr_guid(&probe_message_id));
554 /* Check if we've either received a message without a UUID, or the UUID isn't the one we sent. If so,
555 ignore it and wait for another message. */
556 if ((ret != RPC_S_OK) || (UuidEqual(&uuid, &probe_message_id, &ret) == FALSE)) return S_OK;
559 verify_wsdxml_any_text("pSoap->Header.AnyHeaders", pSoap->Header.AnyHeaders, uri_more_tests_no_slash,
560 prefix_grog, perry, extra_info);
562 if (probe_msg != NULL)
564 static const WCHAR lager[] = {'L','a','g','e','r',0};
565 static const WCHAR more_info[] = {'M','o','r','e','I','n','f','o',0};
567 todo_wine ok(probe_msg->Types != NULL, "Probe message Types == NULL\n");
569 if (probe_msg->Types != NULL)
571 verify_wsdxml_name("probe_msg->Types->Element", probe_msg->Types->Element, uri_more_tests_no_slash,
572 prefix_grog, name_cider);
573 ok(probe_msg->Types->Next == NULL, "probe_msg->Types->Next == %p\n", probe_msg->Types->Next);
576 ok(probe_msg->Scopes == NULL, "Probe message Scopes != NULL\n");
577 verify_wsdxml_any_text("probe_msg->Any", probe_msg->Any, uri_more_tests_no_slash, prefix_grog, lager, more_info);
581 SetEvent(probe_event);
582 return S_OK;
585 static HRESULT WINAPI IWSDiscoveryPublisherNotifyImpl_ResolveHandler(IWSDiscoveryPublisherNotify *This, const WSD_SOAP_MESSAGE *pSoap, IWSDMessageParameters *pMessageParameters)
587 trace("IWSDiscoveryPublisherNotifyImpl_ResolveHandler called (%p, %p, %p)\n", This, pSoap, pMessageParameters);
588 return S_OK;
591 static const IWSDiscoveryPublisherNotifyVtbl publisherNotify_vtbl =
593 IWSDiscoveryPublisherNotifyImpl_QueryInterface,
594 IWSDiscoveryPublisherNotifyImpl_AddRef,
595 IWSDiscoveryPublisherNotifyImpl_Release,
596 IWSDiscoveryPublisherNotifyImpl_ProbeHandler,
597 IWSDiscoveryPublisherNotifyImpl_ResolveHandler
600 static BOOL create_discovery_publisher_notify(IWSDiscoveryPublisherNotify **publisherNotify)
602 IWSDiscoveryPublisherNotifyImpl *obj;
604 *publisherNotify = NULL;
606 obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*obj));
608 if (!obj)
610 trace("Out of memory creating IWSDiscoveryPublisherNotify\n");
611 return FALSE;
614 obj->IWSDiscoveryPublisherNotify_iface.lpVtbl = &publisherNotify_vtbl;
615 obj->ref = 1;
617 *publisherNotify = &obj->IWSDiscoveryPublisherNotify_iface;
619 return TRUE;
622 static void CreateDiscoveryPublisher_tests(void)
624 IWSDiscoveryPublisher *publisher = NULL;
625 IWSDiscoveryPublisher *publisher2;
626 IUnknown *unknown;
627 HRESULT rc;
628 ULONG ref;
630 rc = WSDCreateDiscoveryPublisher(NULL, NULL);
631 ok((rc == E_POINTER) || (rc == E_INVALIDARG), "WSDCreateDiscoveryPublisher(NULL, NULL) failed: %08x\n", rc);
633 rc = WSDCreateDiscoveryPublisher(NULL, &publisher);
634 ok(rc == S_OK, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: %08x\n", rc);
635 ok(publisher != NULL, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: publisher == NULL\n");
637 /* Try to query for objects */
638 rc = IWSDiscoveryPublisher_QueryInterface(publisher, &IID_IUnknown, (LPVOID*)&unknown);
639 ok(rc == S_OK,"IWSDiscoveryPublisher_QueryInterface(IID_IUnknown) failed: %08x\n", rc);
641 if (rc == S_OK)
642 IUnknown_Release(unknown);
644 rc = IWSDiscoveryPublisher_QueryInterface(publisher, &IID_IWSDiscoveryPublisher, (LPVOID*)&publisher2);
645 ok(rc == S_OK,"IWSDiscoveryPublisher_QueryInterface(IID_IWSDiscoveryPublisher) failed: %08x\n", rc);
647 if (rc == S_OK)
648 IWSDiscoveryPublisher_Release(publisher2);
650 ref = IWSDiscoveryPublisher_Release(publisher);
651 ok(ref == 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref);
654 static void CreateDiscoveryPublisher_XMLContext_tests(void)
656 IWSDiscoveryPublisher *publisher = NULL;
657 IWSDXMLContext *xmlContext, *returnedContext;
658 HRESULT rc;
659 int ref;
661 /* Test creating an XML context and supplying it to WSDCreateDiscoveryPublisher */
662 rc = WSDXMLCreateContext(&xmlContext);
663 ok(rc == S_OK, "WSDXMLCreateContext failed: %08x\n", rc);
665 rc = WSDCreateDiscoveryPublisher(xmlContext, &publisher);
666 ok(rc == S_OK, "WSDCreateDiscoveryPublisher(xmlContext, &publisher) failed: %08x\n", rc);
667 ok(publisher != NULL, "WSDCreateDiscoveryPublisher(xmlContext, &publisher) failed: publisher == NULL\n");
669 rc = IWSDiscoveryPublisher_GetXMLContext(publisher, NULL);
670 ok(rc == E_INVALIDARG, "GetXMLContext returned unexpected value with NULL argument: %08x\n", rc);
672 rc = IWSDiscoveryPublisher_GetXMLContext(publisher, &returnedContext);
673 ok(rc == S_OK, "GetXMLContext failed: %08x\n", rc);
675 ok(xmlContext == returnedContext, "GetXMLContext returned unexpected value: returnedContext == %p\n", returnedContext);
677 ref = IWSDXMLContext_Release(returnedContext);
678 ok(ref == 2, "IWSDXMLContext_Release() has %d references, should have 2\n", ref);
680 ref = IWSDiscoveryPublisher_Release(publisher);
681 ok(ref == 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref);
683 ref = IWSDXMLContext_Release(returnedContext);
684 ok(ref == 0, "IWSDXMLContext_Release() has %d references, should have 0\n", ref);
686 /* Test using a default XML context */
687 publisher = NULL;
688 returnedContext = NULL;
690 rc = WSDCreateDiscoveryPublisher(NULL, &publisher);
691 ok(rc == S_OK, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: %08x\n", rc);
692 ok(publisher != NULL, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: publisher == NULL\n");
694 rc = IWSDiscoveryPublisher_GetXMLContext(publisher, &returnedContext);
695 ok(rc == S_OK, "GetXMLContext failed: %08x\n", rc);
697 ref = IWSDXMLContext_Release(returnedContext);
698 ok(ref == 1, "IWSDXMLContext_Release() has %d references, should have 1\n", ref);
700 ref = IWSDiscoveryPublisher_Release(publisher);
701 ok(ref == 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref);
704 static void Publish_tests(void)
706 IWSDiscoveryPublisher *publisher = NULL;
707 IWSDiscoveryPublisherNotify *sink1 = NULL, *sink2 = NULL;
708 IWSDiscoveryPublisherNotifyImpl *sink1Impl = NULL, *sink2Impl = NULL;
709 char endpointReferenceString[MAX_PATH], app_sequence_string[MAX_PATH];
710 LPWSTR publisherIdW = NULL, sequenceIdW = NULL;
711 messageStorage *msgStorage;
712 WSADATA wsaData;
713 BOOL messageOK, hello_message_seen = FALSE, endpoint_reference_seen = FALSE, app_sequence_seen = FALSE;
714 BOOL metadata_version_seen = FALSE, any_header_seen = FALSE, wine_ns_seen = FALSE, body_hello_seen = FALSE;
715 BOOL any_body_seen = FALSE, types_seen = FALSE, xml_namespaces_seen = FALSE, scopes_seen = FALSE;
716 BOOL xaddrs_seen = FALSE;
717 int ret, i;
718 HRESULT rc;
719 ULONG ref;
720 char *msg;
721 WSDXML_ELEMENT *header_any_element, *body_any_element, *endpoint_any_element, *ref_param_any_element;
722 WSDXML_NAME header_any_name, another_name;
723 WSDXML_NAMESPACE ns, ns2;
724 WCHAR header_any_name_text[] = {'B','e','e','r',0};
725 static const WCHAR header_any_text[] = {'P','u','b','l','i','s','h','T','e','s','t',0};
726 static const WCHAR body_any_text[] = {'B','o','d','y','T','e','s','t',0};
727 static const WCHAR endpoint_any_text[] = {'E','n','d','P','T','e','s','t',0};
728 static const WCHAR ref_param_any_text[] = {'R','e','f','P','T','e','s','t',0};
729 static const WCHAR uri[] = {'h','t','t','p',':','/','/','w','i','n','e','.','t','e','s','t','/',0};
730 static const WCHAR prefix[] = {'w','i','n','e',0};
731 static const WCHAR uri3[] = {'h','t','t','p',':','/','/','t','h','i','r','d','.','u','r','l','/',0};
732 WSD_NAME_LIST types_list;
733 WSD_URI_LIST scopes_list, xaddrs_list;
734 unsigned char *probe_uuid_str;
736 rc = WSDCreateDiscoveryPublisher(NULL, &publisher);
737 ok(rc == S_OK, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: %08x\n", rc);
738 ok(publisher != NULL, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: publisher == NULL\n");
740 /* Test SetAddressFamily */
741 rc = IWSDiscoveryPublisher_SetAddressFamily(publisher, 12345);
742 ok(rc == E_INVALIDARG, "IWSDiscoveryPublisher_SetAddressFamily(12345) returned unexpected result: %08x\n", rc);
744 rc = IWSDiscoveryPublisher_SetAddressFamily(publisher, WSDAPI_ADDRESSFAMILY_IPV4);
745 ok(rc == S_OK, "IWSDiscoveryPublisher_SetAddressFamily(WSDAPI_ADDRESSFAMILY_IPV4) failed: %08x\n", rc);
747 /* Try to update the address family after already setting it */
748 rc = IWSDiscoveryPublisher_SetAddressFamily(publisher, WSDAPI_ADDRESSFAMILY_IPV6);
749 ok(rc == STG_E_INVALIDFUNCTION, "IWSDiscoveryPublisher_SetAddressFamily(WSDAPI_ADDRESSFAMILY_IPV6) returned unexpected result: %08x\n", rc);
751 /* Create notification sinks */
752 ok(create_discovery_publisher_notify(&sink1) == TRUE, "create_discovery_publisher_notify failed\n");
753 ok(create_discovery_publisher_notify(&sink2) == TRUE, "create_discovery_publisher_notify failed\n");
755 /* Get underlying implementation so we can check the ref count */
756 sink1Impl = impl_from_IWSDiscoveryPublisherNotify(sink1);
757 sink2Impl = impl_from_IWSDiscoveryPublisherNotify(sink2);
759 /* Attempt to unregister sink before registering it */
760 rc = IWSDiscoveryPublisher_UnRegisterNotificationSink(publisher, sink1);
761 ok(rc == E_FAIL, "IWSDiscoveryPublisher_UnRegisterNotificationSink returned unexpected result: %08x\n", rc);
763 /* Register notification sinks */
764 rc = IWSDiscoveryPublisher_RegisterNotificationSink(publisher, sink1);
765 ok(rc == S_OK, "IWSDiscoveryPublisher_RegisterNotificationSink failed: %08x\n", rc);
766 ok(sink1Impl->ref == 2, "Ref count for sink 1 is not as expected: %d\n", sink1Impl->ref);
768 rc = IWSDiscoveryPublisher_RegisterNotificationSink(publisher, sink2);
769 ok(rc == S_OK, "IWSDiscoveryPublisher_RegisterNotificationSink failed: %08x\n", rc);
770 ok(sink2Impl->ref == 2, "Ref count for sink 2 is not as expected: %d\n", sink2Impl->ref);
772 /* Unregister the first sink */
773 rc = IWSDiscoveryPublisher_UnRegisterNotificationSink(publisher, sink1);
774 ok(rc == S_OK, "IWSDiscoveryPublisher_UnRegisterNotificationSink failed: %08x\n", rc);
775 ok(sink1Impl->ref == 1, "Ref count for sink 1 is not as expected: %d\n", sink1Impl->ref);
777 /* Set up network listener */
778 publisherIdW = utf8_to_wide(publisherId);
779 if (publisherIdW == NULL) goto after_publish_test;
781 sequenceIdW = utf8_to_wide(sequenceId);
782 if (sequenceIdW == NULL) goto after_publish_test;
784 msgStorage = heap_alloc_zero(sizeof(messageStorage));
785 if (msgStorage == NULL) goto after_publish_test;
787 msgStorage->running = TRUE;
788 InitializeCriticalSection(&msgStorage->criticalSection);
790 ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
791 ok(ret == 0, "WSAStartup failed (ret = %d)\n", ret);
793 ret = start_listening_on_all_addresses(msgStorage, AF_INET);
794 ok(ret == TRUE, "Unable to listen on IPv4 addresses (ret == %d)\n", ret);
796 /* Create "any" elements for header */
797 ns.Uri = uri;
798 ns.PreferredPrefix = prefix;
800 header_any_name.LocalName = header_any_name_text;
801 header_any_name.Space = &ns;
803 rc = WSDXMLBuildAnyForSingleElement(&header_any_name, header_any_text, &header_any_element);
804 ok(rc == S_OK, "WSDXMLBuildAnyForSingleElement failed with %08x\n", rc);
806 rc = WSDXMLBuildAnyForSingleElement(&header_any_name, body_any_text, &body_any_element);
807 ok(rc == S_OK, "WSDXMLBuildAnyForSingleElement failed with %08x\n", rc);
809 rc = WSDXMLBuildAnyForSingleElement(&header_any_name, endpoint_any_text, &endpoint_any_element);
810 ok(rc == S_OK, "WSDXMLBuildAnyForSingleElement failed with %08x\n", rc);
812 rc = WSDXMLBuildAnyForSingleElement(&header_any_name, ref_param_any_text, &ref_param_any_element);
813 ok(rc == S_OK, "WSDXMLBuildAnyForSingleElement failed with %08x\n", rc);
815 /* Create types list */
816 ns2.Uri = uri_more_tests;
817 ns2.PreferredPrefix = prefix_grog;
819 another_name.LocalName = (WCHAR *) name_cider;
820 another_name.Space = &ns2;
822 types_list.Next = malloc(sizeof(WSD_NAME_LIST));
823 types_list.Element = &another_name;
825 types_list.Next->Next = NULL;
826 types_list.Next->Element = &header_any_name;
828 /* Create scopes and xaddrs lists */
829 scopes_list.Next = malloc(sizeof(WSD_URI_LIST));
830 scopes_list.Element = uri;
832 scopes_list.Next->Next = NULL;
833 scopes_list.Next->Element = uri_more_tests;
835 xaddrs_list.Next = malloc(sizeof(WSD_URI_LIST));
836 xaddrs_list.Element = uri_more_tests;
838 xaddrs_list.Next->Next = NULL;
839 xaddrs_list.Next->Element = uri3;
841 /* Publish the service */
842 rc = IWSDiscoveryPublisher_PublishEx(publisher, publisherIdW, 1, 1, 1, sequenceIdW, &types_list, &scopes_list,
843 &xaddrs_list, header_any_element, ref_param_any_element, NULL, endpoint_any_element, body_any_element);
845 WSDFreeLinkedMemory(header_any_element);
846 WSDFreeLinkedMemory(body_any_element);
847 WSDFreeLinkedMemory(endpoint_any_element);
848 WSDFreeLinkedMemory(ref_param_any_element);
849 free(types_list.Next);
850 free(scopes_list.Next);
851 free(xaddrs_list.Next);
853 ok(rc == S_OK, "Publish failed: %08x\n", rc);
855 /* Wait up to 2 seconds for messages to be received */
856 if (WaitForMultipleObjects(msgStorage->numThreadHandles, msgStorage->threadHandles, TRUE, 2000) == WAIT_TIMEOUT)
858 /* Wait up to 1 more second for threads to terminate */
859 msgStorage->running = FALSE;
860 WaitForMultipleObjects(msgStorage->numThreadHandles, msgStorage->threadHandles, TRUE, 1000);
863 DeleteCriticalSection(&msgStorage->criticalSection);
865 /* Verify we've received a message */
866 ok(msgStorage->messageCount >= 1, "No messages received\n");
868 sprintf(endpointReferenceString, "<wsa:EndpointReference><wsa:Address>%s</wsa:Address><wsa:ReferenceParameters>"
869 "<wine:Beer>RefPTest</wine:Beer></wsa:ReferenceParameters><wine:Beer>EndPTest</wine:Beer>"
870 "</wsa:EndpointReference>", publisherId);
872 sprintf(app_sequence_string, "<wsd:AppSequence InstanceId=\"1\" SequenceId=\"%s\" MessageNumber=\"1\"></wsd:AppSequence>",
873 sequenceId);
875 messageOK = FALSE;
877 /* Check we're received the correct message */
878 for (i = 0; i < msgStorage->messageCount; i++)
880 msg = msgStorage->messages[i];
881 messageOK = FALSE;
883 hello_message_seen = (strstr(msg, "<wsa:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Hello</wsa:Action>") != NULL);
884 endpoint_reference_seen = (strstr(msg, endpointReferenceString) != NULL);
885 app_sequence_seen = (strstr(msg, app_sequence_string) != NULL);
886 metadata_version_seen = (strstr(msg, "<wsd:MetadataVersion>1</wsd:MetadataVersion>") != NULL);
887 any_header_seen = (strstr(msg, "<wine:Beer>PublishTest</wine:Beer>") != NULL);
888 wine_ns_seen = (strstr(msg, "xmlns:wine=\"http://wine.test/\"") != NULL);
889 body_hello_seen = (strstr(msg, "<soap:Body><wsd:Hello") != NULL);
890 any_body_seen = (strstr(msg, "<wine:Beer>BodyTest</wine:Beer>") != NULL);
891 types_seen = (strstr(msg, "<wsd:Types>grog:Cider wine:Beer</wsd:Types>") != NULL);
892 scopes_seen = (strstr(msg, "<wsd:Scopes>http://wine.test/ http://more.tests/</wsd:Scopes>") != NULL);
893 xaddrs_seen = (strstr(msg, "<wsd:XAddrs>http://more.tests/ http://third.url/</wsd:XAddrs>") != NULL);
894 xml_namespaces_seen = (strstr(msg, "xmlns:wine=\"http://wine.test/\" xmlns:grog=\"http://more.tests/\"") != NULL);
895 messageOK = hello_message_seen && endpoint_reference_seen && app_sequence_seen && metadata_version_seen &&
896 any_header_seen && wine_ns_seen && body_hello_seen && any_body_seen && types_seen && xml_namespaces_seen &&
897 scopes_seen && xaddrs_seen;
899 if (messageOK) break;
902 for (i = 0; i < msgStorage->messageCount; i++)
904 heap_free(msgStorage->messages[i]);
907 heap_free(msgStorage);
909 ok(hello_message_seen == TRUE, "Hello message not received\n");
910 ok(endpoint_reference_seen == TRUE, "EndpointReference not received\n");
911 ok(app_sequence_seen == TRUE, "AppSequence not received\n");
912 ok(metadata_version_seen == TRUE, "MetadataVersion not received\n");
913 ok(messageOK == TRUE, "Hello message metadata not received\n");
914 ok(any_header_seen == TRUE, "Custom header not received\n");
915 ok(wine_ns_seen == TRUE, "Wine namespace not received\n");
916 ok(body_hello_seen == TRUE, "Body and Hello elements not received\n");
917 ok(any_body_seen == TRUE, "Custom body element not received\n");
918 ok(types_seen == TRUE, "Types not received\n");
919 ok(xml_namespaces_seen == TRUE, "XML namespaces not received\n");
920 ok(scopes_seen == TRUE, "Scopes not received\n");
921 ok(xaddrs_seen == TRUE, "XAddrs not received\n");
923 after_publish_test:
925 heap_free(publisherIdW);
926 heap_free(sequenceIdW);
928 /* Test the receiving of a probe message */
929 probe_event = CreateEventW(NULL, TRUE, FALSE, NULL);
931 UuidCreate(&probe_message_id);
932 UuidToStringA(&probe_message_id, &probe_uuid_str);
934 ok(probe_uuid_str != NULL, "Failed to create UUID for probe message\n");
936 if (probe_uuid_str != NULL)
938 char probe_message[sizeof(testProbeMessage) + 50];
939 sprintf(probe_message, testProbeMessage, probe_uuid_str);
941 ok(send_udp_multicast_of_type(probe_message, strlen(probe_message), AF_INET) == TRUE, "Sending Probe message failed\n");
942 ok(WaitForSingleObject(probe_event, 2000) == WAIT_OBJECT_0, "Probe message not received\n");
944 RpcStringFreeA(&probe_uuid_str);
947 CloseHandle(probe_event);
949 ref = IWSDiscoveryPublisher_Release(publisher);
950 ok(ref == 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref);
952 /* Check that the sinks have been released by the publisher */
953 ok(sink1Impl->ref == 1, "Ref count for sink 1 is not as expected: %d\n", sink1Impl->ref);
954 ok(sink2Impl->ref == 1, "Ref count for sink 2 is not as expected: %d\n", sink2Impl->ref);
956 /* Release the sinks */
957 IWSDiscoveryPublisherNotify_Release(sink1);
958 IWSDiscoveryPublisherNotify_Release(sink2);
960 WSACleanup();
963 static void UnPublish_tests(void)
965 IWSDiscoveryPublisher *publisher = NULL;
966 IWSDiscoveryPublisherNotify *sink1 = NULL;
967 char endpoint_reference_string[MAX_PATH], app_sequence_string[MAX_PATH];
968 LPWSTR publisherIdW = NULL, sequenceIdW = NULL;
969 messageStorage *msg_storage;
970 WSADATA wsa_data;
971 BOOL message_ok, hello_message_seen = FALSE, endpoint_reference_seen = FALSE, app_sequence_seen = FALSE;
972 BOOL wine_ns_seen = FALSE, body_hello_seen = FALSE, any_body_seen = FALSE;
973 int ret, i;
974 HRESULT rc;
975 ULONG ref;
976 char *msg;
977 WSDXML_ELEMENT *body_any_element;
978 WSDXML_NAME body_any_name;
979 WSDXML_NAMESPACE ns;
980 WCHAR body_any_name_text[] = {'B','e','e','r',0};
981 static const WCHAR body_any_text[] = {'B','o','d','y','T','e','s','t',0};
982 static const WCHAR uri[] = {'h','t','t','p',':','/','/','w','i','n','e','.','t','e','s','t','/',0};
983 static const WCHAR prefix[] = {'w','i','n','e',0};
985 rc = WSDCreateDiscoveryPublisher(NULL, &publisher);
986 ok(rc == S_OK, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: %08x\n", rc);
987 ok(publisher != NULL, "WSDCreateDiscoveryPublisher(NULL, &publisher) failed: publisher == NULL\n");
989 rc = IWSDiscoveryPublisher_SetAddressFamily(publisher, WSDAPI_ADDRESSFAMILY_IPV4);
990 ok(rc == S_OK, "IWSDiscoveryPublisher_SetAddressFamily(WSDAPI_ADDRESSFAMILY_IPV4) failed: %08x\n", rc);
992 /* Create notification sink */
993 ok(create_discovery_publisher_notify(&sink1) == TRUE, "create_discovery_publisher_notify failed\n");
994 rc = IWSDiscoveryPublisher_RegisterNotificationSink(publisher, sink1);
995 ok(rc == S_OK, "IWSDiscoveryPublisher_RegisterNotificationSink failed: %08x\n", rc);
997 /* Set up network listener */
998 publisherIdW = utf8_to_wide(publisherId);
999 if (publisherIdW == NULL) goto after_unpublish_test;
1001 sequenceIdW = utf8_to_wide(sequenceId);
1002 if (sequenceIdW == NULL) goto after_unpublish_test;
1004 msg_storage = heap_alloc_zero(sizeof(messageStorage));
1005 if (msg_storage == NULL) goto after_unpublish_test;
1007 msg_storage->running = TRUE;
1008 InitializeCriticalSection(&msg_storage->criticalSection);
1010 ret = WSAStartup(MAKEWORD(2, 2), &wsa_data);
1011 ok(ret == 0, "WSAStartup failed (ret = %d)\n", ret);
1013 ret = start_listening_on_all_addresses(msg_storage, AF_INET);
1014 ok(ret == TRUE, "Unable to listen on IPv4 addresses (ret == %d)\n", ret);
1016 /* Create "any" elements for header */
1017 ns.Uri = uri;
1018 ns.PreferredPrefix = prefix;
1020 body_any_name.LocalName = body_any_name_text;
1021 body_any_name.Space = &ns;
1023 rc = WSDXMLBuildAnyForSingleElement(&body_any_name, body_any_text, &body_any_element);
1024 ok(rc == S_OK, "WSDXMLBuildAnyForSingleElement failed with %08x\n", rc);
1026 /* Unpublish the service */
1027 rc = IWSDiscoveryPublisher_UnPublish(publisher, publisherIdW, 1, 1, sequenceIdW, body_any_element);
1029 WSDFreeLinkedMemory(body_any_element);
1031 ok(rc == S_OK, "Unpublish failed: %08x\n", rc);
1033 /* Wait up to 2 seconds for messages to be received */
1034 if (WaitForMultipleObjects(msg_storage->numThreadHandles, msg_storage->threadHandles, TRUE, 2000) == WAIT_TIMEOUT)
1036 /* Wait up to 1 more second for threads to terminate */
1037 msg_storage->running = FALSE;
1038 WaitForMultipleObjects(msg_storage->numThreadHandles, msg_storage->threadHandles, TRUE, 1000);
1041 DeleteCriticalSection(&msg_storage->criticalSection);
1043 /* Verify we've received a message */
1044 ok(msg_storage->messageCount >= 1, "No messages received\n");
1046 sprintf(endpoint_reference_string, "<wsa:EndpointReference><wsa:Address>%s</wsa:Address></wsa:EndpointReference>",
1047 publisherId);
1048 sprintf(app_sequence_string, "<wsd:AppSequence InstanceId=\"1\" SequenceId=\"%s\" MessageNumber=\"1\"></wsd:AppSequence>",
1049 sequenceId);
1051 message_ok = FALSE;
1053 /* Check we're received the correct message */
1054 for (i = 0; i < msg_storage->messageCount; i++)
1056 msg = msg_storage->messages[i];
1057 message_ok = FALSE;
1059 hello_message_seen = (strstr(msg, "<wsa:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Bye</wsa:Action>") != NULL);
1060 endpoint_reference_seen = (strstr(msg, endpoint_reference_string) != NULL);
1061 app_sequence_seen = (strstr(msg, app_sequence_string) != NULL);
1062 wine_ns_seen = (strstr(msg, "xmlns:wine=\"http://wine.test/\"") != NULL);
1063 body_hello_seen = (strstr(msg, "<soap:Body><wsd:Bye") != NULL);
1064 any_body_seen = (strstr(msg, "<wine:Beer>BodyTest</wine:Beer>") != NULL);
1065 message_ok = hello_message_seen && endpoint_reference_seen && app_sequence_seen && wine_ns_seen &&
1066 body_hello_seen && any_body_seen;
1068 if (message_ok) break;
1071 for (i = 0; i < msg_storage->messageCount; i++)
1073 heap_free(msg_storage->messages[i]);
1076 heap_free(msg_storage);
1078 ok(hello_message_seen == TRUE, "Bye message not received\n");
1079 ok(endpoint_reference_seen == TRUE, "EndpointReference not received\n");
1080 ok(app_sequence_seen == TRUE, "AppSequence not received\n");
1081 ok(message_ok == TRUE, "Bye message metadata not received\n");
1082 ok(wine_ns_seen == TRUE, "Wine namespace not received\n");
1083 ok(body_hello_seen == TRUE, "Body and Bye elements not received\n");
1084 ok(any_body_seen == TRUE, "Custom body element not received\n");
1086 after_unpublish_test:
1088 heap_free(publisherIdW);
1089 heap_free(sequenceIdW);
1091 ref = IWSDiscoveryPublisher_Release(publisher);
1092 ok(ref == 0, "IWSDiscoveryPublisher_Release() has %d references, should have 0\n", ref);
1094 /* Release the sinks */
1095 IWSDiscoveryPublisherNotify_Release(sink1);
1097 WSACleanup();
1100 enum firewall_op
1102 APP_ADD,
1103 APP_REMOVE
1106 static BOOL is_process_elevated(void)
1108 HANDLE token;
1109 if (OpenProcessToken( GetCurrentProcess(), TOKEN_QUERY, &token ))
1111 TOKEN_ELEVATION_TYPE type;
1112 DWORD size;
1113 BOOL ret;
1115 ret = GetTokenInformation( token, TokenElevationType, &type, sizeof(type), &size );
1116 CloseHandle( token );
1117 return (ret && type == TokenElevationTypeFull);
1119 return FALSE;
1122 static BOOL is_firewall_enabled(void)
1124 HRESULT hr, init;
1125 INetFwMgr *mgr = NULL;
1126 INetFwPolicy *policy = NULL;
1127 INetFwProfile *profile = NULL;
1128 VARIANT_BOOL enabled = VARIANT_FALSE;
1130 init = CoInitializeEx( 0, COINIT_APARTMENTTHREADED );
1132 hr = CoCreateInstance( &CLSID_NetFwMgr, NULL, CLSCTX_INPROC_SERVER, &IID_INetFwMgr,
1133 (void **)&mgr );
1134 ok( hr == S_OK, "got %08x\n", hr );
1135 if (hr != S_OK) goto done;
1137 hr = INetFwMgr_get_LocalPolicy( mgr, &policy );
1138 ok( hr == S_OK, "got %08x\n", hr );
1139 if (hr != S_OK) goto done;
1141 hr = INetFwPolicy_get_CurrentProfile( policy, &profile );
1142 if (hr != S_OK) goto done;
1144 hr = INetFwProfile_get_FirewallEnabled( profile, &enabled );
1145 ok( hr == S_OK, "got %08x\n", hr );
1147 done:
1148 if (policy) INetFwPolicy_Release( policy );
1149 if (profile) INetFwProfile_Release( profile );
1150 if (mgr) INetFwMgr_Release( mgr );
1151 if (SUCCEEDED( init )) CoUninitialize();
1152 return (enabled == VARIANT_TRUE);
1155 static HRESULT set_firewall( enum firewall_op op )
1157 static const WCHAR testW[] = {'w','s','d','a','p','i','_','t','e','s','t',0};
1158 HRESULT hr, init;
1159 INetFwMgr *mgr = NULL;
1160 INetFwPolicy *policy = NULL;
1161 INetFwProfile *profile = NULL;
1162 INetFwAuthorizedApplication *app = NULL;
1163 INetFwAuthorizedApplications *apps = NULL;
1164 BSTR name, image = SysAllocStringLen( NULL, MAX_PATH );
1166 if (!GetModuleFileNameW( NULL, image, MAX_PATH ))
1168 SysFreeString( image );
1169 return E_FAIL;
1171 init = CoInitializeEx( 0, COINIT_APARTMENTTHREADED );
1173 hr = CoCreateInstance( &CLSID_NetFwMgr, NULL, CLSCTX_INPROC_SERVER, &IID_INetFwMgr,
1174 (void **)&mgr );
1175 ok( hr == S_OK, "got %08x\n", hr );
1176 if (hr != S_OK) goto done;
1178 hr = INetFwMgr_get_LocalPolicy( mgr, &policy );
1179 ok( hr == S_OK, "got %08x\n", hr );
1180 if (hr != S_OK) goto done;
1182 hr = INetFwPolicy_get_CurrentProfile( policy, &profile );
1183 if (hr != S_OK) goto done;
1185 hr = INetFwProfile_get_AuthorizedApplications( profile, &apps );
1186 ok( hr == S_OK, "got %08x\n", hr );
1187 if (hr != S_OK) goto done;
1189 hr = CoCreateInstance( &CLSID_NetFwAuthorizedApplication, NULL, CLSCTX_INPROC_SERVER,
1190 &IID_INetFwAuthorizedApplication, (void **)&app );
1191 ok( hr == S_OK, "got %08x\n", hr );
1192 if (hr != S_OK) goto done;
1194 hr = INetFwAuthorizedApplication_put_ProcessImageFileName( app, image );
1195 if (hr != S_OK) goto done;
1197 name = SysAllocString( testW );
1198 hr = INetFwAuthorizedApplication_put_Name( app, name );
1199 SysFreeString( name );
1200 ok( hr == S_OK, "got %08x\n", hr );
1201 if (hr != S_OK) goto done;
1203 if (op == APP_ADD)
1204 hr = INetFwAuthorizedApplications_Add( apps, app );
1205 else if (op == APP_REMOVE)
1206 hr = INetFwAuthorizedApplications_Remove( apps, image );
1207 else
1208 hr = E_INVALIDARG;
1210 done:
1211 if (app) INetFwAuthorizedApplication_Release( app );
1212 if (apps) INetFwAuthorizedApplications_Release( apps );
1213 if (policy) INetFwPolicy_Release( policy );
1214 if (profile) INetFwProfile_Release( profile );
1215 if (mgr) INetFwMgr_Release( mgr );
1216 if (SUCCEEDED( init )) CoUninitialize();
1217 SysFreeString( image );
1218 return hr;
1221 START_TEST(discovery)
1223 BOOL firewall_enabled = is_firewall_enabled();
1224 HRESULT hr;
1226 if (firewall_enabled)
1228 if (!is_process_elevated())
1230 skip("no privileges, skipping tests to avoid firewall dialog\n");
1231 return;
1233 if ((hr = set_firewall(APP_ADD)) != S_OK)
1235 skip("can't authorize app in firewall %08x\n", hr);
1236 return;
1240 CoInitialize(NULL);
1242 CreateDiscoveryPublisher_tests();
1243 CreateDiscoveryPublisher_XMLContext_tests();
1244 Publish_tests();
1245 UnPublish_tests();
1247 CoUninitialize();
1248 if (firewall_enabled) set_firewall(APP_REMOVE);