When including 'wine/port.h', include it first.
[wine] / dlls / user / dde / server.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2
3 /*
4  * DDEML library
5  *
6  * Copyright 1997 Alexandre Julliard
7  * Copyright 1997 Len White
8  * Copyright 1999 Keith Matthews
9  * Copyright 2000 Corel
10  * Copyright 2001 Eric Pouech
11  */
12
13 #include <string.h>
14 #include "winbase.h"
15 #include "windef.h"
16 #include "wingdi.h"
17 #include "winuser.h"
18 #include "winerror.h"
19 #include "dde.h"
20 #include "ddeml.h"
21 #include "win.h"
22 #include "debugtools.h"
23 #include "dde/dde_private.h"
24
25 DEFAULT_DEBUG_CHANNEL(ddeml);
26
27 static const char  szServerNameClassA[] = "DdeServerNameAnsi";
28 const char  WDML_szServerConvClassA[] = "DdeServerConvAnsi";
29 const WCHAR WDML_szServerConvClassW[] = {'D','d','e','S','e','r','v','e','r','C','o','n','v','U','n','i','c','o','d','e',0};
30
31 static LRESULT CALLBACK WDML_ServerNameProc(HWND, UINT, WPARAM, LPARAM);
32 static LRESULT CALLBACK WDML_ServerConvProc(HWND, UINT, WPARAM, LPARAM);
33
34 /******************************************************************************
35  * DdePostAdvise [USER32.@]  Send transaction to DDE callback function.
36  *
37  * PARAMS
38  *      idInst    [I] Instance identifier
39  *      hszTopic  [I] Handle to topic name string
40  *      hszItem   [I] Handle to item name string
41  *
42  * RETURNS
43  *    Success: TRUE
44  *    Failure: FALSE
45  */
46 BOOL WINAPI DdePostAdvise(DWORD idInst, HSZ hszTopic, HSZ hszItem)
47 {
48     WDML_INSTANCE*      pInstance = NULL;
49     WDML_LINK*          pLink = NULL;
50     HDDEDATA            hDdeData = 0, hItemData = 0;
51     WDML_CONV*          pConv = NULL;
52     ATOM                atom = 0;
53     UINT                count;
54
55     TRACE("(%ld,0x%x,0x%x)\n", idInst, hszTopic, hszItem);
56     
57     EnterCriticalSection(&WDML_CritSect);
58
59     pInstance = WDML_GetInstance(idInst);
60     
61     if (pInstance == NULL || pInstance->links == NULL)
62     {
63         goto theError;
64     }
65     
66     atom = WDML_MakeAtomFromHsz(hszItem);
67     if (!atom) goto theError;
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                     /* FIXME: wrong if app owned... */
116                     DdeFreeDataHandle(hDdeData);
117                     goto theError;
118                 }
119                 
120                 if (!PostMessageA(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
121                                   PackDDElParam(WM_DDE_DATA, (UINT)hItemData, atom)))
122                 {
123                     ERR("post message failed\n");
124                     /* FIXME: wrong if app owned... */
125                     DdeFreeDataHandle(hDdeData);
126                     GlobalFree(hItemData);
127                     goto theError;
128                 }                   
129             }
130         }
131     }
132     LeaveCriticalSection(&WDML_CritSect);
133     return TRUE;
134  theError:
135     LeaveCriticalSection(&WDML_CritSect);
136     if (atom) 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     HDDEDATA            hDdeData;
159     HWND                hwndServer;
160     WNDCLASSEXA         wndclass;
161     
162     hDdeData = (HDDEDATA)NULL;
163     
164     TRACE("(%ld,0x%x,0x%x,%d)\n", idInst, hsz1, hsz2, afCmd);
165     
166     EnterCriticalSection(&WDML_CritSect);
167     
168     /*  First check instance
169      */
170     pInstance = WDML_GetInstance(idInst);
171     if  (pInstance == NULL)
172     {
173         TRACE("Instance not found as initialised\n");
174         /*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
175         goto theError;
176     }
177     
178     if (hsz2 != 0L)
179     {
180         /*      Illegal, reserved parameter
181          */
182         pInstance->lastError = DMLERR_INVALIDPARAMETER;
183         WARN("Reserved parameter no-zero !!\n");
184         goto theError;
185     }
186     if (hsz1 == 0 && afCmd != DNS_UNREGISTER)
187     {
188         /*      don't know if we should check this but it makes sense
189          *      why supply REGISTER or filter flags if de-registering all
190          */
191         TRACE("General unregister unexpected flags\n");
192         pInstance->lastError = DMLERR_INVALIDPARAMETER;
193         goto theError;
194     }
195     
196     switch (afCmd)
197     {
198     case DNS_REGISTER:
199         pServer = WDML_FindServer(pInstance, hsz1, 0);
200         if (pServer)
201         {
202             ERR("Trying to register already registered service!\n");
203             pInstance->lastError = DMLERR_DLL_USAGE;
204             goto theError;
205         }           
206
207         TRACE("Adding service name\n");
208             
209         WDML_IncHSZ(pInstance, hsz1);
210             
211         pServer = WDML_AddServer(pInstance, hsz1, 0);
212             
213         WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_REGISTER, 
214                                  pServer->atomService, pServer->atomServiceSpec);
215         
216         wndclass.cbSize        = sizeof(wndclass);
217         wndclass.style         = 0;
218         wndclass.lpfnWndProc   = WDML_ServerNameProc;
219         wndclass.cbClsExtra    = 0;
220         wndclass.cbWndExtra    = 2 * sizeof(DWORD);
221         wndclass.hInstance     = 0;
222         wndclass.hIcon         = 0;
223         wndclass.hCursor       = 0;
224         wndclass.hbrBackground = 0;
225         wndclass.lpszMenuName  = NULL;
226         wndclass.lpszClassName = szServerNameClassA;
227         wndclass.hIconSm       = 0;
228         
229         RegisterClassExA(&wndclass);
230         
231         LeaveCriticalSection(&WDML_CritSect);
232         hwndServer = CreateWindowA(szServerNameClassA, NULL,
233                                    WS_POPUP, 0, 0, 0, 0,
234                                    0, 0, 0, 0);
235         EnterCriticalSection(&WDML_CritSect);
236
237         SetWindowLongA(hwndServer, GWL_WDML_INSTANCE, (DWORD)pInstance);
238         SetWindowLongA(hwndServer, GWL_WDML_SERVER, (DWORD)pServer);
239         TRACE("Created nameServer=%04x for instance=%08lx\n", hwndServer, idInst);
240         
241         pServer->hwndServer = hwndServer;
242         break;
243
244     case DNS_UNREGISTER:
245         if (hsz1 == 0L)
246         {
247             /* General unregister situation
248              * terminate all server side pending conversations 
249              */
250             while (pInstance->servers)
251                 WDML_RemoveServer(pInstance, pInstance->servers->hszService, 0);
252             pInstance->servers = NULL;
253             TRACE("General de-register - finished\n");
254         }
255         else
256         {
257             WDML_RemoveServer(pInstance, hsz1, 0L);
258         }
259         break;
260     case DNS_FILTERON:
261     case DNS_FILTEROFF:
262         /*      Set filter flags on to hold notifications of connection
263          */
264         pServer = WDML_FindServer(pInstance, hsz1, 0);
265         if (!pServer)
266         {
267             /*  trying to filter where no service names !!
268              */
269             pInstance->lastError = DMLERR_DLL_USAGE;
270             goto theError;
271         } 
272         else 
273         {
274             pServer->filterOn = (afCmd == DNS_FILTERON);
275         }
276         break;
277     }
278     LeaveCriticalSection(&WDML_CritSect);
279     return (HDDEDATA)TRUE;
280
281  theError:
282     LeaveCriticalSection(&WDML_CritSect);
283     return FALSE;
284 }
285
286 /******************************************************************
287  *              WDML_CreateServerConv
288  *
289  *
290  */
291 static WDML_CONV* WDML_CreateServerConv(WDML_INSTANCE* pInstance, HWND hwndClient, 
292                                         HWND hwndServerName, HSZ hszApp, HSZ hszTopic)
293 {
294     HWND        hwndServerConv;
295     WDML_CONV*  pConv;
296     
297     if (pInstance->unicode)
298     {
299         WNDCLASSEXW     wndclass;
300
301         wndclass.cbSize        = sizeof(wndclass);
302         wndclass.style         = 0;
303         wndclass.lpfnWndProc   = WDML_ServerConvProc;
304         wndclass.cbClsExtra    = 0;
305         wndclass.cbWndExtra    = 2 * sizeof(DWORD);
306         wndclass.hInstance     = 0;
307         wndclass.hIcon         = 0;
308         wndclass.hCursor       = 0;
309         wndclass.hbrBackground = 0;
310         wndclass.lpszMenuName  = NULL;
311         wndclass.lpszClassName = WDML_szServerConvClassW;
312         wndclass.hIconSm       = 0;
313
314         RegisterClassExW(&wndclass);
315     
316         hwndServerConv = CreateWindowW(WDML_szServerConvClassW, 0,
317                                        WS_CHILD, 0, 0, 0, 0,
318                                        hwndServerName, 0, 0, 0);
319     }
320     else
321     {
322         WNDCLASSEXA     wndclass;
323
324         wndclass.cbSize        = sizeof(wndclass);
325         wndclass.style         = 0;
326         wndclass.lpfnWndProc   = WDML_ServerConvProc;
327         wndclass.cbClsExtra    = 0;
328         wndclass.cbWndExtra    = 2 * sizeof(DWORD);
329         wndclass.hInstance     = 0;
330         wndclass.hIcon         = 0;
331         wndclass.hCursor       = 0;
332         wndclass.hbrBackground = 0;
333         wndclass.lpszMenuName  = NULL;
334         wndclass.lpszClassName = WDML_szServerConvClassA;
335         wndclass.hIconSm       = 0;
336
337         RegisterClassExA(&wndclass);
338     
339         hwndServerConv = CreateWindowA(WDML_szServerConvClassA, 0,
340                                        WS_CHILD, 0, 0, 0, 0,
341                                        hwndServerName, 0, 0, 0);
342     }
343
344     TRACE("Created convServer=%04x (nameServer=%04x) for instance=%08lx\n", 
345           hwndServerConv, hwndServerName, pInstance->instanceID);
346     
347     pConv = WDML_AddConv(pInstance, WDML_SERVER_SIDE, hszApp, hszTopic, 
348                          hwndClient, hwndServerConv);
349     if (pConv)
350     {
351         SetWindowLongA(hwndServerConv, GWL_WDML_INSTANCE, (DWORD)pInstance);
352         SetWindowLongA(hwndServerConv, GWL_WDML_CONVERSATION, (DWORD)pConv);
353
354         /* this should be the only place using SendMessage for WM_DDE_ACK */
355         SendMessageA(hwndClient, WM_DDE_ACK, (WPARAM)hwndServerConv,
356                      PackDDElParam(WM_DDE_ACK, 
357                                    WDML_MakeAtomFromHsz(hszApp), 
358                                    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                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         TRACE("idInst=%ld, threadID=0x%lx\n", pInstance->instanceID, GetCurrentThreadId());
398         if (!pInstance) return 0;
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                  strcmp(buf, WDML_szClientConvClassA) == 0) ||
424                 (GetClassNameW(hwndClient, (LPWSTR)buf, sizeof(buf)/sizeof(WCHAR)) && 
425                  lstrcmpW((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*)GetWindowLongA(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, (DWORD)pcc, self);
446                     if ((UINT)hDdeData)
447                     {
448                         pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer, 
449                                                       hszApp, hszTop);
450                         if (pConv && pcc) pConv->wStatus |= ST_ISLOCAL;
451                     }
452                 }
453             }
454             else if (pInstance->servers)
455             {
456                 /* pass on to the callback  */
457                 hDdeData = WDML_InvokeCallback(pInstance, XTYP_WILDCONNECT,
458                                                0, 0, hszTop, hszApp, 0, (DWORD)pcc, self);
459
460                 if (hDdeData == CBR_BLOCK)
461                 {
462                     /* MS doc is not consistent here */
463                     FIXME("CBR_BLOCK returned for WILDCONNECT\n");
464                 }
465                 else if ((UINT)hDdeData != 0)
466                 {
467                     HSZPAIR*    hszp;
468                 
469                     hszp = (HSZPAIR*)DdeAccessData(hDdeData, NULL);
470                     if (hszp)
471                     {
472                         int     i;
473                         for (i = 0; hszp[i].hszSvc && hszp[i].hszTopic; i++)
474                         {
475                             pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer, 
476                                                           hszp[i].hszSvc, hszp[i].hszTopic);
477                             if (pConv && pcc) pConv->wStatus |= ST_ISLOCAL;
478                         }       
479                         DdeUnaccessData(hDdeData);
480                     }
481                 }
482             }
483         }
484         /*
485           billx: make a conv and add it to the server list - 
486           this can be delayed when link is created for the conv. NO NEED !!!
487         */
488         
489         return 0;
490         
491         
492     case WM_DDE_REQUEST:
493         FIXME("WM_DDE_REQUEST message received!\n");
494         return 0;
495     case WM_DDE_ADVISE:
496         FIXME("WM_DDE_ADVISE message received!\n");
497         return 0;
498     case WM_DDE_UNADVISE:
499         FIXME("WM_DDE_UNADVISE message received!\n");
500         return 0;
501     case WM_DDE_EXECUTE:
502         FIXME("WM_DDE_EXECUTE message received!\n");
503         return 0;
504     case WM_DDE_POKE:
505         FIXME("WM_DDE_POKE message received!\n");
506         return 0;
507     case WM_DDE_TERMINATE:
508         FIXME("WM_DDE_TERMINATE message received!\n");
509         return 0;
510
511     }
512     
513     return DefWindowProcA(hwndServer, iMsg, wParam, lParam);
514 }
515
516 /******************************************************************
517  *              WDML_ServerQueueRequest
518  *
519  *
520  */
521 static  WDML_XACT*      WDML_ServerQueueRequest(WDML_CONV* pConv, LPARAM lParam)
522 {
523     UINT                uiLo, uiHi;
524     WDML_XACT*          pXAct;
525
526     UnpackDDElParam(WM_DDE_REQUEST, lParam, &uiLo, &uiHi);
527
528     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST, 
529                                   uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi));
530     if (pXAct) pXAct->atom = uiHi;
531     return pXAct;
532 }
533
534 /******************************************************************
535  *              WDML_ServerHandleRequest
536  *
537  *
538  */
539 static  WDML_QUEUE_STATE WDML_ServerHandleRequest(WDML_CONV* pConv, WDML_XACT* pXAct)
540 {
541     HDDEDATA            hDdeData = 0;
542     WDML_QUEUE_STATE    ret = WDML_QS_HANDLED;
543
544     if (!(pConv->instance->CBFflags & CBF_FAIL_REQUESTS))
545     {
546             
547         hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_REQUEST, pXAct->wFmt, (HCONV)pConv, 
548                                        pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
549     }
550
551     switch (hDdeData)
552     {
553     case 0:
554         WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, FALSE, pXAct->hszItem, 
555                      pXAct->lParam, WM_DDE_REQUEST);
556         break;
557     case CBR_BLOCK:
558         ret = WDML_QS_BLOCK;
559         break;
560     default:
561         {
562             HGLOBAL     hMem = WDML_DataHandle2Global(hDdeData, FALSE, FALSE, FALSE, FALSE);
563             if (!PostMessageA(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
564                               ReuseDDElParam(pXAct->lParam, WM_DDE_REQUEST, WM_DDE_DATA, 
565                                              (UINT)hMem, (UINT)pXAct->atom)))
566             {
567                 DdeFreeDataHandle(hDdeData);
568                 GlobalFree(hMem);
569             }
570         }
571         break;
572     }
573     WDML_DecHSZ(pConv->instance, pXAct->hszItem);
574     return ret;
575 }
576
577 /******************************************************************
578  *              WDML_ServerQueueAdvise
579  *
580  *
581  */
582 static  WDML_XACT*      WDML_ServerQueueAdvise(WDML_CONV* pConv, LPARAM lParam)
583 {
584     UINT                uiLo, uiHi;
585     WDML_XACT*          pXAct;
586
587     /* XTYP_ADVSTART transaction: 
588        establish link and save link info to InstanceInfoTable */
589         
590     UnpackDDElParam(WM_DDE_ADVISE, lParam, &uiLo, &uiHi);
591         
592     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE, 
593                                   0, WDML_MakeHszFromAtom(pConv->instance, uiHi));
594     if (pXAct)
595     {
596         pXAct->hMem = (HGLOBAL)uiLo;
597         pXAct->atom = uiHi;
598     }
599     return pXAct;
600 }
601
602 /******************************************************************
603  *              WDML_ServerHandleAdvise
604  *
605  *
606  */
607 static  WDML_QUEUE_STATE WDML_ServerHandleAdvise(WDML_CONV* pConv, WDML_XACT* pXAct)
608 {
609     UINT                uType;
610     WDML_LINK*          pLink;
611     DDEADVISE*          pDdeAdvise;
612     HDDEDATA            hDdeData;
613     BOOL                fAck;
614
615     pDdeAdvise = (DDEADVISE*)GlobalLock(pXAct->hMem);
616     uType = XTYP_ADVSTART | 
617             (pDdeAdvise->fDeferUpd ? XTYPF_NODATA : 0) |
618             (pDdeAdvise->fAckReq ? XTYPF_ACKREQ : 0);
619         
620     if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES))
621     {
622         hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_ADVSTART, pDdeAdvise->cfFormat, 
623                                        (HCONV)pConv, pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
624     }
625     else
626     {
627         hDdeData = 0;
628     }
629
630     if ((UINT)hDdeData)
631     {
632         fAck           = TRUE;
633         
634         /* billx: first to see if the link is already created. */
635         pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, 
636                               pXAct->hszItem, pDdeAdvise->cfFormat);
637
638         if (pLink != NULL)
639         {
640             /* we found a link, and only need to modify it in case it changes */
641             pLink->transactionType = uType;
642         }
643         else
644         {
645             TRACE("Adding Link with hConv=0x%lx\n", (DWORD)pConv);
646             
647             WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, 
648                          uType, pXAct->hszItem, pDdeAdvise->cfFormat);
649         }
650     }
651     else
652     {
653         TRACE("No data returned from the Callback\n");
654         fAck = FALSE;
655     }
656         
657     GlobalUnlock(pXAct->hMem);
658     if (fAck)
659         GlobalFree(pXAct->hMem);
660
661     WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, fAck, pXAct->atom, pXAct->lParam, WM_DDE_ADVISE);
662
663     WDML_DecHSZ(pConv->instance, pXAct->hszItem);
664
665     return WDML_QS_HANDLED;
666 }
667
668 /******************************************************************
669  *              WDML_ServerQueueUnadvise
670  *
671  *
672  */
673 static  WDML_XACT* WDML_ServerQueueUnadvise(WDML_CONV* pConv, LPARAM lParam)
674 {
675     UINT                uiLo, uiHi;
676     WDML_XACT*          pXAct;
677
678     UnpackDDElParam(WM_DDE_UNADVISE, lParam, &uiLo, &uiHi);
679         
680     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE, 
681                                   uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi));
682     if (pXAct) pXAct->atom = uiHi;
683     return pXAct;
684 }
685
686 /******************************************************************
687  *              WDML_ServerHandleUnadvise
688  *
689  *
690  */
691 static  WDML_QUEUE_STATE WDML_ServerHandleUnadvise(WDML_CONV* pConv, WDML_XACT* pXAct)
692 {
693     WDML_LINK*  pLink;
694
695     if (pXAct->hszItem == (HSZ)0 || pXAct->wFmt == 0)
696     {
697         ERR("Unsupported yet options (null item or clipboard format)\n");
698         return WDML_QS_ERROR;
699     }
700
701     pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, 
702                           pXAct->hszItem, pXAct->wFmt);
703     if (pLink == NULL)
704     {
705         ERR("Couln'd find link for %08lx, dropping request\n", (DWORD)pXAct->hszItem);
706         FreeDDElParam(WM_DDE_UNADVISE, pXAct->lParam);
707         return WDML_QS_ERROR;
708     }
709
710     if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES))
711     {
712         WDML_InvokeCallback(pConv->instance, XTYP_ADVSTOP, pXAct->wFmt, (HCONV)pConv, 
713                             pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
714     }
715         
716     WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE, 
717                     pXAct->hszItem, pXAct->wFmt);
718         
719     /* send back ack */
720     WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, TRUE, pXAct->atom, 
721                  pXAct->lParam, WM_DDE_UNADVISE);
722         
723     WDML_DecHSZ(pConv->instance, pXAct->hszItem);
724
725     return WDML_QS_HANDLED;
726 }
727
728 /******************************************************************
729  *              WDML_QueueExecute
730  *
731  *
732  */
733 static  WDML_XACT* WDML_ServerQueueExecute(WDML_CONV* pConv, LPARAM lParam)
734 {
735     WDML_XACT*  pXAct;
736
737     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0);
738     if (pXAct)
739     {
740         pXAct->hMem    = (HGLOBAL)lParam;
741     }
742     return pXAct;    
743 }
744
745  /******************************************************************
746  *              WDML_ServerHandleExecute
747  *
748  *
749  */
750 static  WDML_QUEUE_STATE WDML_ServerHandleExecute(WDML_CONV* pConv, WDML_XACT* pXAct)
751 {
752     HDDEDATA    hDdeData = DDE_FNOTPROCESSED;
753     BOOL        fAck = FALSE, fBusy = FALSE;
754
755     if (!(pConv->instance->CBFflags & CBF_FAIL_EXECUTES))
756     {
757         LPVOID  ptr = GlobalLock(pXAct->hMem);
758         
759         if (ptr)
760         {
761             hDdeData = DdeCreateDataHandle(0, ptr, GlobalSize(pXAct->hMem),
762                                            0, 0, CF_TEXT, 0);
763             GlobalUnlock(pXAct->hMem);
764         }  
765         hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_EXECUTE, 0, (HCONV)pConv, 
766                                        pConv->hszTopic, 0, hDdeData, 0L, 0L);
767     }
768         
769     switch ((UINT)hDdeData)
770     {
771     case DDE_FACK:      
772         fAck = TRUE;    
773         break;
774     case DDE_FBUSY:     
775         fBusy = TRUE;   
776         break;
777     default:    
778         WARN("Bad result code\n");
779         /* fall through */
780     case DDE_FNOTPROCESSED:                             
781         break;
782     }   
783     WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, fBusy, fAck, pXAct->hMem, 0, 0);
784         
785     return WDML_QS_HANDLED;
786 }
787
788 /******************************************************************
789  *              WDML_ServerQueuePoke
790  *
791  *
792  */
793 static  WDML_XACT* WDML_ServerQueuePoke(WDML_CONV* pConv, LPARAM lParam)
794 {
795     UINT                uiLo, uiHi;
796     WDML_XACT*          pXAct;
797
798     UnpackDDElParam(WM_DDE_POKE, lParam, &uiLo, &uiHi);
799
800     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE, 
801                                   0, WDML_MakeHszFromAtom(pConv->instance, uiHi));
802     if (pXAct)
803     {
804         pXAct->atom = uiHi;
805         pXAct->hMem = (HGLOBAL)uiLo;
806     }
807     return pXAct;
808 }
809
810 /******************************************************************
811  *              WDML_ServerHandlePoke
812  *
813  *
814  */
815 static  WDML_QUEUE_STATE WDML_ServerHandlePoke(WDML_CONV* pConv, WDML_XACT* pXAct)
816 {
817     DDEPOKE*            pDdePoke;
818     HDDEDATA            hDdeData;
819     BOOL                fBusy = FALSE, fAck = FALSE;
820
821     pDdePoke = (DDEPOKE*)GlobalLock(pXAct->hMem);
822     if (!pDdePoke)
823     {
824         return WDML_QS_ERROR;
825     }
826
827     if (!(pConv->instance->CBFflags & CBF_FAIL_POKES))
828     {
829         hDdeData = DdeCreateDataHandle(pConv->instance->instanceID, pDdePoke->Value, 
830                                        GlobalSize(pXAct->hMem) - sizeof(DDEPOKE) + 1, 
831                                        0, 0, pDdePoke->cfFormat, 0);
832         if (hDdeData) 
833         {
834             HDDEDATA    hDdeDataOut;
835             
836             hDdeDataOut = WDML_InvokeCallback(pConv->instance, XTYP_POKE, pDdePoke->cfFormat, 
837                                               (HCONV)pConv, pConv->hszTopic, pXAct->hszItem, 
838                                               hDdeData, 0, 0);
839             switch ((UINT)hDdeDataOut) 
840             {
841             case DDE_FACK:
842                 fAck = TRUE;    
843                 break;
844             case DDE_FBUSY:
845                 fBusy = TRUE;
846                 break;
847             default:
848                 FIXME("Unsupported returned value %08lx\n", (DWORD)hDdeDataOut);
849                 /* fal through */
850             case DDE_FNOTPROCESSED:                             
851                 break;
852             }
853             DdeFreeDataHandle(hDdeData);
854         }
855     }
856     GlobalUnlock(pXAct->hMem);
857     
858     if (!fAck)
859         GlobalFree(pXAct->hMem);
860    
861     WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, fBusy, fAck, pXAct->atom, pXAct->lParam, WM_DDE_POKE);
862
863     WDML_DecHSZ(pConv->instance, pXAct->hszItem);
864
865     return WDML_QS_HANDLED;
866 }
867
868 /******************************************************************
869  *              WDML_ServerQueueTerminate
870  *
871  *
872  */
873 static  WDML_XACT*      WDML_ServerQueueTerminate(WDML_CONV* pConv, LPARAM lParam)
874 {
875     WDML_XACT*  pXAct;
876
877     pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_TERMINATE, 0, 0);
878     return pXAct;
879 }
880
881 /******************************************************************
882  *              WDML_ServerHandleTerminate
883  *
884  *
885  */
886 static  WDML_QUEUE_STATE WDML_ServerHandleTerminate(WDML_CONV* pConv, WDML_XACT* pXAct)
887 {
888     /* billx: two things to remove: the conv, and associated links.
889      * Respond with another WM_DDE_TERMINATE iMsg.
890      */
891     if (!(pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS))
892     {
893         WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv, 0, 0, 
894                             0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
895     }
896
897     PostMessageA(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0);
898     WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
899         
900     return WDML_QS_HANDLED;
901 }
902
903 /******************************************************************
904  *              WDML_ServerHandle
905  *
906  *
907  */
908 static WDML_QUEUE_STATE WDML_ServerHandle(WDML_CONV* pConv, WDML_XACT* pXAct)
909 {
910     WDML_QUEUE_STATE    qs = WDML_QS_ERROR;
911
912     switch (pXAct->ddeMsg)
913     {
914     case WM_DDE_INITIATE:
915         FIXME("WM_DDE_INITIATE shouldn't be there!\n");
916         break;
917     case WM_DDE_REQUEST:
918         qs = WDML_ServerHandleRequest(pConv, pXAct);
919         break;
920                 
921     case WM_DDE_ADVISE:
922         qs = WDML_ServerHandleAdvise(pConv, pXAct);
923         break;
924         
925     case WM_DDE_UNADVISE:
926         qs = WDML_ServerHandleUnadvise(pConv, pXAct);
927         break;
928         
929     case WM_DDE_EXECUTE:
930         qs = WDML_ServerHandleExecute(pConv, pXAct);
931         break;
932         
933     case WM_DDE_POKE:
934         qs = WDML_ServerHandlePoke(pConv, pXAct);
935         break;
936         
937     case WM_DDE_TERMINATE:
938         qs = WDML_ServerHandleTerminate(pConv, pXAct);
939         break;
940
941     case WM_DDE_ACK:
942         WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n");
943         break;
944
945     default:
946         FIXME("Unsupported message %d\n", pXAct->ddeMsg);
947     }
948     return qs;
949 }
950
951 /******************************************************************
952  *              WDML_ServerConvProc
953  *
954  *
955  */
956 static LRESULT CALLBACK WDML_ServerConvProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam)
957 {
958     WDML_INSTANCE*      pInstance;
959     WDML_CONV*          pConv;
960     WDML_XACT*          pXAct = NULL;
961
962     if (iMsg == WM_DESTROY)
963     {
964         EnterCriticalSection(&WDML_CritSect);
965         pConv = WDML_GetConvFromWnd(hwndServer);
966         if (pConv && !(pConv->wStatus & ST_TERMINATED))
967         {
968             WDML_ServerHandleTerminate(pConv, NULL);
969         }
970         LeaveCriticalSection(&WDML_CritSect);
971     }
972     if (iMsg < WM_DDE_FIRST || iMsg > WM_DDE_LAST)
973     {
974         return DefWindowProcA(hwndServer, iMsg, wParam, lParam);
975     }
976
977     EnterCriticalSection(&WDML_CritSect);
978
979     pInstance = WDML_GetInstanceFromWnd(hwndServer);
980     pConv = WDML_GetConvFromWnd(hwndServer);
981
982     if (!pConv) 
983     {
984         ERR("Got a message (%u) on a not known conversation, dropping request\n", iMsg);
985         goto theError;
986     }
987     if (pConv->hwndClient != WIN_GetFullHandle( (HWND)wParam ) || pConv->hwndServer != hwndServer)
988     {
989         ERR("mismatch between C/S windows and converstation\n");
990         goto theError;
991     }
992     if (pConv->instance != pInstance || pConv->instance == NULL)
993     {
994         ERR("mismatch in instances\n");
995         goto theError;
996     }
997
998     switch (iMsg)
999     {
1000     case WM_DDE_INITIATE:
1001         FIXME("WM_DDE_INITIATE message received!\n");
1002         break;
1003         
1004     case WM_DDE_REQUEST:
1005         pXAct = WDML_ServerQueueRequest(pConv, lParam);
1006         break;
1007                 
1008     case WM_DDE_ADVISE:
1009         pXAct = WDML_ServerQueueAdvise(pConv, lParam);
1010         break;
1011         
1012     case WM_DDE_UNADVISE:
1013         pXAct = WDML_ServerQueueUnadvise(pConv, lParam);
1014         break;
1015         
1016     case WM_DDE_EXECUTE:
1017         pXAct = WDML_ServerQueueExecute(pConv, lParam);
1018         break;
1019         
1020     case WM_DDE_POKE:
1021         pXAct = WDML_ServerQueuePoke(pConv, lParam);
1022         break;
1023         
1024     case WM_DDE_TERMINATE:
1025         pXAct = WDML_ServerQueueTerminate(pConv, lParam);
1026         break;
1027
1028     case WM_DDE_ACK:
1029         WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n");
1030         break;
1031
1032     default:
1033         FIXME("Unsupported message %d\n", iMsg);
1034     }
1035     
1036     if (pXAct) 
1037     {
1038         pXAct->lParam = lParam;
1039         if (WDML_ServerHandle(pConv, pXAct) == WDML_QS_BLOCK)
1040         {
1041             WDML_QueueTransaction(pConv, pXAct);
1042         }
1043         else
1044         {
1045             WDML_FreeTransaction(pInstance, pXAct, TRUE);
1046         }
1047     }
1048  theError:
1049     LeaveCriticalSection(&WDML_CritSect);
1050     return 0;
1051 }