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