Back to index

php5  5.3.10
xmlrpc_introspection.c
Go to the documentation of this file.
00001 /*
00002   This file is part of libXMLRPC - a C library for xml-encoded function calls.
00003 
00004   Author: Dan Libby (dan@libby.com)
00005   Epinions.com may be contacted at feedback@epinions-inc.com
00006 */
00007 
00008 /*  
00009   Copyright 2001 Epinions, Inc. 
00010 
00011   Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
00012   of charge, to (a) use, copy, distribute, modify, perform and display this 
00013   software and associated documentation files (the "Software"), and (b) 
00014   permit others to whom the Software is furnished to do so as well.  
00015 
00016   1) The above copyright notice and this permission notice shall be included 
00017   without modification in all copies or substantial portions of the 
00018   Software.  
00019 
00020   2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
00021   ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
00022   IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
00023   PURPOSE OR NONINFRINGEMENT.  
00024 
00025   3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
00026   SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
00027   OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
00028   NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
00029   DAMAGES.    
00030 
00031 */
00032 
00033 
00034 /****h* ABOUT/xmlrpc_introspection
00035  * AUTHOR
00036  *   Dan Libby, aka danda  (dan@libby.com)
00037  * HISTORY
00038  *   $Log$
00039  *   Revision 1.4  2003/12/16 21:00:21  sniper
00040  *   Fix some compile warnings (patch by Joe Orton)
00041  *
00042  *   Revision 1.3  2002/07/05 04:43:53  danda
00043  *   merged in updates from SF project.  bring php repository up to date with xmlrpc-epi version 0.51
00044  *
00045  *   Revision 1.9  2001/09/29 21:58:05  danda
00046  *   adding cvs log to history section
00047  *
00048  *   4/10/2001 -- danda -- initial introspection support
00049  * TODO
00050  * NOTES
00051  *******/
00052 
00053 
00054 #ifdef _WIN32
00055 #include "xmlrpc_win32.h"
00056 #endif
00057 #include "queue.h"
00058 #include "xmlrpc.h"
00059 #include "xmlrpc_private.h"
00060 #include "xmlrpc_introspection_private.h"
00061 #include <string.h>
00062 #include <stdlib.h>
00063 #include <stdarg.h>
00064 
00065 
00066 /* forward declarations for static (non public, non api) funcs */
00067 static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
00068 static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
00069 static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
00070 static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
00071 
00072 
00073 /*-**********************************
00074 * Introspection Callbacks (methods) *
00075 ************************************/
00076 
00077 /* iterates through a list of structs and finds the one with key "name" matching
00078  * needle.  slow, would benefit from a struct key hash.
00079  */
00080 static inline XMLRPC_VALUE find_named_value(XMLRPC_VALUE list, const char* needle) {
00081    XMLRPC_VALUE xIter = XMLRPC_VectorRewind(list);
00082    while(xIter) {
00083       const char* name = XMLRPC_VectorGetStringWithID(xIter, xi_token_name);
00084       if(name && !strcmp(name, needle)) {
00085          return xIter;
00086       }
00087       xIter = XMLRPC_VectorNext(list);
00088    }
00089    return NULL;
00090 }
00091 
00092 
00093 /* iterates through docs callbacks and calls any that have not yet been called */
00094 static void check_docs_loaded(XMLRPC_SERVER server, void* userData) {
00095    if(server) {
00096       q_iter qi = Q_Iter_Head_F(&server->docslist);
00097       while( qi ) {
00098          doc_method* dm = Q_Iter_Get_F(qi);
00099          if(dm && !dm->b_called) {
00100             dm->method(server, userData);
00101             dm->b_called = 1;
00102          }
00103          qi = Q_Iter_Next_F(qi);
00104       }
00105    }
00106 }
00107 
00108 
00109 /* utility function for xi_system_describe_methods_cb */
00110 static inline void describe_method(XMLRPC_SERVER server, XMLRPC_VALUE vector, const char* method) {
00111    if(method) {
00112       server_method* sm = find_method(server, method);
00113       if(sm) {
00114          XMLRPC_AddValueToVector(vector, sm->desc);
00115       }
00116    }
00117 }
00118 
00119 
00120 
00121 /* system.describeMethods() callback */
00122 static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
00123    XMLRPC_VALUE xParams = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input));
00124    XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
00125    XMLRPC_VALUE xMethodList = XMLRPC_CreateVector("methodList", xmlrpc_vector_array);
00126    XMLRPC_VALUE xTypeList = NULL;
00127    int bAll = 1;
00128 
00129    /* lazy loading of introspection data */
00130    check_docs_loaded(server, userData);
00131 
00132    xTypeList = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList");
00133 
00134    XMLRPC_AddValueToVector(xResponse, xTypeList);
00135    XMLRPC_AddValueToVector(xResponse, xMethodList);
00136 
00137    /* check if we have any param */
00138    if(xParams) {
00139       /* check if string or vector (1 or n) */
00140       XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(xParams);
00141       if(type == xmlrpc_string) {
00142          /* just one.  spit it out. */
00143          describe_method(server, xMethodList, XMLRPC_GetValueString(xParams));
00144          bAll = 0;
00145       }
00146       else if(type == xmlrpc_vector) {
00147          /* multiple.  spit all out */
00148          XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xParams);
00149          while(xIter) {
00150             describe_method(server, xMethodList, XMLRPC_GetValueString(xIter));
00151             xIter = XMLRPC_VectorNext(xParams);
00152          }
00153          bAll = 0;
00154       }
00155    }
00156 
00157    /* otherwise, default to sending all methods */
00158    if(bAll) {
00159       q_iter qi = Q_Iter_Head_F(&server->methodlist);
00160       while( qi ) {
00161          server_method* sm = Q_Iter_Get_F(qi);
00162          if(sm) {
00163             XMLRPC_AddValueToVector(xMethodList, sm->desc);
00164          }
00165          qi = Q_Iter_Next_F(qi);
00166       }
00167    }
00168    
00169    return xResponse;
00170 }
00171 
00172 /* this complies with system.listMethods as defined at http://xmlrpc.usefulinc.com/doc/reserved.html */
00173 static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
00174    XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
00175 
00176    q_iter qi = Q_Iter_Head_F(&server->methodlist);
00177    while( qi ) {
00178       server_method* sm = Q_Iter_Get_F(qi);
00179       if(sm) {
00180          XMLRPC_VectorAppendString(xResponse, 0, sm->name, 0);
00181       }
00182       qi = Q_Iter_Next_F(qi);
00183    }
00184    return xResponse;
00185 }
00186 
00187 /* this complies with system.methodSignature as defined at 
00188  * http://xmlrpc.usefulinc.com/doc/sysmethodsig.html 
00189  */
00190 static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
00191    const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)));
00192    XMLRPC_VALUE xResponse = NULL;
00193 
00194    /* lazy loading of introspection data */
00195    check_docs_loaded(server, userData);
00196 
00197    if(method) {
00198       server_method* sm = find_method(server, method);
00199       if(sm && sm->desc) {
00200          XMLRPC_VALUE xTypesArray = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
00201          XMLRPC_VALUE xIter, xParams, xSig, xSigIter;
00202          const char* type;
00203 
00204          /* array of possible signatures.  */
00205          xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
00206 
00207          /* find first signature */
00208          xSig = XMLRPC_VectorGetValueWithID(sm->desc, xi_token_signatures);
00209          xSigIter = XMLRPC_VectorRewind( xSig );
00210 
00211          /* iterate through sigs */
00212          while(xSigIter) {
00213             /* first type is the return value */
00214             type = XMLRPC_VectorGetStringWithID(XMLRPC_VectorRewind(
00215                                                  XMLRPC_VectorGetValueWithID(xSigIter, xi_token_returns)), 
00216                                                 xi_token_type);
00217             XMLRPC_AddValueToVector(xTypesArray, 
00218                                     XMLRPC_CreateValueString(NULL, 
00219                                                              type ? type : type_to_str(xmlrpc_none, 0), 
00220                                     0));
00221 
00222             /* the rest are parameters */
00223             xParams = XMLRPC_VectorGetValueWithID(xSigIter, xi_token_params);
00224             xIter = XMLRPC_VectorRewind(xParams);
00225 
00226             /* iter through params, adding to types array */
00227             while(xIter) {
00228                XMLRPC_AddValueToVector(xTypesArray,
00229                                        XMLRPC_CreateValueString(NULL, 
00230                                                                 XMLRPC_VectorGetStringWithID(xIter, xi_token_type),
00231                                                                 0));
00232                xIter = XMLRPC_VectorNext(xParams);
00233             }
00234 
00235             /* add types for this signature */
00236             XMLRPC_AddValueToVector(xResponse, xTypesArray);
00237 
00238             xSigIter = XMLRPC_VectorNext( xSig );
00239          }
00240       }
00241    }
00242 
00243    return xResponse;
00244 }
00245 
00246 /* this complies with system.methodHelp as defined at 
00247  * http://xmlrpc.usefulinc.com/doc/sysmethhelp.html 
00248  */
00249 static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
00250    const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)));
00251    XMLRPC_VALUE xResponse = NULL;
00252 
00253    /* lazy loading of introspection data */
00254    check_docs_loaded(server, userData);
00255 
00256    if(method) {
00257       server_method* sm = find_method(server, method);
00258       if(sm && sm->desc) {
00259          const char* help = XMLRPC_VectorGetStringWithID(sm->desc, xi_token_purpose);
00260 
00261          /* returns a documentation string, or empty string */
00262          xResponse = XMLRPC_CreateValueString(NULL, help ? help : xi_token_empty, 0);
00263       }
00264    }
00265 
00266    return xResponse;
00267 }
00268 
00269 /*-**************************************
00270 * End Introspection Callbacks (methods) *
00271 ****************************************/
00272 
00273 
00274 /*-************************
00275 * Introspection Utilities *
00276 **************************/
00277 
00278 /* performs registration of introspection methods */
00279 void xi_register_system_methods(XMLRPC_SERVER server) {
00280    XMLRPC_ServerRegisterMethod(server, xi_token_system_list_methods, xi_system_list_methods_cb);
00281    XMLRPC_ServerRegisterMethod(server, xi_token_system_method_help, xi_system_method_help_cb);
00282    XMLRPC_ServerRegisterMethod(server, xi_token_system_method_signature, xi_system_method_signature_cb);
00283    XMLRPC_ServerRegisterMethod(server, xi_token_system_describe_methods, xi_system_describe_methods_cb);
00284 }
00285 
00286 /* describe a value (param, return, type) */
00287 static XMLRPC_VALUE describeValue_worker(const char* type, const char* id, const char* desc, int optional, const char* default_val, XMLRPC_VALUE sub_params) {
00288    XMLRPC_VALUE xParam = NULL;
00289    if(id || desc) {
00290       xParam = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
00291       XMLRPC_VectorAppendString(xParam, xi_token_name, id, 0);
00292       XMLRPC_VectorAppendString(xParam, xi_token_type, type, 0);
00293       XMLRPC_VectorAppendString(xParam, xi_token_description, desc, 0);
00294       if(optional != 2) {
00295          XMLRPC_VectorAppendInt(xParam, xi_token_optional, optional);
00296       }
00297       if(optional == 1 && default_val) {
00298          XMLRPC_VectorAppendString(xParam, xi_token_default, default_val, 0);
00299       }
00300       XMLRPC_AddValueToVector(xParam, sub_params);
00301    }
00302    return xParam;
00303 }
00304 
00305 
00306 /* convert an xml tree conforming to spec <url tbd> to  XMLRPC_VALUE
00307  * suitable for use with XMLRPC_ServerAddIntrospectionData
00308  */
00309 XMLRPC_VALUE xml_element_to_method_description(xml_element* el, XMLRPC_ERROR err) {
00310    XMLRPC_VALUE xReturn = NULL;
00311 
00312    if(el->name) {
00313       const char* name = NULL;
00314       const char* type = NULL;
00315       const char* basetype = NULL;
00316       const char* desc = NULL;
00317       const char* def = NULL;
00318       int optional = 0;
00319       xml_element_attr* attr_iter = Q_Head(&el->attrs);
00320 
00321       /* grab element attributes up front to save redundant while loops */
00322       while(attr_iter) {
00323          if(!strcmp(attr_iter->key, "name")) {
00324             name = attr_iter->val;
00325          }
00326          else if(!strcmp(attr_iter->key, "type")) {
00327             type = attr_iter->val;
00328          }
00329          else if(!strcmp(attr_iter->key, "basetype")) {
00330             basetype = attr_iter->val;
00331          }
00332          else if(!strcmp(attr_iter->key, "desc")) {
00333             desc = attr_iter->val;
00334          }
00335          else if(!strcmp(attr_iter->key, "optional")) {
00336             if(attr_iter->val && !strcmp(attr_iter->val, "yes")) {
00337                optional = 1;
00338             }
00339          }
00340          else if(!strcmp(attr_iter->key, "default")) {
00341             def = attr_iter->val;
00342          }
00343          attr_iter = Q_Next(&el->attrs);
00344       }
00345 
00346       /* value and typeDescription behave about the same */
00347       if(!strcmp(el->name, "value") || !strcmp(el->name, "typeDescription")) {
00348          XMLRPC_VALUE xSubList = NULL;
00349          const char* ptype = !strcmp(el->name, "value") ? type : basetype;
00350          if(ptype) {
00351             if(Q_Size(&el->children) &&
00352                (!strcmp(ptype, "array") || !strcmp(ptype, "struct") || !strcmp(ptype, "mixed"))) {
00353                xSubList = XMLRPC_CreateVector("member", xmlrpc_vector_array);
00354 
00355                if(xSubList) {
00356                   xml_element* elem_iter = Q_Head(&el->children);
00357                   while(elem_iter) {
00358                      XMLRPC_AddValueToVector(xSubList, 
00359                                              xml_element_to_method_description(elem_iter, err));
00360                      elem_iter = Q_Next(&el->children);
00361                   }
00362                }
00363             }
00364             xReturn = describeValue_worker(ptype, name, (desc ? desc : (xSubList ? NULL : el->text.str)), optional, def, xSubList);
00365          }
00366       }
00367 
00368       /* these three kids are about equivalent */
00369       else if(!strcmp(el->name, "params") || 
00370               !strcmp(el->name, "returns") || 
00371               !strcmp(el->name, "signature")) {
00372          if(Q_Size(&el->children)) {
00373             xml_element* elem_iter = Q_Head(&el->children);
00374             xReturn = XMLRPC_CreateVector(!strcmp(el->name, "signature") ? NULL : el->name, xmlrpc_vector_struct);
00375 
00376 
00377             while(elem_iter) {
00378                XMLRPC_AddValueToVector(xReturn, 
00379                                        xml_element_to_method_description(elem_iter, err));
00380                elem_iter = Q_Next(&el->children);
00381             }
00382          }
00383       }
00384 
00385 
00386       else if(!strcmp(el->name, "methodDescription")) {
00387          xml_element* elem_iter = Q_Head(&el->children);
00388          xReturn = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
00389 
00390          XMLRPC_VectorAppendString(xReturn, xi_token_name, name, 0);
00391 
00392          while(elem_iter) {
00393             XMLRPC_AddValueToVector(xReturn, 
00394                                     xml_element_to_method_description(elem_iter, err));
00395             elem_iter = Q_Next(&el->children);
00396          }
00397       }
00398 
00399       /* items are slightly special */
00400       else if(!strcmp(el->name, "item")) {
00401          xReturn = XMLRPC_CreateValueString(name, el->text.str, el->text.len);
00402       }
00403 
00404       /* sure.  we'll let any ol element with children through */
00405       else if(Q_Size(&el->children)) {
00406          xml_element* elem_iter = Q_Head(&el->children);
00407          xReturn = XMLRPC_CreateVector(el->name, xmlrpc_vector_mixed);
00408 
00409          while(elem_iter) {
00410             XMLRPC_AddValueToVector(xReturn, 
00411                                     xml_element_to_method_description(elem_iter, err));
00412             elem_iter = Q_Next(&el->children);
00413          }
00414       }
00415 
00416       /* or anything at all really, so long as its got some text. 
00417        * no reason being all snotty about a spec, right? 
00418        */
00419       else if(el->name && el->text.len) {
00420          xReturn = XMLRPC_CreateValueString(el->name, el->text.str, el->text.len);
00421       }
00422    }
00423 
00424    return xReturn;
00425 }
00426 
00427 /*-****************************
00428 * End Introspection Utilities *
00429 ******************************/
00430 
00431 
00432 
00433 /*-******************
00434 * Introspection API *
00435 ********************/
00436 
00437 
00438 /****f* VALUE/XMLRPC_IntrospectionCreateDescription
00439  * NAME
00440  *   XMLRPC_IntrospectionCreateDescription
00441  * SYNOPSIS
00442  *   XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err)
00443  * FUNCTION
00444  *   converts raw xml describing types and methods into an
00445  *   XMLRPC_VALUE suitable for use with XMLRPC_ServerAddIntrospectionData()
00446  * INPUTS
00447  *   xml - xml data conforming to introspection spec at <url tbd>
00448  *   err - optional pointer to error struct. filled in if error occurs and not NULL.
00449  * RESULT
00450  *   XMLRPC_VALUE - newly created value, or NULL if fatal error.
00451  * BUGS
00452  *   Currently does little or no validation of xml.
00453  *   Only parse errors are currently reported in err, not structural errors.
00454  * SEE ALSO
00455  *   XMLRPC_ServerAddIntrospectionData ()
00456  * SOURCE
00457  */
00458 XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) {
00459    XMLRPC_VALUE xReturn = NULL;
00460    xml_element* root = xml_elem_parse_buf(xml, 0, 0, err ? &err->xml_elem_error : NULL);
00461 
00462    if(root) {
00463       xReturn = xml_element_to_method_description(root, err);
00464 
00465       xml_elem_free(root);
00466    }
00467 
00468    return xReturn;
00469 
00470 }
00471 /*******/
00472 
00473 
00474 /****f* SERVER/XMLRPC_ServerAddIntrospectionData
00475  * NAME
00476  *   XMLRPC_ServerAddIntrospectionData
00477  * SYNOPSIS
00478  *   int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc)
00479  * FUNCTION
00480  *   updates server with additional introspection data
00481  * INPUTS
00482  *   server - target server
00483  *   desc - introspection data, should be a struct generated by 
00484  *          XMLRPC_IntrospectionCreateDescription ()
00485  * RESULT
00486  *   int - 1 if success, else 0
00487  * NOTES
00488  *  - function will fail if neither typeList nor methodList key is present in struct.
00489  *  - if method or type already exists, it will be replaced.
00490  *  - desc is never freed by the server.  caller is responsible for cleanup.
00491  * BUGS
00492  *   - horribly slow lookups. prime candidate for hash improvements.
00493  *   - uglier and more complex than I like to see for API functions.
00494  * SEE ALSO
00495  *   XMLRPC_ServerAddIntrospectionData ()
00496  *   XMLRPC_ServerRegisterIntrospectionCallback ()
00497  *   XMLRPC_CleanupValue ()
00498  * SOURCE
00499  */
00500 int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) {
00501    int bSuccess = 0;
00502    if(server && desc) {
00503       XMLRPC_VALUE xNewTypes = XMLRPC_VectorGetValueWithID(desc, "typeList");
00504       XMLRPC_VALUE xNewMethods = XMLRPC_VectorGetValueWithID(desc, "methodList");
00505       XMLRPC_VALUE xServerTypes = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList");
00506 
00507       if(xNewMethods) {
00508          XMLRPC_VALUE xMethod = XMLRPC_VectorRewind(xNewMethods);
00509 
00510          while(xMethod) {
00511             const char* name = XMLRPC_VectorGetStringWithID(xMethod, xi_token_name);
00512             server_method* sm = find_method(server, name);
00513 
00514             if(sm) {
00515                if(sm->desc) {
00516                   XMLRPC_CleanupValue(sm->desc);
00517                }
00518                sm->desc = XMLRPC_CopyValue(xMethod);
00519                bSuccess = 1;
00520             }
00521 
00522             xMethod = XMLRPC_VectorNext(xNewMethods);
00523          }
00524       }
00525       if(xNewTypes) {
00526          if(!xServerTypes) {
00527             if(!server->xIntrospection) {
00528                server->xIntrospection = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
00529             }
00530 
00531             XMLRPC_AddValueToVector(server->xIntrospection, xNewTypes);
00532             bSuccess = 1;
00533          }
00534          else {
00535             XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xNewTypes);
00536             while(xIter) {
00537                /* get rid of old values */
00538                XMLRPC_VALUE xPrev = find_named_value(xServerTypes, XMLRPC_VectorGetStringWithID(xIter, xi_token_name));
00539                if(xPrev) {
00540                   XMLRPC_VectorRemoveValue(xServerTypes, xPrev);
00541                }
00542                XMLRPC_AddValueToVector(xServerTypes, xIter);
00543                bSuccess = 1;
00544                xIter = XMLRPC_VectorNext(xNewTypes);
00545             }
00546          }
00547       }
00548    }
00549    return bSuccess;
00550 }
00551 /*******/
00552 
00553 
00554 /****f* SERVER/XMLRPC_ServerRegisterIntrospectionCallback
00555  * NAME
00556  *   XMLRPC_ServerRegisterIntrospectionCallback
00557  * SYNOPSIS
00558  *   int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb)
00559  * FUNCTION
00560  *   registers a callback for lazy generation of introspection data
00561  * INPUTS
00562  *   server - target server
00563  *   cb - callback that will generate introspection data
00564  * RESULT
00565  *   int - 1 if success, else 0
00566  * NOTES
00567  *   parsing xml and generating introspection data is fairly expensive, thus a
00568  *   server may wish to wait until this data is actually requested before generating
00569  *   it. Any number of callbacks may be registered at any time.  A given callback
00570  *   will only ever be called once, the first time an introspection request is
00571  *   processed after the time of callback registration.
00572  * SEE ALSO
00573  *   XMLRPC_ServerAddIntrospectionData ()
00574  *   XMLRPC_IntrospectionCreateDescription ()
00575  * SOURCE
00576  */
00577 int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) {
00578    int bSuccess = 0;
00579    if(server && cb) {
00580 
00581       doc_method* dm = calloc(1, sizeof(doc_method));
00582       
00583       if(dm) {
00584          dm->method = cb;
00585          dm->b_called = 0;
00586 
00587          if(Q_PushTail(&server->docslist, dm)) {
00588             bSuccess = 1;
00589          }
00590          else {
00591             my_free(dm);
00592          }
00593       }
00594    }
00595    return 0;
00596 }
00597 /*******/
00598 
00599 /*-**********************
00600 * End Introspection API *
00601 ************************/
00602 
00603 
00604