|
|
1.1 root 1: /*
2: * This module serves to demonstrate one way a sophisticated DDE server
3: * that uses enumerable topics and items might be implemented. It takes
4: * full advantage of appowned data handles (when fAppowned is set) to
5: * minimize the need for repeated rendering of data when shared with
6: * multiple clients.
7: *
8: * The server supports full system topic information plus help and non
9: * system topic item enumeration for the benefit of browsing clients
10: * that are wondering what's around.
11: *
12: * This server can be made secure by altering the conversation context
13: * filter.
14: *
15: * This server can appear to support alternate codepages and languages
16: * by altering the conversation context filter. On Windows this is
17: * pretty much moot since there is not yet a clearly defined way of
18: * doing international communication and because the atom manager restricts
19: * what topic and item strings can be used on the system.
20: */
21:
22: #include <string.h>
23: #include <stdlib.h>
24: #include <stdio.h>
25: #include <memory.h>
26: #include "server.h"
27: #include "huge.h"
28:
29: /*
30: * This function verifies that the incomming conversation context fits the
31: * server's context filter's requirements.
32: */
33: BOOL ValidateContext(
34: PCONVCONTEXT pCC)
35: {
36: // make sure our CCFilter allows it...mock security, language support
37: // old DDE app client case...pCC == NULL
38: if (pCC == NULL &&
39: CCFilter.dwSecurity == 0 && // were nonsecure
40: CCFilter.iCodePage == CP_WINANSI // were normal cp
41: ) {
42: return(TRUE);
43: }
44:
45: if (pCC &&
46: pCC->wFlags == CCFilter.wFlags && // no special flags needed
47: pCC->iCodePage == CCFilter.iCodePage && // codepages match
48: pCC->dwSecurity == CCFilter.dwSecurity) { // security passes
49: // dont care about language and country.
50: return(TRUE);
51: }
52: return(FALSE); // disallow no match
53: }
54:
55:
56: /***************************** Public Function ****************************\
57: *
58: * This function is called by the DDE manager DLL and passes control onto
59: * the apropriate function pointed to by the global topic and item arrays.
60: * It handles all DDE interaction generated by external events.
61: *
62: \***************************************************************************/
63: HDDEDATA CALLBACK DdeCallback(
64: WORD wType,
65: WORD wFmt,
66: HCONV hConv,
67: HSZ hszTopic,
68: HSZ hszItem,
69: HDDEDATA hData,
70: DWORD lData1,
71: DWORD lData2)
72: {
73: WORD i, j;
74: register ITEMLIST *pItemList;
75: WORD cItems, iFmt;
76: HDDEDATA hDataRet;
77:
78: /*
79: * Block this callback if its blockable and we are supposed to.
80: */
81: if (fBlockNextCB && !(wType & XTYPF_NOBLOCK)) {
82: fBlockNextCB = FALSE;
83: fAllEnabled = FALSE;
84: return(CBR_BLOCK);
85: }
86:
87: /*
88: * Block this callback if its associated with a conversation and we
89: * are supposed to.
90: */
91: if (fTermNextCB && hConv) {
92: fTermNextCB = FALSE;
93: DdeDisconnect(hConv);
94: wType = XTYP_DISCONNECT;
95: }
96:
97: /*
98: * Keep a count of connections
99: */
100: if (wType == XTYP_CONNECT_CONFIRM) {
101: cServers++;
102: InvalidateRect(hwndServer, &rcConnCount, TRUE);
103: return(0);
104: }
105: if (wType == XTYP_DISCONNECT) {
106: cServers--;
107: InvalidateRect(hwndServer, &rcConnCount, TRUE);
108: return(0);
109: }
110:
111:
112: /*
113: * only allow transactions on the formats we support if they have a format.
114: */
115: if (wFmt) {
116: for (iFmt = 0; iFmt < CFORMATS; iFmt++) {
117: if (wFmt == aFormats[iFmt].atom)
118: break;
119: }
120: if (iFmt == CFORMATS)
121: return(0); // illegal format - ignore now.
122: }
123:
124: /*
125: * Executes are allowed only on the system topic. This is a general
126: * convention, not a requirement.
127: *
128: * Any executes received result in the execute text being shown in
129: * the server client area. No real action is taken.
130: */
131: if (wType == XTYP_EXECUTE) {
132: if (hszTopic == topicList[0].hszTopic) { // must be on system topic
133: // Format is assumed to be CF_TEXT.
134: DdeGetData(hData, (LPBYTE)szExec, MAX_EXEC, 0);
135: szExec[MAX_EXEC - 1] = '\0';
136: InvalidateRect(hwndServer, &rcExec, TRUE);
137: hDataRet = TRUE;
138: goto ReturnSpot;
139: }
140: pszComment = "Execute received on non-system topic - ignored";
141: InvalidateRect(hwndServer, &rcComment, TRUE);
142: return(0);
143: }
144:
145: /*
146: * Process wild initiates here
147: */
148: if (wType == XTYP_WILDCONNECT) {
149: HSZ ahsz[(CTOPICS + 1) * 2];
150: /*
151: * He wants a hsz list of all our available app/topic pairs
152: * that conform to hszTopic and hszItem(App).
153: */
154:
155: if (!ValidateContext((PCONVCONTEXT)lData1)) {
156: return(FALSE);
157: }
158:
159: if (hszItem != hszAppName && hszItem != 0) {
160: // we only support the hszAppName service
161: return(0);
162: }
163:
164: // scan the topic table and create hsz pairs
165: j = 0;
166: for (i = 0; i < CTOPICS; i++) {
167: if (hszTopic == 0 || hszTopic == topicList[i].hszTopic) {
168: ahsz[j++] = hszAppName;
169: ahsz[j++] = topicList[i].hszTopic;
170: }
171: }
172:
173: // cap off the list with 0s
174: ahsz[j++] = ahsz[j++] = 0L;
175:
176: // send it back
177: return(DdeCreateDataHandle(idInst, (LPBYTE)&ahsz[0], sizeof(HSZ) * j, 0L, 0, wFmt, 0));
178: }
179:
180: /*
181: * Check our hsz tables and send to the apropriate proc. to process.
182: * We use DdeCmpStringHandles() which is the portable case-insensitive
183: * method of comparing string handles. (this is a macro on windows so
184: * there is no real speed hit.) On WINDOWS, HSZs are case-insensitive
185: * anyway, but this may not be the case on other platforms.
186: */
187: for (i = 0; i < CTOPICS; i++) {
188: if (DdeCmpStringHandles(topicList[i].hszTopic, hszTopic) == 0) {
189:
190: /*
191: * connections must be on a topic we support.
192: */
193: if (wType == XTYP_CONNECT) {
194: return(ValidateContext((PCONVCONTEXT)lData1));
195: }
196:
197: pItemList = topicList[i].pItemList;
198: cItems = topicList[i].cItems;
199: for (j = 0; j < cItems; j++) {
200: if (DdeCmpStringHandles(pItemList[j].hszItem, hszItem) == 0) {
201: XFERINFO xi;
202: /*
203: * Make call to worker function here...
204: */
205: xi.wType = wType;
206: xi.wFmt = wFmt;
207: xi.hConv = hConv;
208: xi.hszTopic = hszTopic;
209: xi.hszItem = hszItem;
210: xi.hData = hData;
211: xi.lData1 = lData1;
212: xi.lData2 = lData2;
213: hDataRet = (*pItemList[j].npfnCallback)(&xi, iFmt);
214:
215: ReturnSpot:
216: /*
217: * The table functions return a boolean or data.
218: * It gets translated here.
219: */
220: switch (wType & XCLASS_MASK) {
221: case XCLASS_DATA:
222: return(hDataRet);
223: break;
224: case XCLASS_FLAGS:
225: return(hDataRet ? DDE_FACK : DDE_FNOTPROCESSED);
226: break;
227: case XCLASS_BOOL:
228: return(TRUE);
229: default: // XCLASS_NOTIFICATION
230: return(0);
231: break;
232: }
233: break;
234: }
235: }
236: break;
237: }
238: }
239:
240: /*
241: * anything else fails - DDEML is designed so that a 0 return is ALWAYS ok.
242: */
243: return(0);
244: }
245:
246:
247:
248:
249:
250: /***************************** Private Function ****************************\
251: * This passes out a standard tab-delimited list of topic names for this
252: * application.
253: *
254: * This support is required for other apps to be able to
255: * find out about us. This kind of support should be in every DDE
256: * application.
257: *
258: \***************************************************************************/
259: HDDEDATA TopicListXfer(
260: PXFERINFO pXferInfo,
261: WORD iFmt)
262: {
263: WORD cbAlloc, i;
264: LPSTR pszTopicList;
265: HDDEDATA hData;
266:
267: if (pXferInfo->wType == XTYP_ADVSTART)
268: return(TRUE);
269:
270: if (pXferInfo->wType != XTYP_REQUEST &&
271: pXferInfo->wType != XTYP_ADVREQ)
272: return(0);
273: /*
274: * construct the list of topics we have
275: */
276: cbAlloc = 0;
277: for (i = 0; i < CTOPICS; i++)
278: cbAlloc += lstrlen(topicList[i].pszTopic) + 1; // 1 for tab
279:
280: // allocate a data handle big enough for the list.
281: hData = DdeCreateDataHandle(idInst, NULL, 0, cbAlloc, pXferInfo->hszItem,
282: pXferInfo->wFmt, 0);
283: pszTopicList = (LPSTR)DdeAccessData(hData, NULL);
284: if (pszTopicList) {
285: for (i = 0; i < CTOPICS; i++) {
286: _fstrcpy(pszTopicList, topicList[i].pszTopic);
287: pszTopicList += strlen(topicList[i].pszTopic);
288: *pszTopicList++ = '\t';
289: }
290: *--pszTopicList = '\0';
291: DdeUnaccessData(hData);
292: return(hData);
293: }
294: return(0);
295: }
296:
297:
298:
299:
300: /***************************** Private Function ****************************\
301: * This passes out a standard tab-delimited list of item names for the
302: * specified topic.
303: *
304: * This support is required for other apps to be able to
305: * find out about us. This kind of support should be in every DDE
306: * application.
307: *
308: \***************************************************************************/
309: HDDEDATA ItemListXfer(
310: PXFERINFO pXferInfo,
311: WORD iFmt)
312: {
313: WORD cbAlloc, i, iItem, cItems;
314: ITEMLIST *pItemList = 0;
315: LPSTR pszItemList;
316: HDDEDATA hData;
317:
318: if (pXferInfo->wType == XTYP_ADVSTART)
319: return(TRUE);
320:
321: if (pXferInfo->wType != XTYP_REQUEST &&
322: pXferInfo->wType != XTYP_ADVREQ)
323: return(0);
324: /*
325: * construct the list of items we support for this topic - this supports
326: * more than the minimum standard which would support SysItems only on
327: * the system topic.
328: */
329:
330: // locate the requested topic item table
331: for (i = 0; i < CTOPICS; i++) {
332: if (pXferInfo->hszTopic == topicList[i].hszTopic) {
333: pItemList = topicList[i].pItemList;
334: cItems = topicList[i].cItems;
335: break;
336: }
337: }
338:
339: if (!pItemList)
340: return(0); // item not found
341:
342: cbAlloc = 0;
343: for (iItem = 0; iItem < cItems; iItem++)
344: cbAlloc += lstrlen(pItemList[iItem].pszItem) + 1; // 1 for tab
345:
346: // allocate a data handle big enough for the list.
347: hData = DdeCreateDataHandle(idInst, NULL, 0, cbAlloc, pXferInfo->hszItem,
348: pXferInfo->wFmt, 0);
349: pszItemList = (LPSTR)DdeAccessData(hData, NULL);
350: if (pszItemList) {
351: for (i = 0; i < cItems; i++) {
352: _fstrcpy(pszItemList, pItemList[i].pszItem);
353: pszItemList += strlen(pItemList[i].pszItem);
354: *pszItemList++ = '\t';
355: }
356: *--pszItemList = '\0';
357: DdeUnaccessData(hData);
358: return(hData);
359: }
360: return(0);
361: }
362:
363:
364:
365:
366:
367: /***************************** Private Function ****************************\
368: * Gives out a 0 terminated array of dde format numbers supported by this app.
369: *
370: * This support is required for other apps to be able to
371: * find out about us. This kind of support should be in every DDE
372: * application.
373: *
374: \***************************************************************************/
375: HDDEDATA sysFormatsXfer(
376: PXFERINFO pXferInfo,
377: WORD iFmt)
378: {
379: INT i, cb;
380: LPSTR psz, pszT;
381: HDDEDATA hData;
382:
383: if (pXferInfo->wType == XTYP_ADVSTART)
384: return(TRUE);
385:
386: if (pXferInfo->wType != XTYP_REQUEST &&
387: pXferInfo->wType != XTYP_ADVREQ)
388: return(0);
389:
390: for (i = 0, cb = 0; i < CFORMATS; i++)
391: cb += strlen(aFormats[i].sz) + 1;
392:
393: hData = DdeCreateDataHandle(idInst, NULL, (DWORD)cb,
394: 0L, pXferInfo->hszItem, pXferInfo->wFmt, 0);
395: psz = pszT = DdeAccessData(hData, NULL);
396: for (i = 0; i < CFORMATS; i++) {
397: _fstrcpy(pszT, aFormats[i].sz);
398: pszT += _fstrlen(pszT);
399: *pszT++ = '\t';
400: }
401: *(--pszT) = '\0';
402: DdeUnaccessData(hData);
403: return(hData);
404: }
405:
406:
407:
408: /*
409: * This is a runaway item. Each time it is requested, it changes.
410: * pokes just make it change again.
411: */
412: HDDEDATA TestRandomXfer(
413: PXFERINFO pXferInfo,
414: WORD iFmt)
415: {
416: CHAR szT[10]; // SS==DS!
417: LPSTR pszData;
418: HDDEDATA hData;
419: WORD i;
420:
421: switch (pXferInfo->wType) {
422: case XTYP_POKE:
423: // we expect an ascii number to replace the current seed.
424: pszComment = "Rand poke received.";
425: InvalidateRect(hwndServer, &rcComment, TRUE);
426: InvalidateRect(hwndServer, &rcRand, TRUE);
427: if (DdeGetData(pXferInfo->hData, szT, 10, 0)) {
428: szT[9] = '\0'; // just incase we overran.
429: sscanf(szT, "%d", &seed);
430: for (i = 0; i < CFORMATS; i++) {
431: if (hDataRand[i])
432: DdeFreeDataHandle(hDataRand[i]);
433: hDataRand[i] = 0;
434: }
435: DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
436: return(1);
437: }
438: break;
439:
440: case XTYP_REQUEST:
441: pszComment = "Rand data requested.";
442: InvalidateRect(hwndServer, &rcComment, TRUE);
443: case XTYP_ADVREQ:
444: Delay(RenderDelay, FALSE);
445: if (!hDataRand[iFmt]) {
446: hDataRand[iFmt] = DdeCreateDataHandle(idInst, NULL, 0, 10, pXferInfo->hszItem,
447: pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
448: if (pszData = DdeAccessData(hDataRand[iFmt], NULL)) {
449: wsprintf(pszData, "%d", seed);
450: DdeUnaccessData(hDataRand[iFmt]);
451: }
452: }
453: hData = hDataRand[iFmt];
454: if (!fAppowned)
455: hDataRand[iFmt] = 0;
456: return(hData);
457: break;
458:
459: case XTYP_ADVSTART:
460: return(1);
461: }
462: return(0);
463: }
464:
465: /*
466: * This is a runaway item. Each time it is requested, it changes.
467: * pokes just make it change again.
468: */
469: HDDEDATA TestCountXfer(
470: PXFERINFO pXferInfo,
471: WORD iFmt)
472: {
473: CHAR szT[16]; // SS==DS!
474: LPSTR pszData;
475: HDDEDATA hData;
476: WORD i;
477:
478: switch (pXferInfo->wType) {
479: case XTYP_POKE:
480: // we expect an ascii number to replace the current count.
481: pszComment = "Count poke received";
482: InvalidateRect(hwndServer, &rcComment, TRUE);
483: InvalidateRect(hwndServer, &rcCount, TRUE);
484: if (DdeGetData(pXferInfo->hData, szT, 10, 0)) {
485: szT[9] = '\0'; // just incase we overran.
486: sscanf(szT, "%ld", &count);
487: for (i = 0; i < CFORMATS; i++) {
488: if (hDataCount[i])
489: DdeFreeDataHandle(hDataCount[i]);
490: hDataCount[i] = 0;
491: }
492: DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
493: return(1);
494: }
495: break;
496:
497: case XTYP_REQUEST:
498: pszComment = "Count data requested.";
499: InvalidateRect(hwndServer, &rcComment, TRUE);
500: case XTYP_ADVREQ:
501: Delay(RenderDelay, FALSE);
502: if (!hDataCount[iFmt]) {
503: hDataCount[iFmt] = DdeCreateDataHandle(idInst, NULL, 0, 10, pXferInfo->hszItem,
504: pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
505: if (pszData = DdeAccessData(hDataCount[iFmt], NULL)) {
506: wsprintf(pszData, "%ld", count);
507: DdeUnaccessData(hDataCount[iFmt]);
508: }
509: }
510: hData = hDataCount[iFmt];
511: if (!fAppowned)
512: hDataCount[iFmt] = 0;
513: return(hData);
514: break;
515:
516: case XTYP_ADVSTART:
517: return(1);
518: }
519: return(0);
520: }
521:
522:
523: /*
524: * This is not a runaway item. Only Pokes make it change.
525: */
526: HDDEDATA TestHugeXfer(
527: PXFERINFO pXferInfo,
528: WORD iFmt)
529: {
530: BOOL fSuccess;
531: DWORD ulcb;
532: LPBYTE lpData;
533: WORD i;
534: HDDEDATA hData;
535:
536: switch (pXferInfo->wType) {
537: case XTYP_POKE:
538: ulcb = DdeGetData(pXferInfo->hData, NULL, 0, 0);
539: fSuccess = CheckHugeData(pXferInfo->hData);
540: if (fSuccess) {
541: pszComment = "Huge poke data successfully received.";
542: } else {
543: wsprintf(szComment, "%ld bytes of invalid Huge data received.", ulcb);
544: pszComment = szComment;
545: }
546: InvalidateRect(hwndServer, &rcComment, TRUE);
547: InvalidateRect(hwndServer, &rcHugeSize, TRUE);
548: if (fSuccess) {
549: for (i = 0; i < CFORMATS; i++) {
550: if (hDataHuge[i]) {
551: DdeFreeDataHandle(hDataHuge[i]);
552: hDataHuge[i] = 0;
553: }
554: }
555: /*
556: * Since callback data handles are only good for the duration of
557: * the callback, we must copy the data to our own data handle.
558: */
559: lpData = DdeAccessData(pXferInfo->hData, &cbHuge);
560: hDataHuge[iFmt] = DdeCreateDataHandle(idInst, lpData, cbHuge, 0,
561: pXferInfo->hszItem, pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
562: DdeUnaccessData(pXferInfo->hData);
563: DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
564: }
565: return(fSuccess);
566: break;
567:
568: case XTYP_REQUEST:
569: pszComment = "Huge data requested.";
570: InvalidateRect(hwndServer, &rcComment, TRUE);
571: case XTYP_ADVREQ:
572: Delay(RenderDelay, FALSE);
573: if (!hDataHuge[iFmt]) {
574: cbHuge = (DWORD)rand() * 64L + 0x10000L;
575: wsprintf(szComment, "Generating huge data - length=%ld...", cbHuge);
576: pszComment = szComment;
577: InvalidateRect(hwndServer, &rcComment, TRUE);
578: UpdateWindow(hwndServer);
579: hDataHuge[iFmt] = CreateHugeDataHandle(cbHuge, 4325, 345, 5,
580: pXferInfo->hszItem,
581: pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
582: pszComment = "";
583: InvalidateRect(hwndServer, &rcComment, TRUE);
584: InvalidateRect(hwndServer, &rcHugeSize, TRUE);
585: }
586: hData = hDataHuge[iFmt];
587: if (!fAppowned)
588: hDataHuge[iFmt] = 0;
589: return(hData);
590: break;
591:
592: case XTYP_ADVSTART:
593: return(1);
594: }
595: return(0);
596: }
597:
598:
599: HDDEDATA HelpXfer(
600: PXFERINFO pXferInfo,
601: WORD iFmt)
602: {
603: HDDEDATA hData;
604:
605: switch (pXferInfo->wType) {
606: case XTYP_REQUEST:
607: pszComment = "Help text requested.";
608: InvalidateRect(hwndServer, &rcComment, TRUE);
609: case XTYP_ADVREQ:
610: if (!hDataHelp[iFmt]) {
611: hDataHelp[iFmt] = DdeCreateDataHandle(idInst, szDdeHelp, strlen(szDdeHelp) + 1,
612: 0, pXferInfo->hszItem, pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
613: }
614: hData = hDataHelp[iFmt];
615: if (!fAppowned)
616: hDataHelp[iFmt] = 0;
617: return(hData);
618: break;
619:
620: case XTYP_ADVSTART:
621: return(1);
622: }
623: return(0);
624: }
625:
626:
627: /***************************** Private Function ****************************\
628: * This creates often used global hszs from standard global strings.
629: * It also fills the hsz fields of the topic and item tables.
630: *
631: \***************************************************************************/
632: VOID Hszize()
633: {
634: register ITEMLIST *pItemList;
635: WORD iTopic, iItem;
636:
637: hszAppName = DdeCreateStringHandle(idInst, szServer, 0);
638:
639: for (iTopic = 0; iTopic < CTOPICS; iTopic++) {
640: topicList[iTopic].hszTopic =
641: DdeCreateStringHandle(idInst, topicList[iTopic].pszTopic, 0);
642: pItemList = topicList[iTopic].pItemList;
643: for (iItem = 0; iItem < topicList[iTopic].cItems; iItem++) {
644: pItemList[iItem].hszItem =
645: DdeCreateStringHandle(idInst, pItemList[iItem].pszItem, 0);
646: }
647: }
648: }
649:
650:
651:
652:
653:
654: /***************************** Private Function ****************************\
655: * This destroys often used global hszs from standard global strings.
656: *
657: \***************************************************************************/
658: VOID UnHszize()
659: {
660: register ITEMLIST *pItemList;
661: WORD iTopic, iItem;
662:
663: DdeFreeStringHandle(idInst, hszAppName);
664:
665: for (iTopic = 0; iTopic < CTOPICS; iTopic++) {
666: DdeFreeStringHandle(idInst, topicList[iTopic].hszTopic);
667: pItemList = topicList[iTopic].pItemList;
668: for (iItem = 0; iItem < topicList[iTopic].cItems; iItem++) {
669: DdeFreeStringHandle(idInst, pItemList[iItem].hszItem);
670: }
671: }
672: }
673:
674:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.