xref: /reactos/win32ss/user/user32/misc/ddeserver.c (revision ba3f0743)
1 /*
2  * DDEML library
3  *
4  * Copyright 1997 Alexandre Julliard
5  * Copyright 1997 Len White
6  * Copyright 1999 Keith Matthews
7  * Copyright 2000 Corel
8  * Copyright 2001 Eric Pouech
9  * Copyright 2003, 2004, 2005 Dmitry Timoshkov
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include <user32.h>
27 
28 WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
29 
30 static const WCHAR szServerNameClass[] = L"DDEMLMom";
31 const char WDML_szServerConvClassA[] = "DDEMLAnsiServer";
32 const WCHAR WDML_szServerConvClassW[] = L"DDEMLUnicodeServer";
33 
34 static LRESULT CALLBACK WDML_ServerNameProc(HWND, UINT, WPARAM, LPARAM);
35 static LRESULT CALLBACK WDML_ServerConvProc(HWND, UINT, WPARAM, LPARAM);
36 
37 /******************************************************************************
38  * DdePostAdvise [USER32.@]  Send transaction to DDE callback function.
39  *
40  * PARAMS
41  *	idInst	  [I] Instance identifier
42  *	hszTopic  [I] Handle to topic name string
43  *	hszItem	  [I] Handle to item name string
44  *
45  * RETURNS
46  *    Success: TRUE
47  *    Failure: FALSE
48  */
49 BOOL WINAPI DdePostAdvise(DWORD idInst, HSZ hszTopic, HSZ hszItem)
50 {
51     WDML_INSTANCE*	pInstance;
52     WDML_LINK*		pLink;
53     HDDEDATA		hDdeData;
54     HGLOBAL		hItemData;
55     WDML_CONV*		pConv;
56     ATOM		atom;
57     UINT		count;
58 
59     TRACE("(%d,%p,%p)\n", idInst, hszTopic, hszItem);
60 
61     pInstance = WDML_GetInstance(idInst);
62 
63     if (pInstance == NULL)
64         return FALSE;
65 
66     atom = WDML_MakeAtomFromHsz(hszItem);
67     if (!atom) return FALSE;
68 
69     /* first compute the number of links which will trigger a message */
70     count = 0;
71     for (pLink = pInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next)
72     {
73 	if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0)
74 	{
75 	    count++;
76 	}
77     }
78     if (count >= CADV_LATEACK)
79     {
80 	FIXME("too high value for count\n");
81 	count &= 0xFFFF;
82     }
83 
84     for (pLink = pInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next)
85     {
86 	if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0)
87 	{
88 	    hDdeData = WDML_InvokeCallback(pInstance, XTYP_ADVREQ, pLink->uFmt, pLink->hConv,
89 					   hszTopic, hszItem, 0, --count, 0);
90 
91 	    if (hDdeData == CBR_BLOCK)
92 	    {
93 		/* MS doc is not consistent here */
94 		FIXME("CBR_BLOCK returned for ADVREQ\n");
95 		continue;
96 	    }
97 	    if (hDdeData)
98 	    {
99 		if (pLink->transactionType & XTYPF_NODATA)
100 		{
101 		    TRACE("no data\n");
102 		    hItemData = 0;
103 		}
104 		else
105 		{
106 		    TRACE("with data\n");
107 
108 		    hItemData = WDML_DataHandle2Global(hDdeData, FALSE, FALSE, FALSE, FALSE);
109 		}
110 
111 		pConv = WDML_GetConv(pLink->hConv, TRUE);
112 
113 		if (pConv == NULL)
114 		{
115 		    if (!WDML_IsAppOwned(hDdeData))  DdeFreeDataHandle(hDdeData);
116 		    goto theError;
117 		}
118 
119 		if (!PostMessageW(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
120 				  PackDDElParam(WM_DDE_DATA, (UINT_PTR)hItemData, atom)))
121 		{
122 		    ERR("post message failed\n");
123                     pConv->wStatus &= ~ST_CONNECTED;
124                     pConv->instance->lastError = DMLERR_POSTMSG_FAILED;
125 		    if (!WDML_IsAppOwned(hDdeData))  DdeFreeDataHandle(hDdeData);
126 		    GlobalFree(hItemData);
127 		    goto theError;
128 		}
129                 if (!WDML_IsAppOwned(hDdeData))  DdeFreeDataHandle(hDdeData);
130 	    }
131 	}
132     }
133     return TRUE;
134 
135  theError:
136     GlobalDeleteAtom(atom);
137     return FALSE;
138 }
139 
140 
141 /******************************************************************************
142  * DdeNameService [USER32.@]  {Un}registers service name of DDE server
143  *
144  * PARAMS
145  *    idInst [I] Instance identifier
146  *    hsz1   [I] Handle to service name string
147  *    hsz2   [I] Reserved
148  *    afCmd  [I] Service name flags
149  *
150  * RETURNS
151  *    Success: Non-zero
152  *    Failure: 0
153  */
154 HDDEDATA WINAPI DdeNameService(DWORD idInst, HSZ hsz1, HSZ hsz2, UINT afCmd)
155 {
156     WDML_SERVER*	pServer;
157     WDML_INSTANCE*	pInstance;
158     HWND 		hwndServer;
159     WNDCLASSEXW  	wndclass;
160 
161     TRACE("(%d,%p,%p,%x)\n", idInst, hsz1, hsz2, afCmd);
162 
163     /*  First check instance
164      */
165     pInstance = WDML_GetInstance(idInst);
166     if  (pInstance == NULL)
167     {
168 	TRACE("Instance not found as initialised\n");
169 	/*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
170         return NULL;
171     }
172 
173     if (hsz2 != 0L)
174     {
175 	/*	Illegal, reserved parameter
176 	 */
177 	pInstance->lastError = DMLERR_INVALIDPARAMETER;
178 	WARN("Reserved parameter no-zero !!\n");
179         return NULL;
180     }
181     if (hsz1 == 0 && !(afCmd & DNS_UNREGISTER))
182     {
183 	/*	don't know if we should check this but it makes sense
184 	 *	why supply REGISTER or filter flags if de-registering all
185 	 */
186 	TRACE("General unregister unexpected flags\n");
187 	pInstance->lastError = DMLERR_INVALIDPARAMETER;
188         return NULL;
189     }
190 
191     switch (afCmd & (DNS_REGISTER | DNS_UNREGISTER))
192     {
193     case DNS_REGISTER:
194 	pServer = WDML_FindServer(pInstance, hsz1, 0);
195 	if (pServer)
196 	{
197 	    ERR("Trying to register already registered service!\n");
198 	    pInstance->lastError = DMLERR_DLL_USAGE;
199             return NULL;
200 	}
201 
202 	TRACE("Adding service name\n");
203 
204 	WDML_IncHSZ(pInstance, hsz1);
205 
206 	pServer = WDML_AddServer(pInstance, hsz1, 0);
207 
208 	WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_REGISTER,
209 				 pServer->atomService, pServer->atomServiceSpec);
210 
211 	wndclass.cbSize        = sizeof(wndclass);
212 	wndclass.style         = 0;
213 	wndclass.lpfnWndProc   = WDML_ServerNameProc;
214 	wndclass.cbClsExtra    = 0;
215 	wndclass.cbWndExtra    = 2 * sizeof(ULONG_PTR);
216 	wndclass.hInstance     = 0;
217 	wndclass.hIcon         = 0;
218 	wndclass.hCursor       = 0;
219 	wndclass.hbrBackground = 0;
220 	wndclass.lpszMenuName  = NULL;
221 	wndclass.lpszClassName = szServerNameClass;
222 	wndclass.hIconSm       = 0;
223 
224 	RegisterClassExW(&wndclass);
225 
226 	hwndServer = CreateWindowW(szServerNameClass, NULL,
227 				   WS_POPUP, 0, 0, 0, 0,
228 				   0, 0, 0, 0);
229 
230 	SetWindowLongPtrW(hwndServer, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
231 	SetWindowLongPtrW(hwndServer, GWL_WDML_SERVER, (ULONG_PTR)pServer);
232 	TRACE("Created nameServer=%p for instance=%08x\n", hwndServer, idInst);
233 
234 	pServer->hwndServer = hwndServer;
235 	break;
236 
237     case DNS_UNREGISTER:
238 	if (hsz1 == 0L)
239 	{
240 	    /* General unregister situation
241 	     * terminate all server side pending conversations
242 	     */
243 	    while (pInstance->servers)
244 		WDML_RemoveServer(pInstance, pInstance->servers->hszService, 0);
245 	    pInstance->servers = NULL;
246 	    TRACE("General de-register - finished\n");
247 	}
248 	else
249 	{
250 	    WDML_RemoveServer(pInstance, hsz1, 0L);
251 	}
252 	break;
253     }
254 
255     if (afCmd & (DNS_FILTERON | DNS_FILTEROFF))
256     {
257 	/*	Set filter flags on to hold notifications of connection
258 	 */
259 	pServer = WDML_FindServer(pInstance, hsz1, 0);
260 	if (!pServer)
261 	{
262 	    /*  trying to filter where no service names !!
263 	     */
264 	    pInstance->lastError = DMLERR_DLL_USAGE;
265             return NULL;
266 	}
267 	else
268 	{
269 	    pServer->filterOn = (afCmd & DNS_FILTERON) != 0;
270 	}
271     }
272     return (HDDEDATA)TRUE;
273 }
274 
275 /******************************************************************
276  *		WDML_CreateServerConv
277  *
278  *
279  */
280 static WDML_CONV* WDML_CreateServerConv(WDML_INSTANCE* pInstance, HWND hwndClient,
281 					HWND hwndServerName, HSZ hszApp, HSZ hszTopic)
282 {
283     HWND	hwndServerConv;
284     WDML_CONV*	pConv;
285 
286     if (pInstance->unicode)
287     {
288         WNDCLASSEXW wndclass;
289 
290         wndclass.cbSize        = sizeof(wndclass);
291         wndclass.style         = 0;
292         wndclass.lpfnWndProc   = WDML_ServerConvProc;
293         wndclass.cbClsExtra    = 0;
294         wndclass.cbWndExtra    = 2 * sizeof(ULONG_PTR);
295         wndclass.hInstance     = 0;
296         wndclass.hIcon         = 0;
297         wndclass.hCursor       = 0;
298         wndclass.hbrBackground = 0;
299         wndclass.lpszMenuName  = NULL;
300         wndclass.lpszClassName = WDML_szServerConvClassW;
301         wndclass.hIconSm       = 0;
302 
303         RegisterClassExW(&wndclass);
304 
305         hwndServerConv = CreateWindowW(WDML_szServerConvClassW, 0,
306 				       WS_CHILD, 0, 0, 0, 0,
307 				       hwndServerName, 0, 0, 0);
308     }
309     else
310     {
311         WNDCLASSEXA wndclass;
312 
313         wndclass.cbSize        = sizeof(wndclass);
314         wndclass.style         = 0;
315         wndclass.lpfnWndProc   = WDML_ServerConvProc;
316         wndclass.cbClsExtra    = 0;
317         wndclass.cbWndExtra    = 2 * sizeof(ULONG_PTR);
318         wndclass.hInstance     = 0;
319         wndclass.hIcon         = 0;
320         wndclass.hCursor       = 0;
321         wndclass.hbrBackground = 0;
322         wndclass.lpszMenuName  = NULL;
323         wndclass.lpszClassName = WDML_szServerConvClassA;
324         wndclass.hIconSm       = 0;
325 
326         RegisterClassExA(&wndclass);
327 
328         hwndServerConv = CreateWindowA(WDML_szServerConvClassA, 0,
329                                       WS_CHILD, 0, 0, 0, 0,
330                                       hwndServerName, 0, 0, 0);
331     }
332 
333     TRACE("Created convServer=%p (nameServer=%p) for instance=%08x unicode=%d\n",
334 	  hwndServerConv, hwndServerName, pInstance->instanceID, pInstance->unicode);
335 
336     pConv = WDML_AddConv(pInstance, WDML_SERVER_SIDE, hszApp, hszTopic,
337 			 hwndClient, hwndServerConv);
338     if (pConv)
339     {
340 	SetWindowLongPtrW(hwndServerConv, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
341 	SetWindowLongPtrW(hwndServerConv, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
342 
343 	/* this should be the only place using SendMessage for WM_DDE_ACK */
344         /* note: sent messages shall not use packing */
345 	SendMessageW(hwndClient, WM_DDE_ACK, (WPARAM)hwndServerConv,
346 		     MAKELPARAM(WDML_MakeAtomFromHsz(hszApp), WDML_MakeAtomFromHsz(hszTopic)));
347 	/* we assume we're connected since we've sent an answer...
348 	 * I'm not sure what we can do... it doesn't look like the return value
349 	 * of SendMessage is used... sigh...
350 	 */
351 	pConv->wStatus |= ST_CONNECTED;
352     }
353     else
354     {
355 	DestroyWindow(hwndServerConv);
356     }
357     return pConv;
358 }
359 
360 /******************************************************************
361  *		WDML_ServerNameProc
362  *
363  *
364  */
365 static LRESULT CALLBACK WDML_ServerNameProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam)
366 {
367     HWND		hwndClient;
368     HSZ			hszApp, hszTop;
369     HDDEDATA		hDdeData;
370     WDML_INSTANCE*	pInstance;
371     UINT_PTR		uiLo, uiHi;
372 
373     switch (iMsg)
374     {
375     case WM_DDE_INITIATE:
376 
377 	/* wParam         -- sending window handle
378 	   LOWORD(lParam) -- application atom
379 	   HIWORD(lParam) -- topic atom */
380 
381 	TRACE("WM_DDE_INITIATE message received!\n");
382 	hwndClient = (HWND)wParam;
383 
384 	pInstance = WDML_GetInstanceFromWnd(hwndServer);
385 	if (!pInstance) return 0;
386 	TRACE("idInst=%d, threadID=0x%x\n", pInstance->instanceID, GetCurrentThreadId());
387 
388 	/* don't free DDEParams, since this is a broadcast */
389 	UnpackDDElParam(WM_DDE_INITIATE, lParam, &uiLo, &uiHi);
390 
391 	hszApp = WDML_MakeHszFromAtom(pInstance, uiLo);
392 	hszTop = WDML_MakeHszFromAtom(pInstance, uiHi);
393 
394 	if (!(pInstance->CBFflags & CBF_FAIL_CONNECTIONS))
395 	{
396 	    BOOL 		self = FALSE;
397 	    CONVCONTEXT		cc;
398 	    CONVCONTEXT*	pcc = NULL;
399 	    WDML_CONV*		pConv;
400 	    char		buf[256];
401 
402 	    if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
403 		WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
404 	    {
405 		self = TRUE;
406 	    }
407 	    /* FIXME: so far, we don't grab distant convcontext, so only check if remote is
408 	     * handled under DDEML, and if so build a default context
409 	     */
410            if ((GetClassNameA(hwndClient, buf, sizeof(buf)) &&
411                 lstrcmpiA(buf, WDML_szClientConvClassA) == 0) ||
412                (GetClassNameW(hwndClient, (LPWSTR)buf, sizeof(buf)/sizeof(WCHAR)) &&
413                 lstrcmpiW((LPWSTR)buf, WDML_szClientConvClassW) == 0))
414 	    {
415 		pcc = &cc;
416 		memset(pcc, 0, sizeof(*pcc));
417 		pcc->cb = sizeof(*pcc);
418 		pcc->iCodePage = IsWindowUnicode(hwndClient) ? CP_WINUNICODE : CP_WINANSI;
419 	    }
420 	    if ((pInstance->CBFflags & CBF_FAIL_SELFCONNECTIONS) && self)
421 	    {
422 		TRACE("Don't do self connection as requested\n");
423 	    }
424 	    else if (hszApp && hszTop)
425 	    {
426 		WDML_SERVER*	pServer = (WDML_SERVER*)GetWindowLongPtrW(hwndServer, GWL_WDML_SERVER);
427 
428 		/* check filters for name service */
429 		if (!pServer->filterOn || DdeCmpStringHandles(pServer->hszService, hszApp) == 0)
430 		{
431 		    /* pass on to the callback  */
432 		    hDdeData = WDML_InvokeCallback(pInstance, XTYP_CONNECT,
433 						   0, 0, hszTop, hszApp, 0, (ULONG_PTR)pcc, self);
434 		    if ((ULONG_PTR)hDdeData)
435 		    {
436 			pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer,
437 						      hszApp, hszTop);
438                         if (pConv)
439                         {
440                             if (pcc) pConv->wStatus |= ST_ISLOCAL;
441                             WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
442                                                 hszTop, hszApp, 0, (ULONG_PTR)pcc, self);
443                         }
444 		    }
445 		}
446 	    }
447 	    else if (pInstance->servers)
448 	    {
449 		/* pass on to the callback  */
450 		hDdeData = WDML_InvokeCallback(pInstance, XTYP_WILDCONNECT,
451 					       0, 0, hszTop, hszApp, 0, (ULONG_PTR)pcc, self);
452 
453 		if (hDdeData == CBR_BLOCK)
454 		{
455 		    /* MS doc is not consistent here */
456 		    FIXME("CBR_BLOCK returned for WILDCONNECT\n");
457 		}
458 		else if ((ULONG_PTR)hDdeData != 0)
459 		{
460 		    HSZPAIR*	hszp;
461 
462 		    hszp = (HSZPAIR*)DdeAccessData(hDdeData, NULL);
463 		    if (hszp)
464 		    {
465 			int	i;
466 			for (i = 0; hszp[i].hszSvc && hszp[i].hszTopic; i++)
467 			{
468 			    pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer,
469 							  hszp[i].hszSvc, hszp[i].hszTopic);
470                             if (pConv)
471                             {
472                                 if (pcc) pConv->wStatus |= ST_ISLOCAL;
473                                 WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
474                                                     hszp[i].hszTopic, hszp[i].hszSvc, 0, (ULONG_PTR)pcc, self);
475                             }
476 			}
477 			DdeUnaccessData(hDdeData);
478 		    }
479                     if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData);
480 		}
481 	    }
482 	}
483 
484 	return 0;
485 
486     case WM_DDE_REQUEST:
487 	FIXME("WM_DDE_REQUEST message received!\n");
488 	return 0;
489     case WM_DDE_ADVISE:
490 	FIXME("WM_DDE_ADVISE message received!\n");
491 	return 0;
492     case WM_DDE_UNADVISE:
493 	FIXME("WM_DDE_UNADVISE message received!\n");
494 	return 0;
495     case WM_DDE_EXECUTE:
496 	FIXME("WM_DDE_EXECUTE message received!\n");
497 	return 0;
498     case WM_DDE_POKE:
499 	FIXME("WM_DDE_POKE message received!\n");
500 	return 0;
501     case WM_DDE_TERMINATE:
502 	FIXME("WM_DDE_TERMINATE message received!\n");
503 	return 0;
504     default:
505 	break;
506     }
507 
508     return DefWindowProcW(hwndServer, iMsg, wParam, lParam);
509 }
510 
511 /******************************************************************
512  *		WDML_ServerQueueRequest
513  *
514  *
515  */
516 static	WDML_XACT*	WDML_ServerQueueRequest(WDML_CONV* pConv, LPARAM lParam)
517 {
518     UINT_PTR		uiLo, uiHi;
519     WDML_XACT*		pXAct;
520 
521     UnpackDDElParam(WM_DDE_REQUEST, lParam, &uiLo, &uiHi);
522 
523     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST,
524 				  uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi));
525     if (pXAct) pXAct->atom = uiHi;
526     return pXAct;
527 }
528 
529 /******************************************************************
530  *		WDML_ServerHandleRequest
531  *
532  *
533  */
534 static	WDML_QUEUE_STATE WDML_ServerHandleRequest(WDML_CONV* pConv, WDML_XACT* pXAct)
535 {
536     HDDEDATA		hDdeData = 0;
537     BOOL		fAck = TRUE;
538 
539     if (!(pConv->instance->CBFflags & CBF_FAIL_REQUESTS))
540     {
541 
542 	hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_REQUEST, pXAct->wFmt, (HCONV)pConv,
543 				       pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
544     }
545 
546     switch ((ULONG_PTR)hDdeData)
547     {
548     case 0:
549 	TRACE("No data returned from the Callback\n");
550 	fAck = FALSE;
551 	break;
552 
553     case (ULONG_PTR)CBR_BLOCK:
554 	return WDML_QS_BLOCK;
555 
556     default:
557         {
558 	    HGLOBAL	hMem = WDML_DataHandle2Global(hDdeData, TRUE, FALSE, FALSE, FALSE);
559 	    if (!PostMessageW(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
560 			      ReuseDDElParam(pXAct->lParam, WM_DDE_REQUEST, WM_DDE_DATA,
561 					     (UINT_PTR)hMem, (UINT_PTR)pXAct->atom)))
562 	    {
563                 pConv->instance->lastError = DMLERR_POSTMSG_FAILED;
564 		DdeFreeDataHandle(hDdeData);
565 		GlobalFree(hMem);
566 		fAck = FALSE;
567 	    }
568 	}
569 	break;
570     }
571 
572     WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, fAck, pXAct->atom, pXAct->lParam, WM_DDE_REQUEST);
573 
574     WDML_DecHSZ(pConv->instance, pXAct->hszItem);
575 
576     return WDML_QS_HANDLED;
577 }
578 
579 /******************************************************************
580  *		WDML_ServerQueueAdvise
581  *
582  *
583  */
584 static	WDML_XACT*	WDML_ServerQueueAdvise(WDML_CONV* pConv, LPARAM lParam)
585 {
586     UINT_PTR		uiLo, uiHi;
587     WDML_XACT*		pXAct;
588 
589     /* XTYP_ADVSTART transaction:
590        establish link and save link info to InstanceInfoTable */
591 
592     if (!UnpackDDElParam(WM_DDE_ADVISE, lParam, &uiLo, &uiHi))
593 	return NULL;
594 
595     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE,
596 				  0, WDML_MakeHszFromAtom(pConv->instance, uiHi));
597     if (pXAct)
598     {
599 	pXAct->hMem = (HGLOBAL)uiLo;
600 	pXAct->atom = uiHi;
601     }
602     return pXAct;
603 }
604 
605 /******************************************************************
606  *		WDML_ServerHandleAdvise
607  *
608  *
609  */
610 static	WDML_QUEUE_STATE WDML_ServerHandleAdvise(WDML_CONV* pConv, WDML_XACT* pXAct)
611 {
612     UINT		uType;
613     WDML_LINK*		pLink;
614     DDEADVISE*		pDdeAdvise;
615     HDDEDATA		hDdeData = 0;
616     BOOL		fAck = TRUE;
617 
618     pDdeAdvise = GlobalLock(pXAct->hMem);
619     uType = XTYP_ADVSTART |
620 	    (pDdeAdvise->fDeferUpd ? XTYPF_NODATA : 0) |
621 	    (pDdeAdvise->fAckReq ? XTYPF_ACKREQ : 0);
622 
623     if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES))
624     {
625 	hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_ADVSTART, pDdeAdvise->cfFormat,
626 				       (HCONV)pConv, pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
627     }
628 
629     switch ((ULONG_PTR)hDdeData)
630     {
631     case 0:
632 	TRACE("No data returned from the Callback\n");
633 	fAck = FALSE;
634 	break;
635 
636     case (ULONG_PTR)CBR_BLOCK:
637 	return WDML_QS_BLOCK;
638 
639     default:
640 	/* billx: first to see if the link is already created. */
641 	pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
642 			      pXAct->hszItem, TRUE, pDdeAdvise->cfFormat);
643 
644 	if (pLink != NULL)
645 	{
646 	    /* we found a link, and only need to modify it in case it changes */
647 	    pLink->transactionType = uType;
648 	}
649 	else
650 	{
651 	    TRACE("Adding Link with hConv %p\n", pConv);
652 	    WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
653 			 uType, pXAct->hszItem, pDdeAdvise->cfFormat);
654 	}
655 	break;
656     }
657 
658     GlobalUnlock(pXAct->hMem);
659     if (fAck)
660     {
661 	GlobalFree(pXAct->hMem);
662     }
663     pXAct->hMem = 0;
664 
665     WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, fAck, pXAct->atom, pXAct->lParam, WM_DDE_ADVISE);
666 
667     WDML_DecHSZ(pConv->instance, pXAct->hszItem);
668 
669     return WDML_QS_HANDLED;
670 }
671 
672 /******************************************************************
673  *		WDML_ServerQueueUnadvise
674  *
675  *
676  */
677 static	WDML_XACT* WDML_ServerQueueUnadvise(WDML_CONV* pConv, LPARAM lParam)
678 {
679     UINT_PTR		uiLo, uiHi;
680     WDML_XACT*		pXAct;
681 
682     UnpackDDElParam(WM_DDE_UNADVISE, lParam, &uiLo, &uiHi);
683 
684     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE,
685 				  uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi));
686     if (pXAct) pXAct->atom = uiHi;
687     return pXAct;
688 }
689 
690 /******************************************************************
691  *		WDML_ServerHandleUnadvise
692  *
693  *
694  */
695 static	WDML_QUEUE_STATE WDML_ServerHandleUnadvise(WDML_CONV* pConv, WDML_XACT* pXAct)
696 {
697     WDML_LINK*	pLink;
698 
699     if (pXAct->hszItem == NULL || pXAct->wFmt == 0)
700     {
701 	ERR("Unsupported yet options (null item or clipboard format)\n");
702 	return WDML_QS_ERROR;
703     }
704 
705     pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
706 			  pXAct->hszItem, TRUE, pXAct->wFmt);
707     if (pLink == NULL)
708     {
709 	ERR("Couldn't find link for %p, dropping request\n", pXAct->hszItem);
710 	FreeDDElParam(WM_DDE_UNADVISE, pXAct->lParam);
711 	return WDML_QS_ERROR;
712     }
713 
714     if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES))
715     {
716 	WDML_InvokeCallback(pConv->instance, XTYP_ADVSTOP, pXAct->wFmt, (HCONV)pConv,
717 			    pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
718     }
719 
720     WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
721 		    pXAct->hszItem, pXAct->wFmt);
722 
723     /* send back ack */
724     WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, TRUE, pXAct->atom,
725                  pXAct->lParam, WM_DDE_UNADVISE);
726 
727     WDML_DecHSZ(pConv->instance, pXAct->hszItem);
728 
729     return WDML_QS_HANDLED;
730 }
731 
732 /******************************************************************
733  *		WDML_QueueExecute
734  *
735  *
736  */
737 static	WDML_XACT* WDML_ServerQueueExecute(WDML_CONV* pConv, LPARAM lParam)
738 {
739     WDML_XACT*	pXAct;
740 
741     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0);
742     if (pXAct)
743     {
744 	pXAct->hMem    = (HGLOBAL)lParam;
745     }
746     return pXAct;
747 }
748 
749 static BOOL data_looks_unicode( const WCHAR *data, DWORD size )
750 {
751     DWORD i;
752 
753     if (size % sizeof(WCHAR)) return FALSE;
754     for (i = 0; i < size / sizeof(WCHAR); i++) if (data[i] > 255) return FALSE;
755     return TRUE;
756 }
757 
758 /* convert data to Unicode, unless it looks like it's already Unicode */
759 static HDDEDATA map_A_to_W( DWORD instance, void *ptr, DWORD size )
760 {
761     HDDEDATA ret;
762     DWORD len;
763     const char *end;
764 
765     if (!data_looks_unicode( ptr, size ))
766     {
767         if ((end = memchr( ptr, 0, size ))) size = end + 1 - (const char *)ptr;
768         len = MultiByteToWideChar( CP_ACP, 0, ptr, size, NULL, 0 );
769         ret = DdeCreateDataHandle( instance, NULL, len * sizeof(WCHAR), 0, 0, CF_TEXT, 0);
770         MultiByteToWideChar( CP_ACP, 0, ptr, size, (WCHAR *)DdeAccessData(ret, NULL), len );
771     }
772     else ret = DdeCreateDataHandle( instance, ptr, size, 0, 0, CF_TEXT, 0 );
773 
774     return ret;
775 }
776 
777 /* convert data to ASCII, unless it looks like it's not in Unicode format */
778 static HDDEDATA map_W_to_A( DWORD instance, void *ptr, DWORD size )
779 {
780     HDDEDATA ret;
781     DWORD len;
782     const WCHAR *end;
783 
784     if (data_looks_unicode( ptr, size ))
785     {
786         size /= sizeof(WCHAR);
787         if ((end = memchrW( ptr, 0, size ))) size = end + 1 - (const WCHAR *)ptr;
788         len = WideCharToMultiByte( CP_ACP, 0, ptr, size, NULL, 0, NULL, NULL );
789         ret = DdeCreateDataHandle( instance, NULL, len, 0, 0, CF_TEXT, 0);
790         WideCharToMultiByte( CP_ACP, 0, ptr, size, (char *)DdeAccessData(ret, NULL), len, NULL, NULL );
791     }
792     else ret = DdeCreateDataHandle( instance, ptr, size, 0, 0, CF_TEXT, 0 );
793 
794     return ret;
795 }
796 
797  /******************************************************************
798  *		WDML_ServerHandleExecute
799  *
800  *
801  */
802 static	WDML_QUEUE_STATE WDML_ServerHandleExecute(WDML_CONV* pConv, WDML_XACT* pXAct)
803 {
804     HDDEDATA	hDdeData = DDE_FNOTPROCESSED;
805     BOOL	fAck = FALSE, fBusy = FALSE;
806 
807     if (!(pConv->instance->CBFflags & CBF_FAIL_EXECUTES))
808     {
809 	LPVOID	ptr = GlobalLock(pXAct->hMem);
810         DWORD size = GlobalSize(pXAct->hMem);
811 
812 	if (ptr)
813 	{
814             if (pConv->instance->unicode)  /* Unicode server, try to map A->W */
815                 hDdeData = map_A_to_W( pConv->instance->instanceID, ptr, size );
816             else if (!IsWindowUnicode( pConv->hwndClient )) /* ASCII server and client, try to map W->A */
817                 hDdeData = map_W_to_A( pConv->instance->instanceID, ptr, size );
818             else
819                 hDdeData = DdeCreateDataHandle(pConv->instance->instanceID, ptr, size, 0, 0, CF_TEXT, 0);
820 	    GlobalUnlock(pXAct->hMem);
821 	}
822 	hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_EXECUTE, 0, (HCONV)pConv,
823 				       pConv->hszTopic, 0, hDdeData, 0L, 0L);
824     }
825 
826     switch ((ULONG_PTR)hDdeData)
827     {
828     case (ULONG_PTR)CBR_BLOCK:
829 	return WDML_QS_BLOCK;
830 
831     case DDE_FACK:
832 	fAck = TRUE;
833 	break;
834     case DDE_FBUSY:
835 	fBusy = TRUE;
836 	break;
837     default:
838 	FIXME("Unsupported returned value %p\n", hDdeData);
839 	/* fall through */
840     case DDE_FNOTPROCESSED:
841 	break;
842     }
843     WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, fBusy, fAck, (UINT_PTR)pXAct->hMem, 0, 0);
844 
845     return WDML_QS_HANDLED;
846 }
847 
848 /******************************************************************
849  *		WDML_ServerQueuePoke
850  *
851  *
852  */
853 static	WDML_XACT* WDML_ServerQueuePoke(WDML_CONV* pConv, LPARAM lParam)
854 {
855     UINT_PTR		uiLo, uiHi;
856     WDML_XACT*		pXAct;
857 
858     UnpackDDElParam(WM_DDE_POKE, lParam, &uiLo, &uiHi);
859 
860     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE,
861 				  0, WDML_MakeHszFromAtom(pConv->instance, uiHi));
862     if (pXAct)
863     {
864 	pXAct->atom = uiHi;
865 	pXAct->hMem = (HGLOBAL)uiLo;
866     }
867     return pXAct;
868 }
869 
870 /******************************************************************
871  *		WDML_ServerHandlePoke
872  *
873  *
874  */
875 static	WDML_QUEUE_STATE WDML_ServerHandlePoke(WDML_CONV* pConv, WDML_XACT* pXAct)
876 {
877     DDEPOKE*		pDdePoke;
878     HDDEDATA		hDdeData;
879     BOOL		fBusy = FALSE, fAck = FALSE;
880 
881     pDdePoke = GlobalLock(pXAct->hMem);
882     if (!pDdePoke)
883     {
884 	return WDML_QS_ERROR;
885     }
886 
887     if (!(pConv->instance->CBFflags & CBF_FAIL_POKES))
888     {
889 	hDdeData = DdeCreateDataHandle(pConv->instance->instanceID, pDdePoke->Value,
890 				       GlobalSize(pXAct->hMem) - FIELD_OFFSET(DDEPOKE, Value),
891 				       0, 0, pDdePoke->cfFormat, 0);
892 	if (hDdeData)
893 	{
894 	    HDDEDATA	hDdeDataOut;
895 
896 	    hDdeDataOut = WDML_InvokeCallback(pConv->instance, XTYP_POKE, pDdePoke->cfFormat,
897 					      (HCONV)pConv, pConv->hszTopic, pXAct->hszItem,
898 					      hDdeData, 0, 0);
899 	    switch ((ULONG_PTR)hDdeDataOut)
900 	    {
901 	    case DDE_FACK:
902 		fAck = TRUE;
903 		break;
904 	    case DDE_FBUSY:
905 		fBusy = TRUE;
906 		break;
907 	    default:
908 		FIXME("Unsupported returned value %p\n", hDdeDataOut);
909 		/* fal through */
910 	    case DDE_FNOTPROCESSED:
911 		break;
912 	    }
913 	    DdeFreeDataHandle(hDdeData);
914 	}
915     }
916     GlobalUnlock(pXAct->hMem);
917 
918     if (!fAck)
919     {
920 	GlobalFree(pXAct->hMem);
921     }
922     WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, fBusy, fAck, pXAct->atom, pXAct->lParam, WM_DDE_POKE);
923 
924     WDML_DecHSZ(pConv->instance, pXAct->hszItem);
925 
926     return WDML_QS_HANDLED;
927 }
928 
929 /******************************************************************
930  *		WDML_ServerQueueTerminate
931  *
932  *
933  */
934 static	WDML_XACT*	WDML_ServerQueueTerminate(WDML_CONV* pConv, LPARAM lParam)
935 {
936     WDML_XACT*	pXAct;
937 
938     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_TERMINATE, 0, 0);
939     return pXAct;
940 }
941 
942 /******************************************************************
943  *		WDML_ServerHandleTerminate
944  *
945  *
946  */
947 static	WDML_QUEUE_STATE WDML_ServerHandleTerminate(WDML_CONV* pConv, WDML_XACT* pXAct)
948 {
949     /* billx: two things to remove: the conv, and associated links.
950      * Respond with another WM_DDE_TERMINATE iMsg.
951      */
952     if (!(pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS))
953     {
954 	WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv, 0, 0,
955 			    0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
956     }
957     PostMessageW(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0);
958     WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
959 
960     return WDML_QS_HANDLED;
961 }
962 
963 /******************************************************************
964  *		WDML_ServerHandle
965  *
966  *
967  */
968 WDML_QUEUE_STATE WDML_ServerHandle(WDML_CONV* pConv, WDML_XACT* pXAct)
969 {
970     WDML_QUEUE_STATE	qs = WDML_QS_ERROR;
971 
972     switch (pXAct->ddeMsg)
973     {
974     case WM_DDE_INITIATE:
975 	FIXME("WM_DDE_INITIATE shouldn't be there!\n");
976 	break;
977     case WM_DDE_REQUEST:
978 	qs = WDML_ServerHandleRequest(pConv, pXAct);
979 	break;
980 
981     case WM_DDE_ADVISE:
982 	qs = WDML_ServerHandleAdvise(pConv, pXAct);
983 	break;
984 
985     case WM_DDE_UNADVISE:
986 	qs = WDML_ServerHandleUnadvise(pConv, pXAct);
987 	break;
988 
989     case WM_DDE_EXECUTE:
990 	qs = WDML_ServerHandleExecute(pConv, pXAct);
991 	break;
992 
993     case WM_DDE_POKE:
994 	qs = WDML_ServerHandlePoke(pConv, pXAct);
995 	break;
996 
997     case WM_DDE_TERMINATE:
998 	qs = WDML_ServerHandleTerminate(pConv, pXAct);
999 	break;
1000 
1001     case WM_DDE_ACK:
1002 	WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n");
1003 	break;
1004 
1005     default:
1006 	FIXME("Unsupported message %d\n", pXAct->ddeMsg);
1007     }
1008     return qs;
1009 }
1010 
1011 /******************************************************************
1012  *		WDML_ServerConvProc
1013  *
1014  *
1015  */
1016 static LRESULT CALLBACK WDML_ServerConvProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam)
1017 {
1018     WDML_INSTANCE*	pInstance;
1019     WDML_CONV*		pConv;
1020     WDML_XACT*		pXAct = NULL;
1021 
1022     TRACE("%p %04x %08lx %08lx\n", hwndServer, iMsg, wParam, lParam);
1023 
1024     if (iMsg == WM_DESTROY)
1025     {
1026 	pConv = WDML_GetConvFromWnd(hwndServer);
1027 	if (pConv && !(pConv->wStatus & ST_TERMINATED))
1028 	{
1029 	    WDML_ServerHandleTerminate(pConv, NULL);
1030 	}
1031     }
1032     if (iMsg < WM_DDE_FIRST || iMsg > WM_DDE_LAST)
1033     {
1034         return IsWindowUnicode(hwndServer) ? DefWindowProcW(hwndServer, iMsg, wParam, lParam) :
1035                                              DefWindowProcA(hwndServer, iMsg, wParam, lParam);
1036     }
1037 
1038     pInstance = WDML_GetInstanceFromWnd(hwndServer);
1039     pConv = WDML_GetConvFromWnd(hwndServer);
1040 
1041     if (!pConv)
1042     {
1043 	ERR("Got a message (%x) on a not known conversation, dropping request\n", iMsg);
1044         return 0;
1045     }
1046     if (pConv->hwndClient != WIN_GetFullHandle( (HWND)wParam ) || pConv->hwndServer != hwndServer)
1047     {
1048 	ERR("mismatch between C/S windows and conversation\n");
1049         return 0;
1050     }
1051     if (pConv->instance != pInstance || pConv->instance == NULL)
1052     {
1053 	ERR("mismatch in instances\n");
1054         return 0;
1055     }
1056 
1057     switch (iMsg)
1058     {
1059     case WM_DDE_INITIATE:
1060 	FIXME("WM_DDE_INITIATE message received!\n");
1061 	break;
1062 
1063     case WM_DDE_REQUEST:
1064 	pXAct = WDML_ServerQueueRequest(pConv, lParam);
1065 	break;
1066 
1067     case WM_DDE_ADVISE:
1068 	pXAct = WDML_ServerQueueAdvise(pConv, lParam);
1069 	break;
1070 
1071     case WM_DDE_UNADVISE:
1072 	pXAct = WDML_ServerQueueUnadvise(pConv, lParam);
1073 	break;
1074 
1075     case WM_DDE_EXECUTE:
1076 	pXAct = WDML_ServerQueueExecute(pConv, lParam);
1077 	break;
1078 
1079     case WM_DDE_POKE:
1080 	pXAct = WDML_ServerQueuePoke(pConv, lParam);
1081 	break;
1082 
1083     case WM_DDE_TERMINATE:
1084 	pXAct = WDML_ServerQueueTerminate(pConv, lParam);
1085 	break;
1086 
1087     case WM_DDE_ACK:
1088 	WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n");
1089 	break;
1090 
1091     default:
1092 	FIXME("Unsupported message %x\n", iMsg);
1093         break;
1094     }
1095 
1096     if (pXAct)
1097     {
1098 	pXAct->lParam = lParam;
1099 
1100 	if ((pConv->wStatus & ST_BLOCKED) || WDML_ServerHandle(pConv, pXAct) == WDML_QS_BLOCK)
1101 	{
1102             TRACE("Transactions are blocked, add to the queue and exit\n");
1103 	    WDML_QueueTransaction(pConv, pXAct);
1104 	}
1105 	else
1106 	{
1107 	    WDML_FreeTransaction(pInstance, pXAct, TRUE);
1108 	}
1109     }
1110     else
1111         pConv->instance->lastError = DMLERR_MEMORY_ERROR;
1112 
1113     return 0;
1114 }
1115