dde_server.c 30.5 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * DDEML library
 *
 * Copyright 1997 Alexandre Julliard
 * Copyright 1997 Len White
 * Copyright 1999 Keith Matthews
 * Copyright 2000 Corel
 * Copyright 2001 Eric Pouech
9
 * Copyright 2003, 2004, 2005 Dmitry Timoshkov
10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 28
#include <string.h>
#include "windef.h"
29
#include "winbase.h"
30 31
#include "wingdi.h"
#include "winuser.h"
32
#include "winnls.h"
33 34
#include "dde.h"
#include "ddeml.h"
35
#include "win.h"
36
#include "wine/unicode.h"
37
#include "wine/debug.h"
38
#include "dde_private.h"
39

40
WINE_DEFAULT_DEBUG_CHANNEL(ddeml);
41

42
static const WCHAR szServerNameClass[] = {'W','i','n','e','D','d','e','S','e','r','v','e','r','N','a','m','e',0};
43 44
const char WDML_szServerConvClassA[] = "WineDdeServerConvA";
const WCHAR WDML_szServerConvClassW[] = {'W','i','n','e','D','d','e','S','e','r','v','e','r','C','o','n','v','W',0};
45 46 47 48 49 50 51

static LRESULT CALLBACK WDML_ServerNameProc(HWND, UINT, WPARAM, LPARAM);
static LRESULT CALLBACK WDML_ServerConvProc(HWND, UINT, WPARAM, LPARAM);

/******************************************************************************
 * DdePostAdvise [USER32.@]  Send transaction to DDE callback function.
 *
52 53 54 55 56
 * PARAMS
 *	idInst	  [I] Instance identifier
 *	hszTopic  [I] Handle to topic name string
 *	hszItem	  [I] Handle to item name string
 *
57 58 59 60
 * RETURNS
 *    Success: TRUE
 *    Failure: FALSE
 */
61
BOOL WINAPI DdePostAdvise(DWORD idInst, HSZ hszTopic, HSZ hszItem)
62
{
63
    WDML_INSTANCE*	pInstance = NULL;
64
    WDML_LINK*		pLink = NULL;
65 66
    HDDEDATA		hDdeData = 0;
    HGLOBAL             hItemData = 0;
67
    WDML_CONV*		pConv = NULL;
68 69 70
    ATOM		atom = 0;
    UINT		count;

71
    TRACE("(%d,%p,%p)\n", idInst, hszTopic, hszItem);
72

73
    pInstance = WDML_GetInstance(idInst);
74

75
    if (pInstance == NULL)
76
        return FALSE;
77

78
    atom = WDML_MakeAtomFromHsz(hszItem);
79
    if (!atom) return FALSE;
80 81 82 83 84 85 86 87 88 89 90

    /* first compute the number of links which will trigger a message */
    count = 0;
    for (pLink = pInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next)
    {
	if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0)
	{
	    count++;
	}
    }
    if (count >= CADV_LATEACK)
91
    {
92 93
	FIXME("too high value for count\n");
	count &= 0xFFFF;
94
    }
95 96

    for (pLink = pInstance->links[WDML_SERVER_SIDE]; pLink != NULL; pLink = pLink->next)
97 98 99
    {
	if (DdeCmpStringHandles(hszItem, pLink->hszItem) == 0)
	{
100
	    hDdeData = WDML_InvokeCallback(pInstance, XTYP_ADVREQ, pLink->uFmt, pLink->hConv,
101
					   hszTopic, hszItem, 0, --count, 0);
102

103
	    if (hDdeData == CBR_BLOCK)
104 105 106 107
	    {
		/* MS doc is not consistent here */
		FIXME("CBR_BLOCK returned for ADVREQ\n");
		continue;
108 109 110 111 112 113 114 115 116 117 118
	    }
	    if (hDdeData)
	    {
		if (pLink->transactionType & XTYPF_NODATA)
		{
		    TRACE("no data\n");
		    hItemData = 0;
		}
		else
		{
		    TRACE("with data\n");
119

120 121
		    hItemData = WDML_DataHandle2Global(hDdeData, FALSE, FALSE, FALSE, FALSE);
		}
122

123
		pConv = WDML_GetConv(pLink->hConv, TRUE);
124

125 126
		if (pConv == NULL)
		{
127
		    if (!WDML_IsAppOwned(hDdeData))  DdeFreeDataHandle(hDdeData);
128 129
		    goto theError;
		}
130

131
		if (!PostMessageW(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
132
				  PackDDElParam(WM_DDE_DATA, (UINT_PTR)hItemData, atom)))
133 134
		{
		    ERR("post message failed\n");
135
                    pConv->wStatus &= ~ST_CONNECTED;
136
                    pConv->instance->lastError = DMLERR_POSTMSG_FAILED;
137
		    if (!WDML_IsAppOwned(hDdeData))  DdeFreeDataHandle(hDdeData);
138 139
		    GlobalFree(hItemData);
		    goto theError;
140
		}
141
                if (!WDML_IsAppOwned(hDdeData))  DdeFreeDataHandle(hDdeData);
142 143 144 145
	    }
	}
    }
    return TRUE;
146

147
 theError:
148
    GlobalDeleteAtom(atom);
149
    return FALSE;
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
}


/******************************************************************************
 * DdeNameService [USER32.@]  {Un}registers service name of DDE server
 *
 * PARAMS
 *    idInst [I] Instance identifier
 *    hsz1   [I] Handle to service name string
 *    hsz2   [I] Reserved
 *    afCmd  [I] Service name flags
 *
 * RETURNS
 *    Success: Non-zero
 *    Failure: 0
 */
HDDEDATA WINAPI DdeNameService(DWORD idInst, HSZ hsz1, HSZ hsz2, UINT afCmd)
{
    WDML_SERVER*	pServer;
169
    WDML_INSTANCE*	pInstance;
170
    HWND 		hwndServer;
171
    WNDCLASSEXW  	wndclass;
172

173
    TRACE("(%d,%p,%p,%x)\n", idInst, hsz1, hsz2, afCmd);
174

175 176
    /*  First check instance
     */
177 178
    pInstance = WDML_GetInstance(idInst);
    if  (pInstance == NULL)
179 180 181
    {
	TRACE("Instance not found as initialised\n");
	/*  Nothing has been initialised - exit now ! can return TRUE since effect is the same */
182
        return NULL;
183
    }
184

185 186 187 188
    if (hsz2 != 0L)
    {
	/*	Illegal, reserved parameter
	 */
189 190
	pInstance->lastError = DMLERR_INVALIDPARAMETER;
	WARN("Reserved parameter no-zero !!\n");
191
        return NULL;
192
    }
193
    if (hsz1 == 0 && !(afCmd & DNS_UNREGISTER))
194
    {
195 196
	/*	don't know if we should check this but it makes sense
	 *	why supply REGISTER or filter flags if de-registering all
197
	 */
198 199
	TRACE("General unregister unexpected flags\n");
	pInstance->lastError = DMLERR_INVALIDPARAMETER;
200
        return NULL;
201
    }
202

203
    switch (afCmd & (DNS_REGISTER | DNS_UNREGISTER))
204
    {
205 206
    case DNS_REGISTER:
	pServer = WDML_FindServer(pInstance, hsz1, 0);
207 208
	if (pServer)
	{
209 210
	    ERR("Trying to register already registered service!\n");
	    pInstance->lastError = DMLERR_DLL_USAGE;
211
            return NULL;
212
	}
213 214

	TRACE("Adding service name\n");
215

216
	WDML_IncHSZ(pInstance, hsz1);
217

218
	pServer = WDML_AddServer(pInstance, hsz1, 0);
219 220

	WDML_BroadcastDDEWindows(WDML_szEventClass, WM_WDML_REGISTER,
221
				 pServer->atomService, pServer->atomServiceSpec);
222

223 224 225 226
	wndclass.cbSize        = sizeof(wndclass);
	wndclass.style         = 0;
	wndclass.lpfnWndProc   = WDML_ServerNameProc;
	wndclass.cbClsExtra    = 0;
227
	wndclass.cbWndExtra    = 2 * sizeof(ULONG_PTR);
228 229 230 231 232
	wndclass.hInstance     = 0;
	wndclass.hIcon         = 0;
	wndclass.hCursor       = 0;
	wndclass.hbrBackground = 0;
	wndclass.lpszMenuName  = NULL;
233
	wndclass.lpszClassName = szServerNameClass;
234
	wndclass.hIconSm       = 0;
235

236
	RegisterClassExW(&wndclass);
237

238
	hwndServer = CreateWindowW(szServerNameClass, NULL,
239 240
				   WS_POPUP, 0, 0, 0, 0,
				   0, 0, 0, 0);
241

242 243
	SetWindowLongPtrW(hwndServer, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
	SetWindowLongPtrW(hwndServer, GWL_WDML_SERVER, (ULONG_PTR)pServer);
244
	TRACE("Created nameServer=%p for instance=%08x\n", hwndServer, idInst);
245

246
	pServer->hwndServer = hwndServer;
247 248 249 250
	break;

    case DNS_UNREGISTER:
	if (hsz1 == 0L)
251
	{
252
	    /* General unregister situation
253
	     * terminate all server side pending conversations
254
	     */
255 256 257 258 259 260
	    while (pInstance->servers)
		WDML_RemoveServer(pInstance, pInstance->servers->hszService, 0);
	    pInstance->servers = NULL;
	    TRACE("General de-register - finished\n");
	}
	else
261
	{
262
	    WDML_RemoveServer(pInstance, hsz1, 0L);
263
	}
264
	break;
265 266 267 268
    }

    if (afCmd & (DNS_FILTERON | DNS_FILTEROFF))
    {
269 270
	/*	Set filter flags on to hold notifications of connection
	 */
271
	pServer = WDML_FindServer(pInstance, hsz1, 0);
272 273 274 275
	if (!pServer)
	{
	    /*  trying to filter where no service names !!
	     */
276
	    pInstance->lastError = DMLERR_DLL_USAGE;
277
            return NULL;
278 279
	}
	else
280
	{
281
	    pServer->filterOn = (afCmd & DNS_FILTERON) != 0;
282 283 284 285 286 287 288 289 290 291
	}
    }
    return (HDDEDATA)TRUE;
}

/******************************************************************
 *		WDML_CreateServerConv
 *
 *
 */
292
static WDML_CONV* WDML_CreateServerConv(WDML_INSTANCE* pInstance, HWND hwndClient,
293
					HWND hwndServerName, HSZ hszApp, HSZ hszTopic)
294 295 296
{
    HWND	hwndServerConv;
    WDML_CONV*	pConv;
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

    if (pInstance->unicode)
    {
        WNDCLASSEXW wndclass;

        wndclass.cbSize        = sizeof(wndclass);
        wndclass.style         = 0;
        wndclass.lpfnWndProc   = WDML_ServerConvProc;
        wndclass.cbClsExtra    = 0;
        wndclass.cbWndExtra    = 2 * sizeof(ULONG_PTR);
        wndclass.hInstance     = 0;
        wndclass.hIcon         = 0;
        wndclass.hCursor       = 0;
        wndclass.hbrBackground = 0;
        wndclass.lpszMenuName  = NULL;
        wndclass.lpszClassName = WDML_szServerConvClassW;
        wndclass.hIconSm       = 0;

        RegisterClassExW(&wndclass);

        hwndServerConv = CreateWindowW(WDML_szServerConvClassW, 0,
318 319
				       WS_CHILD, 0, 0, 0, 0,
				       hwndServerName, 0, 0, 0);
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
    }
    else
    {
        WNDCLASSEXA wndclass;

        wndclass.cbSize        = sizeof(wndclass);
        wndclass.style         = 0;
        wndclass.lpfnWndProc   = WDML_ServerConvProc;
        wndclass.cbClsExtra    = 0;
        wndclass.cbWndExtra    = 2 * sizeof(ULONG_PTR);
        wndclass.hInstance     = 0;
        wndclass.hIcon         = 0;
        wndclass.hCursor       = 0;
        wndclass.hbrBackground = 0;
        wndclass.lpszMenuName  = NULL;
        wndclass.lpszClassName = WDML_szServerConvClassA;
        wndclass.hIconSm       = 0;

        RegisterClassExA(&wndclass);

        hwndServerConv = CreateWindowA(WDML_szServerConvClassA, 0,
                                      WS_CHILD, 0, 0, 0, 0,
                                      hwndServerName, 0, 0, 0);
    }
344

345 346
    TRACE("Created convServer=%p (nameServer=%p) for instance=%08x unicode=%d\n",
	  hwndServerConv, hwndServerName, pInstance->instanceID, pInstance->unicode);
347 348

    pConv = WDML_AddConv(pInstance, WDML_SERVER_SIDE, hszApp, hszTopic,
349 350
			 hwndClient, hwndServerConv);
    if (pConv)
351
    {
352 353
	SetWindowLongPtrW(hwndServerConv, GWL_WDML_INSTANCE, (ULONG_PTR)pInstance);
	SetWindowLongPtrW(hwndServerConv, GWL_WDML_CONVERSATION, (ULONG_PTR)pConv);
354 355

	/* this should be the only place using SendMessage for WM_DDE_ACK */
356
        /* note: sent messages shall not use packing */
357
	SendMessageW(hwndClient, WM_DDE_ACK, (WPARAM)hwndServerConv,
358
		     MAKELPARAM(WDML_MakeAtomFromHsz(hszApp), WDML_MakeAtomFromHsz(hszTopic)));
359
	/* we assume we're connected since we've sent an answer...
360 361
	 * I'm not sure what we can do... it doesn't look like the return value
	 * of SendMessage is used... sigh...
362
	 */
363
	pConv->wStatus |= ST_CONNECTED;
364
    }
365 366 367 368 369
    else
    {
	DestroyWindow(hwndServerConv);
    }
    return pConv;
370 371 372 373 374 375 376 377 378 379 380 381
}

/******************************************************************
 *		WDML_ServerNameProc
 *
 *
 */
static LRESULT CALLBACK WDML_ServerNameProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
    HWND		hwndClient;
    HSZ			hszApp, hszTop;
    HDDEDATA		hDdeData = 0;
382
    WDML_INSTANCE*	pInstance;
383
    UINT_PTR		uiLo, uiHi;
384

385 386 387
    switch (iMsg)
    {
    case WM_DDE_INITIATE:
388

389 390 391
	/* wParam         -- sending window handle
	   LOWORD(lParam) -- application atom
	   HIWORD(lParam) -- topic atom */
392

393
	TRACE("WM_DDE_INITIATE message received!\n");
394
	hwndClient = (HWND)wParam;
395

396 397
	pInstance = WDML_GetInstanceFromWnd(hwndServer);
	if (!pInstance) return 0;
398
	TRACE("idInst=%d, threadID=0x%x\n", pInstance->instanceID, GetCurrentThreadId());
399

400
	/* don't free DDEParams, since this is a broadcast */
401 402
	UnpackDDElParam(WM_DDE_INITIATE, lParam, &uiLo, &uiHi);

403 404
	hszApp = WDML_MakeHszFromAtom(pInstance, uiLo);
	hszTop = WDML_MakeHszFromAtom(pInstance, uiHi);
405

406
	if (!(pInstance->CBFflags & CBF_FAIL_CONNECTIONS))
407
	{
408 409 410 411
	    BOOL 		self = FALSE;
	    CONVCONTEXT		cc;
	    CONVCONTEXT*	pcc = NULL;
	    WDML_CONV*		pConv;
412
	    char		buf[256];
413 414 415 416 417 418 419 420 421

	    if (GetWindowThreadProcessId(hwndClient, NULL) == GetWindowThreadProcessId(hwndServer, NULL) &&
		WDML_GetInstanceFromWnd(hwndClient) == WDML_GetInstanceFromWnd(hwndServer))
	    {
		self = TRUE;
	    }
	    /* FIXME: so far, we don't grab distant convcontext, so only check if remote is
	     * handled under DDEML, and if so build a default context
	     */
422 423 424 425
           if ((GetClassNameA(hwndClient, buf, sizeof(buf)) &&
                lstrcmpiA(buf, WDML_szClientConvClassA) == 0) ||
               (GetClassNameW(hwndClient, (LPWSTR)buf, sizeof(buf)/sizeof(WCHAR)) &&
                lstrcmpiW((LPWSTR)buf, WDML_szClientConvClassW) == 0))
426 427 428 429
	    {
		pcc = &cc;
		memset(pcc, 0, sizeof(*pcc));
		pcc->cb = sizeof(*pcc);
430
		pcc->iCodePage = IsWindowUnicode(hwndClient) ? CP_WINUNICODE : CP_WINANSI;
431 432 433 434 435
	    }
	    if ((pInstance->CBFflags & CBF_FAIL_SELFCONNECTIONS) && self)
	    {
		TRACE("Don't do self connection as requested\n");
	    }
436
	    else if (hszApp && hszTop)
437
	    {
438
		WDML_SERVER*	pServer = (WDML_SERVER*)GetWindowLongPtrW(hwndServer, GWL_WDML_SERVER);
439

440 441
		/* check filters for name service */
		if (!pServer->filterOn || DdeCmpStringHandles(pServer->hszService, hszApp) == 0)
442
		{
443 444
		    /* pass on to the callback  */
		    hDdeData = WDML_InvokeCallback(pInstance, XTYP_CONNECT,
445
						   0, 0, hszTop, hszApp, 0, (ULONG_PTR)pcc, self);
446
		    if ((ULONG_PTR)hDdeData)
447
		    {
448
			pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer,
449
						      hszApp, hszTop);
450 451 452 453
                        if (pConv)
                        {
                            if (pcc) pConv->wStatus |= ST_ISLOCAL;
                            WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
454
                                                hszTop, hszApp, 0, (ULONG_PTR)pcc, self);
455
                        }
456
		    }
457 458
		}
	    }
459
	    else if (pInstance->servers)
460
	    {
461 462
		/* pass on to the callback  */
		hDdeData = WDML_InvokeCallback(pInstance, XTYP_WILDCONNECT,
463
					       0, 0, hszTop, hszApp, 0, (ULONG_PTR)pcc, self);
464

465
		if (hDdeData == CBR_BLOCK)
466 467 468 469
		{
		    /* MS doc is not consistent here */
		    FIXME("CBR_BLOCK returned for WILDCONNECT\n");
		}
470
		else if ((ULONG_PTR)hDdeData != 0)
471 472
		{
		    HSZPAIR*	hszp;
473

474 475 476 477 478 479
		    hszp = (HSZPAIR*)DdeAccessData(hDdeData, NULL);
		    if (hszp)
		    {
			int	i;
			for (i = 0; hszp[i].hszSvc && hszp[i].hszTopic; i++)
			{
480
			    pConv = WDML_CreateServerConv(pInstance, hwndClient, hwndServer,
481
							  hszp[i].hszSvc, hszp[i].hszTopic);
482 483 484 485
                            if (pConv)
                            {
                                if (pcc) pConv->wStatus |= ST_ISLOCAL;
                                WDML_InvokeCallback(pInstance, XTYP_CONNECT_CONFIRM, 0, (HCONV)pConv,
486
                                                    hszp[i].hszTopic, hszp[i].hszSvc, 0, (ULONG_PTR)pcc, self);
487
                            }
488
			}
489 490
			DdeUnaccessData(hDdeData);
		    }
491
                    if (!WDML_IsAppOwned(hDdeData)) DdeFreeDataHandle(hDdeData);
492 493 494
		}
	    }
	}
495

496
	return 0;
497

498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
    case WM_DDE_REQUEST:
	FIXME("WM_DDE_REQUEST message received!\n");
	return 0;
    case WM_DDE_ADVISE:
	FIXME("WM_DDE_ADVISE message received!\n");
	return 0;
    case WM_DDE_UNADVISE:
	FIXME("WM_DDE_UNADVISE message received!\n");
	return 0;
    case WM_DDE_EXECUTE:
	FIXME("WM_DDE_EXECUTE message received!\n");
	return 0;
    case WM_DDE_POKE:
	FIXME("WM_DDE_POKE message received!\n");
	return 0;
    case WM_DDE_TERMINATE:
	FIXME("WM_DDE_TERMINATE message received!\n");
	return 0;
516 517
    default:
	break;
518
    }
519

520
    return DefWindowProcW(hwndServer, iMsg, wParam, lParam);
521 522 523
}

/******************************************************************
524
 *		WDML_ServerQueueRequest
525 526 527
 *
 *
 */
528
static	WDML_XACT*	WDML_ServerQueueRequest(WDML_CONV* pConv, LPARAM lParam)
529
{
530
    UINT_PTR		uiLo, uiHi;
531
    WDML_XACT*		pXAct;
532

533
    UnpackDDElParam(WM_DDE_REQUEST, lParam, &uiLo, &uiHi);
534

535
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_REQUEST,
536 537 538 539
				  uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi));
    if (pXAct) pXAct->atom = uiHi;
    return pXAct;
}
540

541 542 543 544 545 546 547 548
/******************************************************************
 *		WDML_ServerHandleRequest
 *
 *
 */
static	WDML_QUEUE_STATE WDML_ServerHandleRequest(WDML_CONV* pConv, WDML_XACT* pXAct)
{
    HDDEDATA		hDdeData = 0;
549
    BOOL		fAck = TRUE;
550 551

    if (!(pConv->instance->CBFflags & CBF_FAIL_REQUESTS))
552
    {
553 554

	hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_REQUEST, pXAct->wFmt, (HCONV)pConv,
555
				       pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
556
    }
557

558
    switch ((ULONG_PTR)hDdeData)
559
    {
560
    case 0:
561 562
	TRACE("No data returned from the Callback\n");
	fAck = FALSE;
563
	break;
564

565
    case (ULONG_PTR)CBR_BLOCK:
566 567
	return WDML_QS_BLOCK;

568 569
    default:
        {
570
	    HGLOBAL	hMem = WDML_DataHandle2Global(hDdeData, TRUE, FALSE, FALSE, FALSE);
571
	    if (!PostMessageW(pConv->hwndClient, WM_DDE_DATA, (WPARAM)pConv->hwndServer,
572
			      ReuseDDElParam(pXAct->lParam, WM_DDE_REQUEST, WM_DDE_DATA,
573
					     (UINT_PTR)hMem, (UINT_PTR)pXAct->atom)))
574
	    {
575
                pConv->instance->lastError = DMLERR_POSTMSG_FAILED;
576 577
		DdeFreeDataHandle(hDdeData);
		GlobalFree(hMem);
578
		fAck = FALSE;
579
	    }
580
	}
581
	break;
582
    }
583 584 585

    WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, fAck, pXAct->atom, pXAct->lParam, WM_DDE_REQUEST);

586
    WDML_DecHSZ(pConv->instance, pXAct->hszItem);
587 588

    return WDML_QS_HANDLED;
589 590 591
}

/******************************************************************
592
 *		WDML_ServerQueueAdvise
593 594 595
 *
 *
 */
596
static	WDML_XACT*	WDML_ServerQueueAdvise(WDML_CONV* pConv, LPARAM lParam)
597
{
598
    UINT_PTR		uiLo, uiHi;
599
    WDML_XACT*		pXAct;
600

601
    /* XTYP_ADVSTART transaction:
602
       establish link and save link info to InstanceInfoTable */
603

604 605
    if (!UnpackDDElParam(WM_DDE_ADVISE, lParam, &uiLo, &uiHi))
	return NULL;
606 607

    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_ADVISE,
608 609
				  0, WDML_MakeHszFromAtom(pConv->instance, uiHi));
    if (pXAct)
610
    {
611 612
	pXAct->hMem = (HGLOBAL)uiLo;
	pXAct->atom = uiHi;
613
    }
614 615
    return pXAct;
}
616

617 618 619 620 621 622 623 624 625 626
/******************************************************************
 *		WDML_ServerHandleAdvise
 *
 *
 */
static	WDML_QUEUE_STATE WDML_ServerHandleAdvise(WDML_CONV* pConv, WDML_XACT* pXAct)
{
    UINT		uType;
    WDML_LINK*		pLink;
    DDEADVISE*		pDdeAdvise;
627 628
    HDDEDATA		hDdeData = 0;
    BOOL		fAck = TRUE;
629

630
    pDdeAdvise = GlobalLock(pXAct->hMem);
631
    uType = XTYP_ADVSTART |
632 633
	    (pDdeAdvise->fDeferUpd ? XTYPF_NODATA : 0) |
	    (pDdeAdvise->fAckReq ? XTYPF_ACKREQ : 0);
634

635
    if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES))
636
    {
637
	hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_ADVSTART, pDdeAdvise->cfFormat,
638 639
				       (HCONV)pConv, pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
    }
640

641
    switch ((ULONG_PTR)hDdeData)
642
    {
643 644 645 646 647 648 649
    case 0:
	TRACE("No data returned from the Callback\n");
	fAck = FALSE;
	break;

    case (ULONG_PTR)CBR_BLOCK:
	return WDML_QS_BLOCK;
650

651
    default:
652
	/* billx: first to see if the link is already created. */
653
	pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
654
			      pXAct->hszItem, TRUE, pDdeAdvise->cfFormat);
655 656 657 658 659 660 661 662

	if (pLink != NULL)
	{
	    /* we found a link, and only need to modify it in case it changes */
	    pLink->transactionType = uType;
	}
	else
	{
663
	    TRACE("Adding Link with hConv %p\n", pConv);
664
	    WDML_AddLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
665
			 uType, pXAct->hszItem, pDdeAdvise->cfFormat);
666
	}
667
	break;
668
    }
669

670 671
    GlobalUnlock(pXAct->hMem);
    if (fAck)
672
    {
673
	GlobalFree(pXAct->hMem);
674 675
    }
    pXAct->hMem = 0;
676 677 678 679

    WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, fAck, pXAct->atom, pXAct->lParam, WM_DDE_ADVISE);

    WDML_DecHSZ(pConv->instance, pXAct->hszItem);
680

681
    return WDML_QS_HANDLED;
682 683 684
}

/******************************************************************
685
 *		WDML_ServerQueueUnadvise
686 687 688
 *
 *
 */
689
static	WDML_XACT* WDML_ServerQueueUnadvise(WDML_CONV* pConv, LPARAM lParam)
690
{
691
    UINT_PTR		uiLo, uiHi;
692
    WDML_XACT*		pXAct;
693 694

    UnpackDDElParam(WM_DDE_UNADVISE, lParam, &uiLo, &uiHi);
695 696

    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_UNADVISE,
697 698 699 700 701 702 703 704 705 706 707 708 709
				  uiLo, WDML_MakeHszFromAtom(pConv->instance, uiHi));
    if (pXAct) pXAct->atom = uiHi;
    return pXAct;
}

/******************************************************************
 *		WDML_ServerHandleUnadvise
 *
 *
 */
static	WDML_QUEUE_STATE WDML_ServerHandleUnadvise(WDML_CONV* pConv, WDML_XACT* pXAct)
{
    WDML_LINK*	pLink;
710

711
    if (pXAct->hszItem == NULL || pXAct->wFmt == 0)
712
    {
713 714
	ERR("Unsupported yet options (null item or clipboard format)\n");
	return WDML_QS_ERROR;
715 716
    }

717
    pLink = WDML_FindLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
718
			  pXAct->hszItem, TRUE, pXAct->wFmt);
719 720
    if (pLink == NULL)
    {
721
	ERR("Couldn't find link for %p, dropping request\n", pXAct->hszItem);
722 723
	FreeDDElParam(WM_DDE_UNADVISE, pXAct->lParam);
	return WDML_QS_ERROR;
724 725
    }

726
    if (!(pConv->instance->CBFflags & CBF_FAIL_ADVISES))
727
    {
728
	WDML_InvokeCallback(pConv->instance, XTYP_ADVSTOP, pXAct->wFmt, (HCONV)pConv,
729
			    pConv->hszTopic, pXAct->hszItem, 0, 0, 0);
730
    }
731 732

    WDML_RemoveLink(pConv->instance, (HCONV)pConv, WDML_SERVER_SIDE,
733
		    pXAct->hszItem, pXAct->wFmt);
734

735
    /* send back ack */
736
    WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, FALSE, TRUE, pXAct->atom,
737
                 pXAct->lParam, WM_DDE_UNADVISE);
738

739 740 741
    WDML_DecHSZ(pConv->instance, pXAct->hszItem);

    return WDML_QS_HANDLED;
742 743 744
}

/******************************************************************
745
 *		WDML_QueueExecute
746 747 748
 *
 *
 */
749
static	WDML_XACT* WDML_ServerQueueExecute(WDML_CONV* pConv, LPARAM lParam)
750
{
751
    WDML_XACT*	pXAct;
752

753 754 755 756 757
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_EXECUTE, 0, 0);
    if (pXAct)
    {
	pXAct->hMem    = (HGLOBAL)lParam;
    }
758
    return pXAct;
759 760
}

761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808
static BOOL data_looks_unicode( const WCHAR *data, DWORD size )
{
    DWORD i;

    if (size % sizeof(WCHAR)) return FALSE;
    for (i = 0; i < size / sizeof(WCHAR); i++) if (data[i] > 255) return FALSE;
    return TRUE;
}

/* convert data to Unicode, unless it looks like it's already Unicode */
static HDDEDATA map_A_to_W( DWORD instance, void *ptr, DWORD size )
{
    HDDEDATA ret;
    DWORD len;
    const char *end;

    if (!data_looks_unicode( ptr, size ))
    {
        if ((end = memchr( ptr, 0, size ))) size = end + 1 - (const char *)ptr;
        len = MultiByteToWideChar( CP_ACP, 0, ptr, size, NULL, 0 );
        ret = DdeCreateDataHandle( instance, NULL, len * sizeof(WCHAR), 0, 0, CF_TEXT, 0);
        MultiByteToWideChar( CP_ACP, 0, ptr, size, (WCHAR *)DdeAccessData(ret, NULL), len );
    }
    else ret = DdeCreateDataHandle( instance, ptr, size, 0, 0, CF_TEXT, 0 );

    return ret;
}

/* convert data to ASCII, unless it looks like it's not in Unicode format */
static HDDEDATA map_W_to_A( DWORD instance, void *ptr, DWORD size )
{
    HDDEDATA ret;
    DWORD len;
    const WCHAR *end;

    if (data_looks_unicode( ptr, size ))
    {
        size /= sizeof(WCHAR);
        if ((end = memchrW( ptr, 0, size ))) size = end + 1 - (const WCHAR *)ptr;
        len = WideCharToMultiByte( CP_ACP, 0, ptr, size, NULL, 0, NULL, NULL );
        ret = DdeCreateDataHandle( instance, NULL, len, 0, 0, CF_TEXT, 0);
        WideCharToMultiByte( CP_ACP, 0, ptr, size, (char *)DdeAccessData(ret, NULL), len, NULL, NULL );
    }
    else ret = DdeCreateDataHandle( instance, ptr, size, 0, 0, CF_TEXT, 0 );

    return ret;
}

809 810 811 812 813 814 815 816 817
 /******************************************************************
 *		WDML_ServerHandleExecute
 *
 *
 */
static	WDML_QUEUE_STATE WDML_ServerHandleExecute(WDML_CONV* pConv, WDML_XACT* pXAct)
{
    HDDEDATA	hDdeData = DDE_FNOTPROCESSED;
    BOOL	fAck = FALSE, fBusy = FALSE;
818

819
    if (!(pConv->instance->CBFflags & CBF_FAIL_EXECUTES))
820
    {
821
	LPVOID	ptr = GlobalLock(pXAct->hMem);
822
        DWORD size = GlobalSize(pXAct->hMem);
823

824 825
	if (ptr)
	{
826 827 828 829 830 831
            if (pConv->instance->unicode)  /* Unicode server, try to map A->W */
                hDdeData = map_A_to_W( pConv->instance->instanceID, ptr, size );
            else if (!IsWindowUnicode( pConv->hwndClient )) /* ASCII server and client, try to map W->A */
                hDdeData = map_W_to_A( pConv->instance->instanceID, ptr, size );
            else
                hDdeData = DdeCreateDataHandle(pConv->instance->instanceID, ptr, size, 0, 0, CF_TEXT, 0);
832
	    GlobalUnlock(pXAct->hMem);
833 834
	}
	hDdeData = WDML_InvokeCallback(pConv->instance, XTYP_EXECUTE, 0, (HCONV)pConv,
835
				       pConv->hszTopic, 0, hDdeData, 0L, 0L);
836
    }
837

838
    switch ((ULONG_PTR)hDdeData)
839
    {
840 841 842
    case (ULONG_PTR)CBR_BLOCK:
	return WDML_QS_BLOCK;

843 844
    case DDE_FACK:
	fAck = TRUE;
845
	break;
846 847
    case DDE_FBUSY:
	fBusy = TRUE;
848
	break;
849
    default:
850
	FIXME("Unsupported returned value %p\n", hDdeData);
851
	/* fall through */
852
    case DDE_FNOTPROCESSED:
853
	break;
854
    }
855
    WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, fBusy, fAck, (UINT_PTR)pXAct->hMem, 0, 0);
856

857
    return WDML_QS_HANDLED;
858 859 860
}

/******************************************************************
861
 *		WDML_ServerQueuePoke
862 863 864
 *
 *
 */
865
static	WDML_XACT* WDML_ServerQueuePoke(WDML_CONV* pConv, LPARAM lParam)
866
{
867
    UINT_PTR		uiLo, uiHi;
868 869 870 871
    WDML_XACT*		pXAct;

    UnpackDDElParam(WM_DDE_POKE, lParam, &uiLo, &uiHi);

872
    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_POKE,
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888
				  0, WDML_MakeHszFromAtom(pConv->instance, uiHi));
    if (pXAct)
    {
	pXAct->atom = uiHi;
	pXAct->hMem = (HGLOBAL)uiLo;
    }
    return pXAct;
}

/******************************************************************
 *		WDML_ServerHandlePoke
 *
 *
 */
static	WDML_QUEUE_STATE WDML_ServerHandlePoke(WDML_CONV* pConv, WDML_XACT* pXAct)
{
889 890
    DDEPOKE*		pDdePoke;
    HDDEDATA		hDdeData;
891
    BOOL		fBusy = FALSE, fAck = FALSE;
892

893
    pDdePoke = GlobalLock(pXAct->hMem);
894 895
    if (!pDdePoke)
    {
896
	return WDML_QS_ERROR;
897 898
    }

899
    if (!(pConv->instance->CBFflags & CBF_FAIL_POKES))
900
    {
901
	hDdeData = DdeCreateDataHandle(pConv->instance->instanceID, pDdePoke->Value,
902
				       GlobalSize(pXAct->hMem) - FIELD_OFFSET(DDEPOKE, Value),
903
				       0, 0, pDdePoke->cfFormat, 0);
904
	if (hDdeData)
905 906
	{
	    HDDEDATA	hDdeDataOut;
907 908 909

	    hDdeDataOut = WDML_InvokeCallback(pConv->instance, XTYP_POKE, pDdePoke->cfFormat,
					      (HCONV)pConv, pConv->hszTopic, pXAct->hszItem,
910
					      hDdeData, 0, 0);
911
	    switch ((ULONG_PTR)hDdeDataOut)
912 913
	    {
	    case DDE_FACK:
914
		fAck = TRUE;
915 916
		break;
	    case DDE_FBUSY:
917
		fBusy = TRUE;
918 919
		break;
	    default:
920
		FIXME("Unsupported returned value %p\n", hDdeDataOut);
921
		/* fal through */
922
	    case DDE_FNOTPROCESSED:
923 924 925 926 927
		break;
	    }
	    DdeFreeDataHandle(hDdeData);
	}
    }
928
    GlobalUnlock(pXAct->hMem);
929

930
    if (!fAck)
931
    {
932
	GlobalFree(pXAct->hMem);
933
    }
934 935 936 937 938 939
    WDML_PostAck(pConv, WDML_SERVER_SIDE, 0, fBusy, fAck, pXAct->atom, pXAct->lParam, WM_DDE_POKE);

    WDML_DecHSZ(pConv->instance, pXAct->hszItem);

    return WDML_QS_HANDLED;
}
940

941 942 943 944 945 946 947 948 949 950 951
/******************************************************************
 *		WDML_ServerQueueTerminate
 *
 *
 */
static	WDML_XACT*	WDML_ServerQueueTerminate(WDML_CONV* pConv, LPARAM lParam)
{
    WDML_XACT*	pXAct;

    pXAct = WDML_AllocTransaction(pConv->instance, WM_DDE_TERMINATE, 0, 0);
    return pXAct;
952 953 954 955 956 957 958
}

/******************************************************************
 *		WDML_ServerHandleTerminate
 *
 *
 */
959
static	WDML_QUEUE_STATE WDML_ServerHandleTerminate(WDML_CONV* pConv, WDML_XACT* pXAct)
960
{
961 962 963 964 965
    /* billx: two things to remove: the conv, and associated links.
     * Respond with another WM_DDE_TERMINATE iMsg.
     */
    if (!(pConv->instance->CBFflags & CBF_SKIP_DISCONNECTS))
    {
966
	WDML_InvokeCallback(pConv->instance, XTYP_DISCONNECT, 0, (HCONV)pConv, 0, 0,
967 968
			    0, 0, (pConv->wStatus & ST_ISSELF) ? 1 : 0);
    }
969
    PostMessageW(pConv->hwndClient, WM_DDE_TERMINATE, (WPARAM)pConv->hwndServer, 0);
970
    WDML_RemoveConv(pConv, WDML_SERVER_SIDE);
971

972 973 974 975 976 977 978 979
    return WDML_QS_HANDLED;
}

/******************************************************************
 *		WDML_ServerHandle
 *
 *
 */
980
WDML_QUEUE_STATE WDML_ServerHandle(WDML_CONV* pConv, WDML_XACT* pXAct)
981 982 983 984 985 986 987 988 989 990 991
{
    WDML_QUEUE_STATE	qs = WDML_QS_ERROR;

    switch (pXAct->ddeMsg)
    {
    case WM_DDE_INITIATE:
	FIXME("WM_DDE_INITIATE shouldn't be there!\n");
	break;
    case WM_DDE_REQUEST:
	qs = WDML_ServerHandleRequest(pConv, pXAct);
	break;
992

993 994 995
    case WM_DDE_ADVISE:
	qs = WDML_ServerHandleAdvise(pConv, pXAct);
	break;
996

997 998 999
    case WM_DDE_UNADVISE:
	qs = WDML_ServerHandleUnadvise(pConv, pXAct);
	break;
1000

1001 1002 1003
    case WM_DDE_EXECUTE:
	qs = WDML_ServerHandleExecute(pConv, pXAct);
	break;
1004

1005 1006 1007
    case WM_DDE_POKE:
	qs = WDML_ServerHandlePoke(pConv, pXAct);
	break;
1008

1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
    case WM_DDE_TERMINATE:
	qs = WDML_ServerHandleTerminate(pConv, pXAct);
	break;

    case WM_DDE_ACK:
	WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n");
	break;

    default:
	FIXME("Unsupported message %d\n", pXAct->ddeMsg);
    }
    return qs;
1021 1022 1023 1024 1025 1026 1027 1028 1029
}

/******************************************************************
 *		WDML_ServerConvProc
 *
 *
 */
static LRESULT CALLBACK WDML_ServerConvProc(HWND hwndServer, UINT iMsg, WPARAM wParam, LPARAM lParam)
{
1030
    WDML_INSTANCE*	pInstance;
1031
    WDML_CONV*		pConv;
1032
    WDML_XACT*		pXAct = NULL;
1033

1034
    TRACE("%p %04x %08lx %08lx\n", hwndServer, iMsg, wParam, lParam);
1035

1036 1037 1038 1039 1040 1041 1042 1043
    if (iMsg == WM_DESTROY)
    {
	pConv = WDML_GetConvFromWnd(hwndServer);
	if (pConv && !(pConv->wStatus & ST_TERMINATED))
	{
	    WDML_ServerHandleTerminate(pConv, NULL);
	}
    }
1044 1045
    if (iMsg < WM_DDE_FIRST || iMsg > WM_DDE_LAST)
    {
1046 1047
        return IsWindowUnicode(hwndServer) ? DefWindowProcW(hwndServer, iMsg, wParam, lParam) :
                                             DefWindowProcA(hwndServer, iMsg, wParam, lParam);
1048 1049
    }

1050 1051 1052
    pInstance = WDML_GetInstanceFromWnd(hwndServer);
    pConv = WDML_GetConvFromWnd(hwndServer);

1053
    if (!pConv)
1054
    {
1055
	ERR("Got a message (%x) on a not known conversation, dropping request\n", iMsg);
1056
        return 0;
1057
    }
1058
    if (pConv->hwndClient != WIN_GetFullHandle( (HWND)wParam ) || pConv->hwndServer != hwndServer)
1059
    {
1060
	ERR("mismatch between C/S windows and conversation\n");
1061
        return 0;
1062 1063 1064 1065
    }
    if (pConv->instance != pInstance || pConv->instance == NULL)
    {
	ERR("mismatch in instances\n");
1066
        return 0;
1067 1068 1069 1070 1071
    }

    switch (iMsg)
    {
    case WM_DDE_INITIATE:
1072
	FIXME("WM_DDE_INITIATE message received!\n");
1073
	break;
1074

1075
    case WM_DDE_REQUEST:
1076
	pXAct = WDML_ServerQueueRequest(pConv, lParam);
1077
	break;
1078

1079
    case WM_DDE_ADVISE:
1080
	pXAct = WDML_ServerQueueAdvise(pConv, lParam);
1081
	break;
1082

1083
    case WM_DDE_UNADVISE:
1084
	pXAct = WDML_ServerQueueUnadvise(pConv, lParam);
1085
	break;
1086

1087
    case WM_DDE_EXECUTE:
1088
	pXAct = WDML_ServerQueueExecute(pConv, lParam);
1089
	break;
1090

1091
    case WM_DDE_POKE:
1092
	pXAct = WDML_ServerQueuePoke(pConv, lParam);
1093
	break;
1094

1095
    case WM_DDE_TERMINATE:
1096
	pXAct = WDML_ServerQueueTerminate(pConv, lParam);
1097 1098 1099 1100 1101 1102 1103
	break;

    case WM_DDE_ACK:
	WARN("Shouldn't receive a ACK message (never requests them). Ignoring it\n");
	break;

    default:
1104
	FIXME("Unsupported message %x\n", iMsg);
1105
        break;
1106
    }
1107 1108

    if (pXAct)
1109 1110
    {
	pXAct->lParam = lParam;
1111 1112

	if ((pConv->wStatus & ST_BLOCKED) || WDML_ServerHandle(pConv, pXAct) == WDML_QS_BLOCK)
1113
	{
1114
            TRACE("Transactions are blocked, add to the queue and exit\n");
1115 1116 1117 1118 1119 1120 1121
	    WDML_QueueTransaction(pConv, pXAct);
	}
	else
	{
	    WDML_FreeTransaction(pInstance, pXAct, TRUE);
	}
    }
1122 1123
    else
        pConv->instance->lastError = DMLERR_MEMORY_ERROR;
1124

1125 1126
    return 0;
}