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