Annotation of embedaddon/php/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c, revision 1.1.1.1
1.1 misho 1: /*
2: This file is part of libXMLRPC - a C library for xml-encoded function calls.
3:
4: Author: Dan Libby (dan@libby.com)
5: Epinions.com may be contacted at feedback@epinions-inc.com
6: */
7:
8: /*
9: Copyright 2001 Epinions, Inc.
10:
11: Subject to the following 3 conditions, Epinions, Inc. permits you, free
12: of charge, to (a) use, copy, distribute, modify, perform and display this
13: software and associated documentation files (the "Software"), and (b)
14: permit others to whom the Software is furnished to do so as well.
15:
16: 1) The above copyright notice and this permission notice shall be included
17: without modification in all copies or substantial portions of the
18: Software.
19:
20: 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF
21: ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY
22: IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR
23: PURPOSE OR NONINFRINGEMENT.
24:
25: 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
26: SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
27: OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING
28: NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH
29: DAMAGES.
30:
31: */
32:
33:
34: /****h* ABOUT/xmlrpc_introspection
35: * AUTHOR
36: * Dan Libby, aka danda (dan@libby.com)
37: * HISTORY
38: * $Log$
39: * Revision 1.4 2003/12/16 21:00:21 sniper
40: * Fix some compile warnings (patch by Joe Orton)
41: *
42: * Revision 1.3 2002/07/05 04:43:53 danda
43: * merged in updates from SF project. bring php repository up to date with xmlrpc-epi version 0.51
44: *
45: * Revision 1.9 2001/09/29 21:58:05 danda
46: * adding cvs log to history section
47: *
48: * 4/10/2001 -- danda -- initial introspection support
49: * TODO
50: * NOTES
51: *******/
52:
53:
54: #ifdef _WIN32
55: #include "xmlrpc_win32.h"
56: #endif
57: #include "queue.h"
58: #include "xmlrpc.h"
59: #include "xmlrpc_private.h"
60: #include "xmlrpc_introspection_private.h"
61: #include <string.h>
62: #include <stdlib.h>
63: #include <stdarg.h>
64:
65:
66: /* forward declarations for static (non public, non api) funcs */
67: static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
68: static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
69: static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
70: static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
71:
72:
73: /*-**********************************
74: * Introspection Callbacks (methods) *
75: ************************************/
76:
77: /* iterates through a list of structs and finds the one with key "name" matching
78: * needle. slow, would benefit from a struct key hash.
79: */
80: static inline XMLRPC_VALUE find_named_value(XMLRPC_VALUE list, const char* needle) {
81: XMLRPC_VALUE xIter = XMLRPC_VectorRewind(list);
82: while(xIter) {
83: const char* name = XMLRPC_VectorGetStringWithID(xIter, xi_token_name);
84: if(name && !strcmp(name, needle)) {
85: return xIter;
86: }
87: xIter = XMLRPC_VectorNext(list);
88: }
89: return NULL;
90: }
91:
92:
93: /* iterates through docs callbacks and calls any that have not yet been called */
94: static void check_docs_loaded(XMLRPC_SERVER server, void* userData) {
95: if(server) {
96: q_iter qi = Q_Iter_Head_F(&server->docslist);
97: while( qi ) {
98: doc_method* dm = Q_Iter_Get_F(qi);
99: if(dm && !dm->b_called) {
100: dm->method(server, userData);
101: dm->b_called = 1;
102: }
103: qi = Q_Iter_Next_F(qi);
104: }
105: }
106: }
107:
108:
109: /* utility function for xi_system_describe_methods_cb */
110: static inline void describe_method(XMLRPC_SERVER server, XMLRPC_VALUE vector, const char* method) {
111: if(method) {
112: server_method* sm = find_method(server, method);
113: if(sm) {
114: XMLRPC_AddValueToVector(vector, sm->desc);
115: }
116: }
117: }
118:
119:
120:
121: /* system.describeMethods() callback */
122: static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
123: XMLRPC_VALUE xParams = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input));
124: XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
125: XMLRPC_VALUE xMethodList = XMLRPC_CreateVector("methodList", xmlrpc_vector_array);
126: XMLRPC_VALUE xTypeList = NULL;
127: int bAll = 1;
128:
129: /* lazy loading of introspection data */
130: check_docs_loaded(server, userData);
131:
132: xTypeList = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList");
133:
134: XMLRPC_AddValueToVector(xResponse, xTypeList);
135: XMLRPC_AddValueToVector(xResponse, xMethodList);
136:
137: /* check if we have any param */
138: if(xParams) {
139: /* check if string or vector (1 or n) */
140: XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(xParams);
141: if(type == xmlrpc_string) {
142: /* just one. spit it out. */
143: describe_method(server, xMethodList, XMLRPC_GetValueString(xParams));
144: bAll = 0;
145: }
146: else if(type == xmlrpc_vector) {
147: /* multiple. spit all out */
148: XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xParams);
149: while(xIter) {
150: describe_method(server, xMethodList, XMLRPC_GetValueString(xIter));
151: xIter = XMLRPC_VectorNext(xParams);
152: }
153: bAll = 0;
154: }
155: }
156:
157: /* otherwise, default to sending all methods */
158: if(bAll) {
159: q_iter qi = Q_Iter_Head_F(&server->methodlist);
160: while( qi ) {
161: server_method* sm = Q_Iter_Get_F(qi);
162: if(sm) {
163: XMLRPC_AddValueToVector(xMethodList, sm->desc);
164: }
165: qi = Q_Iter_Next_F(qi);
166: }
167: }
168:
169: return xResponse;
170: }
171:
172: /* this complies with system.listMethods as defined at http://xmlrpc.usefulinc.com/doc/reserved.html */
173: static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
174: XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
175:
176: q_iter qi = Q_Iter_Head_F(&server->methodlist);
177: while( qi ) {
178: server_method* sm = Q_Iter_Get_F(qi);
179: if(sm) {
180: XMLRPC_VectorAppendString(xResponse, 0, sm->name, 0);
181: }
182: qi = Q_Iter_Next_F(qi);
183: }
184: return xResponse;
185: }
186:
187: /* this complies with system.methodSignature as defined at
188: * http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
189: */
190: static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
191: const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)));
192: XMLRPC_VALUE xResponse = NULL;
193:
194: /* lazy loading of introspection data */
195: check_docs_loaded(server, userData);
196:
197: if(method) {
198: server_method* sm = find_method(server, method);
199: if(sm && sm->desc) {
200: XMLRPC_VALUE xTypesArray = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
201: XMLRPC_VALUE xIter, xParams, xSig, xSigIter;
202: const char* type;
203:
204: /* array of possible signatures. */
205: xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
206:
207: /* find first signature */
208: xSig = XMLRPC_VectorGetValueWithID(sm->desc, xi_token_signatures);
209: xSigIter = XMLRPC_VectorRewind( xSig );
210:
211: /* iterate through sigs */
212: while(xSigIter) {
213: /* first type is the return value */
214: type = XMLRPC_VectorGetStringWithID(XMLRPC_VectorRewind(
215: XMLRPC_VectorGetValueWithID(xSigIter, xi_token_returns)),
216: xi_token_type);
217: XMLRPC_AddValueToVector(xTypesArray,
218: XMLRPC_CreateValueString(NULL,
219: type ? type : type_to_str(xmlrpc_none, 0),
220: 0));
221:
222: /* the rest are parameters */
223: xParams = XMLRPC_VectorGetValueWithID(xSigIter, xi_token_params);
224: xIter = XMLRPC_VectorRewind(xParams);
225:
226: /* iter through params, adding to types array */
227: while(xIter) {
228: XMLRPC_AddValueToVector(xTypesArray,
229: XMLRPC_CreateValueString(NULL,
230: XMLRPC_VectorGetStringWithID(xIter, xi_token_type),
231: 0));
232: xIter = XMLRPC_VectorNext(xParams);
233: }
234:
235: /* add types for this signature */
236: XMLRPC_AddValueToVector(xResponse, xTypesArray);
237:
238: xSigIter = XMLRPC_VectorNext( xSig );
239: }
240: }
241: }
242:
243: return xResponse;
244: }
245:
246: /* this complies with system.methodHelp as defined at
247: * http://xmlrpc.usefulinc.com/doc/sysmethhelp.html
248: */
249: static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
250: const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)));
251: XMLRPC_VALUE xResponse = NULL;
252:
253: /* lazy loading of introspection data */
254: check_docs_loaded(server, userData);
255:
256: if(method) {
257: server_method* sm = find_method(server, method);
258: if(sm && sm->desc) {
259: const char* help = XMLRPC_VectorGetStringWithID(sm->desc, xi_token_purpose);
260:
261: /* returns a documentation string, or empty string */
262: xResponse = XMLRPC_CreateValueString(NULL, help ? help : xi_token_empty, 0);
263: }
264: }
265:
266: return xResponse;
267: }
268:
269: /*-**************************************
270: * End Introspection Callbacks (methods) *
271: ****************************************/
272:
273:
274: /*-************************
275: * Introspection Utilities *
276: **************************/
277:
278: /* performs registration of introspection methods */
279: void xi_register_system_methods(XMLRPC_SERVER server) {
280: XMLRPC_ServerRegisterMethod(server, xi_token_system_list_methods, xi_system_list_methods_cb);
281: XMLRPC_ServerRegisterMethod(server, xi_token_system_method_help, xi_system_method_help_cb);
282: XMLRPC_ServerRegisterMethod(server, xi_token_system_method_signature, xi_system_method_signature_cb);
283: XMLRPC_ServerRegisterMethod(server, xi_token_system_describe_methods, xi_system_describe_methods_cb);
284: }
285:
286: /* describe a value (param, return, type) */
287: static XMLRPC_VALUE describeValue_worker(const char* type, const char* id, const char* desc, int optional, const char* default_val, XMLRPC_VALUE sub_params) {
288: XMLRPC_VALUE xParam = NULL;
289: if(id || desc) {
290: xParam = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
291: XMLRPC_VectorAppendString(xParam, xi_token_name, id, 0);
292: XMLRPC_VectorAppendString(xParam, xi_token_type, type, 0);
293: XMLRPC_VectorAppendString(xParam, xi_token_description, desc, 0);
294: if(optional != 2) {
295: XMLRPC_VectorAppendInt(xParam, xi_token_optional, optional);
296: }
297: if(optional == 1 && default_val) {
298: XMLRPC_VectorAppendString(xParam, xi_token_default, default_val, 0);
299: }
300: XMLRPC_AddValueToVector(xParam, sub_params);
301: }
302: return xParam;
303: }
304:
305:
306: /* convert an xml tree conforming to spec <url tbd> to XMLRPC_VALUE
307: * suitable for use with XMLRPC_ServerAddIntrospectionData
308: */
309: XMLRPC_VALUE xml_element_to_method_description(xml_element* el, XMLRPC_ERROR err) {
310: XMLRPC_VALUE xReturn = NULL;
311:
312: if(el->name) {
313: const char* name = NULL;
314: const char* type = NULL;
315: const char* basetype = NULL;
316: const char* desc = NULL;
317: const char* def = NULL;
318: int optional = 0;
319: xml_element_attr* attr_iter = Q_Head(&el->attrs);
320:
321: /* grab element attributes up front to save redundant while loops */
322: while(attr_iter) {
323: if(!strcmp(attr_iter->key, "name")) {
324: name = attr_iter->val;
325: }
326: else if(!strcmp(attr_iter->key, "type")) {
327: type = attr_iter->val;
328: }
329: else if(!strcmp(attr_iter->key, "basetype")) {
330: basetype = attr_iter->val;
331: }
332: else if(!strcmp(attr_iter->key, "desc")) {
333: desc = attr_iter->val;
334: }
335: else if(!strcmp(attr_iter->key, "optional")) {
336: if(attr_iter->val && !strcmp(attr_iter->val, "yes")) {
337: optional = 1;
338: }
339: }
340: else if(!strcmp(attr_iter->key, "default")) {
341: def = attr_iter->val;
342: }
343: attr_iter = Q_Next(&el->attrs);
344: }
345:
346: /* value and typeDescription behave about the same */
347: if(!strcmp(el->name, "value") || !strcmp(el->name, "typeDescription")) {
348: XMLRPC_VALUE xSubList = NULL;
349: const char* ptype = !strcmp(el->name, "value") ? type : basetype;
350: if(ptype) {
351: if(Q_Size(&el->children) &&
352: (!strcmp(ptype, "array") || !strcmp(ptype, "struct") || !strcmp(ptype, "mixed"))) {
353: xSubList = XMLRPC_CreateVector("member", xmlrpc_vector_array);
354:
355: if(xSubList) {
356: xml_element* elem_iter = Q_Head(&el->children);
357: while(elem_iter) {
358: XMLRPC_AddValueToVector(xSubList,
359: xml_element_to_method_description(elem_iter, err));
360: elem_iter = Q_Next(&el->children);
361: }
362: }
363: }
364: xReturn = describeValue_worker(ptype, name, (desc ? desc : (xSubList ? NULL : el->text.str)), optional, def, xSubList);
365: }
366: }
367:
368: /* these three kids are about equivalent */
369: else if(!strcmp(el->name, "params") ||
370: !strcmp(el->name, "returns") ||
371: !strcmp(el->name, "signature")) {
372: if(Q_Size(&el->children)) {
373: xml_element* elem_iter = Q_Head(&el->children);
374: xReturn = XMLRPC_CreateVector(!strcmp(el->name, "signature") ? NULL : el->name, xmlrpc_vector_struct);
375:
376:
377: while(elem_iter) {
378: XMLRPC_AddValueToVector(xReturn,
379: xml_element_to_method_description(elem_iter, err));
380: elem_iter = Q_Next(&el->children);
381: }
382: }
383: }
384:
385:
386: else if(!strcmp(el->name, "methodDescription")) {
387: xml_element* elem_iter = Q_Head(&el->children);
388: xReturn = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
389:
390: XMLRPC_VectorAppendString(xReturn, xi_token_name, name, 0);
391:
392: while(elem_iter) {
393: XMLRPC_AddValueToVector(xReturn,
394: xml_element_to_method_description(elem_iter, err));
395: elem_iter = Q_Next(&el->children);
396: }
397: }
398:
399: /* items are slightly special */
400: else if(!strcmp(el->name, "item")) {
401: xReturn = XMLRPC_CreateValueString(name, el->text.str, el->text.len);
402: }
403:
404: /* sure. we'll let any ol element with children through */
405: else if(Q_Size(&el->children)) {
406: xml_element* elem_iter = Q_Head(&el->children);
407: xReturn = XMLRPC_CreateVector(el->name, xmlrpc_vector_mixed);
408:
409: while(elem_iter) {
410: XMLRPC_AddValueToVector(xReturn,
411: xml_element_to_method_description(elem_iter, err));
412: elem_iter = Q_Next(&el->children);
413: }
414: }
415:
416: /* or anything at all really, so long as its got some text.
417: * no reason being all snotty about a spec, right?
418: */
419: else if(el->name && el->text.len) {
420: xReturn = XMLRPC_CreateValueString(el->name, el->text.str, el->text.len);
421: }
422: }
423:
424: return xReturn;
425: }
426:
427: /*-****************************
428: * End Introspection Utilities *
429: ******************************/
430:
431:
432:
433: /*-******************
434: * Introspection API *
435: ********************/
436:
437:
438: /****f* VALUE/XMLRPC_IntrospectionCreateDescription
439: * NAME
440: * XMLRPC_IntrospectionCreateDescription
441: * SYNOPSIS
442: * XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err)
443: * FUNCTION
444: * converts raw xml describing types and methods into an
445: * XMLRPC_VALUE suitable for use with XMLRPC_ServerAddIntrospectionData()
446: * INPUTS
447: * xml - xml data conforming to introspection spec at <url tbd>
448: * err - optional pointer to error struct. filled in if error occurs and not NULL.
449: * RESULT
450: * XMLRPC_VALUE - newly created value, or NULL if fatal error.
451: * BUGS
452: * Currently does little or no validation of xml.
453: * Only parse errors are currently reported in err, not structural errors.
454: * SEE ALSO
455: * XMLRPC_ServerAddIntrospectionData ()
456: * SOURCE
457: */
458: XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) {
459: XMLRPC_VALUE xReturn = NULL;
460: xml_element* root = xml_elem_parse_buf(xml, 0, 0, err ? &err->xml_elem_error : NULL);
461:
462: if(root) {
463: xReturn = xml_element_to_method_description(root, err);
464:
465: xml_elem_free(root);
466: }
467:
468: return xReturn;
469:
470: }
471: /*******/
472:
473:
474: /****f* SERVER/XMLRPC_ServerAddIntrospectionData
475: * NAME
476: * XMLRPC_ServerAddIntrospectionData
477: * SYNOPSIS
478: * int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc)
479: * FUNCTION
480: * updates server with additional introspection data
481: * INPUTS
482: * server - target server
483: * desc - introspection data, should be a struct generated by
484: * XMLRPC_IntrospectionCreateDescription ()
485: * RESULT
486: * int - 1 if success, else 0
487: * NOTES
488: * - function will fail if neither typeList nor methodList key is present in struct.
489: * - if method or type already exists, it will be replaced.
490: * - desc is never freed by the server. caller is responsible for cleanup.
491: * BUGS
492: * - horribly slow lookups. prime candidate for hash improvements.
493: * - uglier and more complex than I like to see for API functions.
494: * SEE ALSO
495: * XMLRPC_ServerAddIntrospectionData ()
496: * XMLRPC_ServerRegisterIntrospectionCallback ()
497: * XMLRPC_CleanupValue ()
498: * SOURCE
499: */
500: int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) {
501: int bSuccess = 0;
502: if(server && desc) {
503: XMLRPC_VALUE xNewTypes = XMLRPC_VectorGetValueWithID(desc, "typeList");
504: XMLRPC_VALUE xNewMethods = XMLRPC_VectorGetValueWithID(desc, "methodList");
505: XMLRPC_VALUE xServerTypes = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList");
506:
507: if(xNewMethods) {
508: XMLRPC_VALUE xMethod = XMLRPC_VectorRewind(xNewMethods);
509:
510: while(xMethod) {
511: const char* name = XMLRPC_VectorGetStringWithID(xMethod, xi_token_name);
512: server_method* sm = find_method(server, name);
513:
514: if(sm) {
515: if(sm->desc) {
516: XMLRPC_CleanupValue(sm->desc);
517: }
518: sm->desc = XMLRPC_CopyValue(xMethod);
519: bSuccess = 1;
520: }
521:
522: xMethod = XMLRPC_VectorNext(xNewMethods);
523: }
524: }
525: if(xNewTypes) {
526: if(!xServerTypes) {
527: if(!server->xIntrospection) {
528: server->xIntrospection = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
529: }
530:
531: XMLRPC_AddValueToVector(server->xIntrospection, xNewTypes);
532: bSuccess = 1;
533: }
534: else {
535: XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xNewTypes);
536: while(xIter) {
537: /* get rid of old values */
538: XMLRPC_VALUE xPrev = find_named_value(xServerTypes, XMLRPC_VectorGetStringWithID(xIter, xi_token_name));
539: if(xPrev) {
540: XMLRPC_VectorRemoveValue(xServerTypes, xPrev);
541: }
542: XMLRPC_AddValueToVector(xServerTypes, xIter);
543: bSuccess = 1;
544: xIter = XMLRPC_VectorNext(xNewTypes);
545: }
546: }
547: }
548: }
549: return bSuccess;
550: }
551: /*******/
552:
553:
554: /****f* SERVER/XMLRPC_ServerRegisterIntrospectionCallback
555: * NAME
556: * XMLRPC_ServerRegisterIntrospectionCallback
557: * SYNOPSIS
558: * int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb)
559: * FUNCTION
560: * registers a callback for lazy generation of introspection data
561: * INPUTS
562: * server - target server
563: * cb - callback that will generate introspection data
564: * RESULT
565: * int - 1 if success, else 0
566: * NOTES
567: * parsing xml and generating introspection data is fairly expensive, thus a
568: * server may wish to wait until this data is actually requested before generating
569: * it. Any number of callbacks may be registered at any time. A given callback
570: * will only ever be called once, the first time an introspection request is
571: * processed after the time of callback registration.
572: * SEE ALSO
573: * XMLRPC_ServerAddIntrospectionData ()
574: * XMLRPC_IntrospectionCreateDescription ()
575: * SOURCE
576: */
577: int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) {
578: int bSuccess = 0;
579: if(server && cb) {
580:
581: doc_method* dm = calloc(1, sizeof(doc_method));
582:
583: if(dm) {
584: dm->method = cb;
585: dm->b_called = 0;
586:
587: if(Q_PushTail(&server->docslist, dm)) {
588: bSuccess = 1;
589: }
590: else {
591: my_free(dm);
592: }
593: }
594: }
595: return 0;
596: }
597: /*******/
598:
599: /*-**********************
600: * End Introspection API *
601: ************************/
602:
603:
604:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>