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