|
|
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(
1.1.1.2 ! root 64: UINT wType,
! 65: UINT wFmt,
1.1 root 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++) {
1.1.1.2 ! root 286: strcpy(pszTopicList, topicList[i].pszTopic);
1.1 root 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++) {
1.1.1.2 ! root 352: strcpy(pszItemList, pItemList[i].pszItem);
1.1 root 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++) {
1.1.1.2 ! root 397: strcpy(pszT, aFormats[i].sz);
! 398: pszT += strlen(pszT);
1.1 root 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]) {
1.1.1.2 ! root 446: hDataRand[iFmt] = DdeCreateDataHandle(idInst, NULL, 0, 10,
! 447: pXferInfo->hszItem, (UINT)pXferInfo->wFmt,
! 448: fAppowned ? HDATA_APPOWNED : 0);
1.1 root 449: if (pszData = DdeAccessData(hDataRand[iFmt], NULL)) {
450: wsprintf(pszData, "%d", seed);
451: DdeUnaccessData(hDataRand[iFmt]);
452: }
453: }
454: hData = hDataRand[iFmt];
455: if (!fAppowned)
456: hDataRand[iFmt] = 0;
457: return(hData);
458: break;
459:
460: case XTYP_ADVSTART:
461: return(1);
462: }
463: return(0);
464: }
465:
466: /*
467: * This is a runaway item. Each time it is requested, it changes.
468: * pokes just make it change again.
469: */
470: HDDEDATA TestCountXfer(
471: PXFERINFO pXferInfo,
472: WORD iFmt)
473: {
474: CHAR szT[16]; // SS==DS!
475: LPSTR pszData;
476: HDDEDATA hData;
477: WORD i;
478:
479: switch (pXferInfo->wType) {
480: case XTYP_POKE:
481: // we expect an ascii number to replace the current count.
482: pszComment = "Count poke received";
483: InvalidateRect(hwndServer, &rcComment, TRUE);
484: InvalidateRect(hwndServer, &rcCount, TRUE);
485: if (DdeGetData(pXferInfo->hData, szT, 10, 0)) {
486: szT[9] = '\0'; // just incase we overran.
487: sscanf(szT, "%ld", &count);
488: for (i = 0; i < CFORMATS; i++) {
489: if (hDataCount[i])
490: DdeFreeDataHandle(hDataCount[i]);
491: hDataCount[i] = 0;
492: }
493: DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
494: return(1);
495: }
496: break;
497:
498: case XTYP_REQUEST:
499: pszComment = "Count data requested.";
500: InvalidateRect(hwndServer, &rcComment, TRUE);
501: case XTYP_ADVREQ:
502: Delay(RenderDelay, FALSE);
503: if (!hDataCount[iFmt]) {
504: hDataCount[iFmt] = DdeCreateDataHandle(idInst, NULL, 0, 10, pXferInfo->hszItem,
505: pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
506: if (pszData = DdeAccessData(hDataCount[iFmt], NULL)) {
507: wsprintf(pszData, "%ld", count);
508: DdeUnaccessData(hDataCount[iFmt]);
509: }
510: }
511: hData = hDataCount[iFmt];
512: if (!fAppowned)
513: hDataCount[iFmt] = 0;
514: return(hData);
515: break;
516:
517: case XTYP_ADVSTART:
518: return(1);
519: }
520: return(0);
521: }
522:
523:
524: /*
525: * This is not a runaway item. Only Pokes make it change.
526: */
527: HDDEDATA TestHugeXfer(
528: PXFERINFO pXferInfo,
529: WORD iFmt)
530: {
531: BOOL fSuccess;
532: DWORD ulcb;
533: LPBYTE lpData;
534: WORD i;
535: HDDEDATA hData;
536:
537: switch (pXferInfo->wType) {
538: case XTYP_POKE:
539: ulcb = DdeGetData(pXferInfo->hData, NULL, 0, 0);
540: fSuccess = CheckHugeData(pXferInfo->hData);
541: if (fSuccess) {
542: pszComment = "Huge poke data successfully received.";
543: } else {
544: wsprintf(szComment, "%ld bytes of invalid Huge data received.", ulcb);
545: pszComment = szComment;
546: }
547: InvalidateRect(hwndServer, &rcComment, TRUE);
548: InvalidateRect(hwndServer, &rcHugeSize, TRUE);
549: if (fSuccess) {
550: for (i = 0; i < CFORMATS; i++) {
551: if (hDataHuge[i]) {
552: DdeFreeDataHandle(hDataHuge[i]);
553: hDataHuge[i] = 0;
554: }
555: }
556: /*
557: * Since callback data handles are only good for the duration of
558: * the callback, we must copy the data to our own data handle.
559: */
560: lpData = DdeAccessData(pXferInfo->hData, &cbHuge);
561: hDataHuge[iFmt] = DdeCreateDataHandle(idInst, lpData, cbHuge, 0,
562: pXferInfo->hszItem, pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
563: DdeUnaccessData(pXferInfo->hData);
564: DdePostAdvise(idInst, pXferInfo->hszTopic, pXferInfo->hszItem);
565: }
566: return(fSuccess);
567: break;
568:
569: case XTYP_REQUEST:
570: pszComment = "Huge data requested.";
571: InvalidateRect(hwndServer, &rcComment, TRUE);
572: case XTYP_ADVREQ:
573: Delay(RenderDelay, FALSE);
574: if (!hDataHuge[iFmt]) {
575: cbHuge = (DWORD)rand() * 64L + 0x10000L;
576: wsprintf(szComment, "Generating huge data - length=%ld...", cbHuge);
577: pszComment = szComment;
578: InvalidateRect(hwndServer, &rcComment, TRUE);
579: UpdateWindow(hwndServer);
1.1.1.2 ! root 580: hDataHuge[iFmt] = CreateHugeDataHandle((LONG)cbHuge, 4325, 345, 5,
1.1 root 581: pXferInfo->hszItem,
1.1.1.2 ! root 582: pXferInfo->wFmt, (WORD)(fAppowned ? HDATA_APPOWNED : 0));
1.1 root 583: pszComment = "";
584: InvalidateRect(hwndServer, &rcComment, TRUE);
585: InvalidateRect(hwndServer, &rcHugeSize, TRUE);
586: }
587: hData = hDataHuge[iFmt];
588: if (!fAppowned)
589: hDataHuge[iFmt] = 0;
590: return(hData);
591: break;
592:
593: case XTYP_ADVSTART:
594: return(1);
595: }
596: return(0);
597: }
598:
599:
600: HDDEDATA HelpXfer(
601: PXFERINFO pXferInfo,
602: WORD iFmt)
603: {
604: HDDEDATA hData;
605:
606: switch (pXferInfo->wType) {
607: case XTYP_REQUEST:
608: pszComment = "Help text requested.";
609: InvalidateRect(hwndServer, &rcComment, TRUE);
610: case XTYP_ADVREQ:
611: if (!hDataHelp[iFmt]) {
612: hDataHelp[iFmt] = DdeCreateDataHandle(idInst, szDdeHelp, strlen(szDdeHelp) + 1,
613: 0, pXferInfo->hszItem, pXferInfo->wFmt, fAppowned ? HDATA_APPOWNED : 0);
614: }
615: hData = hDataHelp[iFmt];
616: if (!fAppowned)
617: hDataHelp[iFmt] = 0;
618: return(hData);
619: break;
620:
621: case XTYP_ADVSTART:
622: return(1);
623: }
624: return(0);
625: }
626:
627:
628: /***************************** Private Function ****************************\
629: * This creates often used global hszs from standard global strings.
630: * It also fills the hsz fields of the topic and item tables.
631: *
632: \***************************************************************************/
633: VOID Hszize()
634: {
635: register ITEMLIST *pItemList;
636: WORD iTopic, iItem;
637:
638: hszAppName = DdeCreateStringHandle(idInst, szServer, 0);
639:
640: for (iTopic = 0; iTopic < CTOPICS; iTopic++) {
641: topicList[iTopic].hszTopic =
642: DdeCreateStringHandle(idInst, topicList[iTopic].pszTopic, 0);
643: pItemList = topicList[iTopic].pItemList;
644: for (iItem = 0; iItem < topicList[iTopic].cItems; iItem++) {
645: pItemList[iItem].hszItem =
646: DdeCreateStringHandle(idInst, pItemList[iItem].pszItem, 0);
647: }
648: }
649: }
650:
651:
652:
653:
654:
655: /***************************** Private Function ****************************\
656: * This destroys often used global hszs from standard global strings.
657: *
658: \***************************************************************************/
659: VOID UnHszize()
660: {
661: register ITEMLIST *pItemList;
662: WORD iTopic, iItem;
663:
664: DdeFreeStringHandle(idInst, hszAppName);
665:
666: for (iTopic = 0; iTopic < CTOPICS; iTopic++) {
667: DdeFreeStringHandle(idInst, topicList[iTopic].hszTopic);
668: pItemList = topicList[iTopic].pItemList;
669: for (iItem = 0; iItem < topicList[iTopic].cItems; iItem++) {
670: DdeFreeStringHandle(idInst, pItemList[iItem].hszItem);
671: }
672: }
673: }
674:
675:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.