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