xmlrpc_introspection.c [plain text]
#ifdef _WIN32
#include "xmlrpc_win32.h"
#endif
#include "queue.h"
#include "xmlrpc.h"
#include "xmlrpc_private.h"
#include "xmlrpc_introspection_private.h"
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData);
static inline XMLRPC_VALUE find_named_value(XMLRPC_VALUE list, const char* needle) {
XMLRPC_VALUE xIter = XMLRPC_VectorRewind(list);
while(xIter) {
const char* name = XMLRPC_VectorGetStringWithID(xIter, xi_token_name);
if(name && !strcmp(name, needle)) {
return xIter;
}
xIter = XMLRPC_VectorNext(list);
}
return NULL;
}
static void check_docs_loaded(XMLRPC_SERVER server, void* userData) {
if(server) {
q_iter qi = Q_Iter_Head_F(&server->docslist);
while( qi ) {
doc_method* dm = Q_Iter_Get_F(qi);
if(dm && !dm->b_called) {
dm->method(server, userData);
dm->b_called = 1;
}
qi = Q_Iter_Next_F(qi);
}
}
}
static inline void describe_method(XMLRPC_SERVER server, XMLRPC_VALUE vector, const char* method) {
if(method) {
server_method* sm = find_method(server, method);
if(sm) {
XMLRPC_AddValueToVector(vector, sm->desc);
}
}
}
static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
XMLRPC_VALUE xParams = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input));
XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
XMLRPC_VALUE xMethodList = XMLRPC_CreateVector("methodList", xmlrpc_vector_array);
XMLRPC_VALUE xTypeList = NULL;
int bAll = 1;
check_docs_loaded(server, userData);
xTypeList = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList");
XMLRPC_AddValueToVector(xResponse, xTypeList);
XMLRPC_AddValueToVector(xResponse, xMethodList);
if(xParams) {
XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(xParams);
if(type == xmlrpc_string) {
describe_method(server, xMethodList, XMLRPC_GetValueString(xParams));
bAll = 0;
}
else if(type == xmlrpc_vector) {
XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xParams);
while(xIter) {
describe_method(server, xMethodList, XMLRPC_GetValueString(xIter));
xIter = XMLRPC_VectorNext(xParams);
}
bAll = 0;
}
}
if(bAll) {
q_iter qi = Q_Iter_Head_F(&server->methodlist);
while( qi ) {
server_method* sm = Q_Iter_Get_F(qi);
if(sm) {
XMLRPC_AddValueToVector(xMethodList, sm->desc);
}
qi = Q_Iter_Next_F(qi);
}
}
return xResponse;
}
static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
q_iter qi = Q_Iter_Head_F(&server->methodlist);
while( qi ) {
server_method* sm = Q_Iter_Get_F(qi);
if(sm) {
XMLRPC_VectorAppendString(xResponse, 0, sm->name, 0);
}
qi = Q_Iter_Next_F(qi);
}
return xResponse;
}
static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)));
XMLRPC_VALUE xResponse = NULL;
check_docs_loaded(server, userData);
if(method) {
server_method* sm = find_method(server, method);
if(sm && sm->desc) {
XMLRPC_VALUE xTypesArray = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
XMLRPC_VALUE xIter, xParams, xSig, xSigIter;
const char* type;
xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array);
xSig = XMLRPC_VectorGetValueWithID(sm->desc, xi_token_signatures);
xSigIter = XMLRPC_VectorRewind( xSig );
while(xSigIter) {
type = XMLRPC_VectorGetStringWithID(XMLRPC_VectorRewind(
XMLRPC_VectorGetValueWithID(xSigIter, xi_token_returns)),
xi_token_type);
XMLRPC_AddValueToVector(xTypesArray,
XMLRPC_CreateValueString(NULL,
type ? type : type_to_str(xmlrpc_none, 0),
0));
xParams = XMLRPC_VectorGetValueWithID(xSigIter, xi_token_params);
xIter = XMLRPC_VectorRewind(xParams);
while(xIter) {
XMLRPC_AddValueToVector(xTypesArray,
XMLRPC_CreateValueString(NULL,
XMLRPC_VectorGetStringWithID(xIter, xi_token_type),
0));
xIter = XMLRPC_VectorNext(xParams);
}
XMLRPC_AddValueToVector(xResponse, xTypesArray);
xSigIter = XMLRPC_VectorNext( xSig );
}
}
}
return xResponse;
}
static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) {
const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)));
XMLRPC_VALUE xResponse = NULL;
check_docs_loaded(server, userData);
if(method) {
server_method* sm = find_method(server, method);
if(sm && sm->desc) {
const char* help = XMLRPC_VectorGetStringWithID(sm->desc, xi_token_purpose);
xResponse = XMLRPC_CreateValueString(NULL, help ? help : xi_token_empty, 0);
}
}
return xResponse;
}
void xi_register_system_methods(XMLRPC_SERVER server) {
XMLRPC_ServerRegisterMethod(server, xi_token_system_list_methods, xi_system_list_methods_cb);
XMLRPC_ServerRegisterMethod(server, xi_token_system_method_help, xi_system_method_help_cb);
XMLRPC_ServerRegisterMethod(server, xi_token_system_method_signature, xi_system_method_signature_cb);
XMLRPC_ServerRegisterMethod(server, xi_token_system_describe_methods, xi_system_describe_methods_cb);
}
static XMLRPC_VALUE describeValue_worker(const char* type, const char* id, const char* desc, int optional, const char* default_val, XMLRPC_VALUE sub_params) {
XMLRPC_VALUE xParam = NULL;
if(id || desc) {
xParam = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
XMLRPC_VectorAppendString(xParam, xi_token_name, id, 0);
XMLRPC_VectorAppendString(xParam, xi_token_type, type, 0);
XMLRPC_VectorAppendString(xParam, xi_token_description, desc, 0);
if(optional != 2) {
XMLRPC_VectorAppendInt(xParam, xi_token_optional, optional);
}
if(optional == 1 && default_val) {
XMLRPC_VectorAppendString(xParam, xi_token_default, default_val, 0);
}
XMLRPC_AddValueToVector(xParam, sub_params);
}
return xParam;
}
XMLRPC_VALUE xml_element_to_method_description(xml_element* el, XMLRPC_ERROR err) {
XMLRPC_VALUE xReturn = NULL;
if(el->name) {
const char* name = NULL;
const char* type = NULL;
const char* basetype = NULL;
const char* desc = NULL;
const char* def = NULL;
int optional = 0;
xml_element_attr* attr_iter = Q_Head(&el->attrs);
while(attr_iter) {
if(!strcmp(attr_iter->key, "name")) {
name = attr_iter->val;
}
else if(!strcmp(attr_iter->key, "type")) {
type = attr_iter->val;
}
else if(!strcmp(attr_iter->key, "basetype")) {
basetype = attr_iter->val;
}
else if(!strcmp(attr_iter->key, "desc")) {
desc = attr_iter->val;
}
else if(!strcmp(attr_iter->key, "optional")) {
if(attr_iter->val && !strcmp(attr_iter->val, "yes")) {
optional = 1;
}
}
else if(!strcmp(attr_iter->key, "default")) {
def = attr_iter->val;
}
attr_iter = Q_Next(&el->attrs);
}
if(!strcmp(el->name, "value") || !strcmp(el->name, "typeDescription")) {
XMLRPC_VALUE xSubList = NULL;
const char* ptype = !strcmp(el->name, "value") ? type : basetype;
if(ptype) {
if(Q_Size(&el->children) &&
(!strcmp(ptype, "array") || !strcmp(ptype, "struct") || !strcmp(ptype, "mixed"))) {
xSubList = XMLRPC_CreateVector("member", xmlrpc_vector_array);
if(xSubList) {
xml_element* elem_iter = Q_Head(&el->children);
while(elem_iter) {
XMLRPC_AddValueToVector(xSubList,
xml_element_to_method_description(elem_iter, err));
elem_iter = Q_Next(&el->children);
}
}
}
xReturn = describeValue_worker(ptype, name, (desc ? desc : (xSubList ? NULL : el->text.str)), optional, def, xSubList);
}
}
else if(!strcmp(el->name, "params") ||
!strcmp(el->name, "returns") ||
!strcmp(el->name, "signature")) {
if(Q_Size(&el->children)) {
xml_element* elem_iter = Q_Head(&el->children);
xReturn = XMLRPC_CreateVector(!strcmp(el->name, "signature") ? NULL : el->name, xmlrpc_vector_struct);
while(elem_iter) {
XMLRPC_AddValueToVector(xReturn,
xml_element_to_method_description(elem_iter, err));
elem_iter = Q_Next(&el->children);
}
}
}
else if(!strcmp(el->name, "methodDescription")) {
xml_element* elem_iter = Q_Head(&el->children);
xReturn = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
XMLRPC_VectorAppendString(xReturn, xi_token_name, name, 0);
while(elem_iter) {
XMLRPC_AddValueToVector(xReturn,
xml_element_to_method_description(elem_iter, err));
elem_iter = Q_Next(&el->children);
}
}
else if(!strcmp(el->name, "item")) {
xReturn = XMLRPC_CreateValueString(name, el->text.str, el->text.len);
}
else if(Q_Size(&el->children)) {
xml_element* elem_iter = Q_Head(&el->children);
xReturn = XMLRPC_CreateVector(el->name, xmlrpc_vector_mixed);
while(elem_iter) {
XMLRPC_AddValueToVector(xReturn,
xml_element_to_method_description(elem_iter, err));
elem_iter = Q_Next(&el->children);
}
}
else if(el->name && el->text.len) {
xReturn = XMLRPC_CreateValueString(el->name, el->text.str, el->text.len);
}
}
return xReturn;
}
XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) {
XMLRPC_VALUE xReturn = NULL;
xml_element* root = xml_elem_parse_buf(xml, 0, 0, err ? &err->xml_elem_error : NULL);
if(root) {
xReturn = xml_element_to_method_description(root, err);
xml_elem_free(root);
}
return xReturn;
}
int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) {
int bSuccess = 0;
if(server && desc) {
XMLRPC_VALUE xNewTypes = XMLRPC_VectorGetValueWithID(desc, "typeList");
XMLRPC_VALUE xNewMethods = XMLRPC_VectorGetValueWithID(desc, "methodList");
XMLRPC_VALUE xServerTypes = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList");
if(xNewMethods) {
XMLRPC_VALUE xMethod = XMLRPC_VectorRewind(xNewMethods);
while(xMethod) {
const char* name = XMLRPC_VectorGetStringWithID(xMethod, xi_token_name);
server_method* sm = find_method(server, name);
if(sm) {
if(sm->desc) {
XMLRPC_CleanupValue(sm->desc);
}
sm->desc = XMLRPC_CopyValue(xMethod);
bSuccess = 1;
}
xMethod = XMLRPC_VectorNext(xNewMethods);
}
}
if(xNewTypes) {
if(!xServerTypes) {
if(!server->xIntrospection) {
server->xIntrospection = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
}
XMLRPC_AddValueToVector(server->xIntrospection, xNewTypes);
bSuccess = 1;
}
else {
XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xNewTypes);
while(xIter) {
XMLRPC_VALUE xPrev = find_named_value(xServerTypes, XMLRPC_VectorGetStringWithID(xIter, xi_token_name));
if(xPrev) {
XMLRPC_VectorRemoveValue(xServerTypes, xPrev);
}
XMLRPC_AddValueToVector(xServerTypes, xIter);
bSuccess = 1;
xIter = XMLRPC_VectorNext(xNewTypes);
}
}
}
}
return bSuccess;
}
int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) {
int bSuccess = 0;
if(server && cb) {
doc_method* dm = calloc(1, sizeof(doc_method));
if(dm) {
dm->method = cb;
dm->b_called = 0;
if(Q_PushTail(&server->docslist, dm)) {
bSuccess = 1;
}
else {
my_free(dm);
}
}
}
return 0;
}