/* ==================================================================== * The Kannel Software License, Version 1.0 * * Copyright (c) 2001-2005 Kannel Group * Copyright (c) 1998-2001 WapIT Ltd. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Kannel Group (http://www.kannel.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Kannel" and "Kannel Group" must not be used to * endorse or promote products derived from this software without * prior written permission. For written permission, please * contact org@kannel.org. * * 5. Products derived from this software may not be called "Kannel", * nor may "Kannel" appear in their name, without prior written * permission of the Kannel Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE KANNEL GROUP OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Kannel Group. For more information on * the Kannel Group, please see . * * Portions of this software are based upon software originally written at * WapIT Ltd., Helsinki, Finland for the Kannel project. */ /* * gwlib/xmlrpc.c: functions to handle XML-RPC structure - building and parsing * * XML-RPC is HTTP-based XML defination to handle remote procedure calls, * and is defined at http://www.xml-rpc.org * * Kalle Marjola 2001 for project Kannel * Stipe Tolj * Robert Gaach */ #include #include #include #include #include "gwlib/gwlib.h" #include "gwlib/xmlrpc.h" #define XR_ENABLE_EMPTY_STRING_VALUES #define OPTIMAL_STRUCT_SIZE 7 typedef struct xmlrpc_methodresponse XMLRPCMethodResponse; typedef struct xmlrpc_member XMLRPCMember; typedef struct xmlrpc_methodcall XMLRPCMethodCall; typedef struct xmlrpc_fault XMLRPCFault; typedef struct xmlrpc_table_t xmlrpc_table_t; typedef struct xmlrpc_2table_t xmlrpc_2table_t; struct xmlrpc_methodcall { Octstr *method_name; List *params; /* List of XMLRPCValues */ }; struct xmlrpc_methodresponse { XMLRPCValue *param; /* Param value */ XMLRPCFault *fault; /* ..or this */ }; struct xmlrpc_fault { long f_code; /* Fault code */ Octstr *f_string; /* and description */ }; struct xmlrpc_document { int d_type; /* enum here */ int parse_status; /* enum here */ Octstr *parse_error; /* error string in case of parsing error */ XMLRPCMethodCall *methodcall; XMLRPCMethodResponse *methodresponse; }; struct xmlrpc_value { int v_type; /* enum here */ XMLRPCScalar *v_scalar; List *v_array; /* List of XMLRPCValues */ Dict *v_struct; /* Dict of XMLRPCValues */ }; struct xmlrpc_member { /* member of struct */ Octstr *name; XMLRPCValue *value; }; struct xmlrpc_scalar { int s_type; /* enum here */ Octstr *s_str; long s_int; int s_bool; double s_double; Octstr *s_date; Octstr *s_base64; }; struct xmlrpc_table_t { char *name; }; struct xmlrpc_2table_t { char *name; int s_type; /* enum here */ }; static xmlrpc_table_t methodcall_elements[] = { { "METHODNAME" }, { "PARAMS" } }; static xmlrpc_table_t methodresponse_elements[] = { { "FAULT" }, { "PARAMS" } }; static xmlrpc_table_t params_elements[] = { { "PARAM" } }; static xmlrpc_table_t param_elements[] = { { "VALUE" } }; static xmlrpc_2table_t value_elements[] = { { "I4", xr_int }, { "INT", xr_int }, { "BOOLEAN", xr_bool }, { "STRING", xr_string }, { "DOUBLE", xr_double }, { "DATETIME.ISO8601", xr_date }, { "BASE64", xr_base64 }, { "STRUCT", xr_struct }, { "ARRAY", xr_array } }; static xmlrpc_table_t struct_elements[] = { { "MEMBER" } }; static xmlrpc_table_t member_elements[] = { { "NAME" }, { "VALUE" } }; static xmlrpc_table_t array_elements[] = { { "DATA" } }; static xmlrpc_table_t data_elements[] = { { "VALUE" } }; static xmlrpc_table_t fault_elements[] = { { "VALUE" } }; #define NUMBER_OF_METHODCALL_ELEMENTS \ sizeof(methodcall_elements)/sizeof(methodcall_elements[0]) #define NUMBER_OF_METHODRESPONSE_ELEMENTS \ sizeof(methodresponse_elements)/sizeof(methodresponse_elements[0]) #define NUMBER_OF_PARAMS_ELEMENTS \ sizeof(params_elements)/sizeof(params_elements[0]) #define NUMBER_OF_PARAM_ELEMENTS \ sizeof(param_elements)/sizeof(param_elements[0]) #define NUMBER_OF_VALUE_ELEMENTS \ sizeof(value_elements)/sizeof(value_elements[0]) #define NUMBER_OF_STRUCT_ELEMENTS \ sizeof(struct_elements)/sizeof(struct_elements[0]) #define NUMBER_OF_MEMBER_ELEMENTS \ sizeof(member_elements)/sizeof(member_elements[0]) #define NUMBER_OF_ARRAY_ELEMENTS \ sizeof(array_elements)/sizeof(array_elements[0]) #define NUMBER_OF_DATA_ELEMENTS \ sizeof(data_elements)/sizeof(data_elements[0]) #define NUMBER_OF_FAULT_ELEMENTS \ sizeof(fault_elements)/sizeof(fault_elements[0]) /* -------------------------------------- * internal parser function declarations */ static int parse_document(xmlDocPtr document, XMLRPCDocument *xrdoc); static int parse_methodcall(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMethodCall *methodcall); static int parse_methodcall_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMethodCall *methodcall); static int parse_methodresponse(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMethodResponse *methodresponse, int* n); static int parse_methodresponse_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMethodResponse *methodresponse); static int parse_params(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *params); static int parse_params_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *params); static int parse_param(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *params, int *n); static int parse_param_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *params); static int parse_value(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCValue *value); static int parse_value_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCValue *xrvalue); static int parse_struct(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, Dict *members); static int parse_struct_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, Dict *members); static int parse_member(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMember *member); static int parse_member_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMember *member); static int parse_array(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *elements); static int parse_array_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *elements); static int parse_data(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *elements); static int parse_data_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *elements); static int parse_fault(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCFault *fault); static int parse_fault_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCFault *fault); /*------------------------------------- * MethodCall */ static XMLRPCMethodCall *xmlrpc_call_create(Octstr *name) { XMLRPCMethodCall *nmsg = gw_malloc(sizeof(XMLRPCMethodCall)); nmsg->method_name = octstr_duplicate(name); nmsg->params = gwlist_create(); return nmsg; } static void xmlrpc_call_destroy(XMLRPCMethodCall *call) { if (call == NULL) return; octstr_destroy(call->method_name); gwlist_destroy(call->params, xmlrpc_value_destroy_item); gw_free(call); } static Octstr *xmlrpc_call_get_name(XMLRPCMethodCall *call) { return call->method_name; } static int xmlrpc_call_add_param(XMLRPCMethodCall *method, XMLRPCValue *value) { if (method == NULL || value == NULL) return -1; gwlist_produce(method->params, value); return 0; } static Octstr *xmlrpc_call_print(XMLRPCMethodCall *call, int level) { Octstr *body, *os_value; XMLRPCValue *val; long i; if (call == NULL || call->method_name == NULL) return NULL; body = octstr_format("%*s\n" "%*s%S\n", level, "", level + 2, "", call->method_name); gwlist_lock(call->params); if (gwlist_len(call->params) > 0) { octstr_format_append(body, "%*s\n", level + 2, ""); for (i = 0; i < gwlist_len(call->params); i++) { val = gwlist_get(call->params, i); os_value = xmlrpc_value_print(val, level + 6); if (os_value == NULL) { error(0, "XMLRPC: Could not print method call, param %ld malformed", i); octstr_destroy(body); return NULL; } octstr_format_append(body, "%*s\n%S%*s\n", level + 4, "", os_value, level + 4, ""); octstr_destroy(os_value); } octstr_format_append(body, "%*s\n", level + 2, ""); } gwlist_unlock(call->params); octstr_format_append(body, "%*s\n", level, ""); return body; } /*------------------------------------- * XMLRPCFault */ static XMLRPCFault *xmlrpc_fault_create(long fcode, Octstr *fstring) { XMLRPCFault *fault = gw_malloc(sizeof(XMLRPCFault)); fault->f_code = fcode; fault->f_string = octstr_duplicate(fstring); return fault; } static void xmlrpc_fault_destroy(XMLRPCFault *fault) { if (fault == NULL) return; octstr_destroy(fault->f_string); gw_free(fault); } static long xmlrpc_fault_get_code(XMLRPCFault *fault) { if (fault == NULL) return -1; return fault->f_code; } static Octstr *xmlrpc_fault_get_string(XMLRPCFault *fault) { if (fault == NULL) return NULL; return fault->f_string; } static Octstr *xmlrpc_fault_print(XMLRPCFault *fault, int level) { Octstr *os; if (fault == NULL) return NULL; os = octstr_format("%*s\n%*s\n" "%*s\n" "%*s\n" "%*sfaultCode\n" "%*s%ld\n" "%*s\n" "%*s\n" "%*sfaultString\n" "%*s%S\n" "%*s\n" "%*s\n" "%*s\n%*s\n", level, "", level+2, "", level+4, "", level+6, "", level+8, "", level+8, "", fault->f_code, level+6, "", level+6, "", level+8, "", level+8, "", (fault->f_string == NULL ? octstr_imm("/") : fault->f_string), level+6, "", level+4, "", level+2, "", level, ""); return os; } /*------------------------------------- * MethodResponse */ static XMLRPCMethodResponse *xmlrpc_response_create(void) { XMLRPCMethodResponse *nmsg = gw_malloc(sizeof(XMLRPCMethodResponse)); nmsg->param = NULL; nmsg->fault = NULL; return nmsg; } static void xmlrpc_response_destroy(XMLRPCMethodResponse *response) { if (response == NULL) return; xmlrpc_value_destroy(response->param); xmlrpc_fault_destroy(response->fault); gw_free(response); } static int xmlrpc_response_add_param(XMLRPCMethodResponse *response, XMLRPCValue *value) { if (response == NULL || value == NULL) return -1; if (response->param != NULL) { error(0, "XMLRPC: Method Response may contain only one param."); return -1; } if (response->fault != NULL) { error(0, "XMLRPC: Fault Response may not contain any param."); return -1; } response->param = value; return 0; } static int xmlrpc_response_is_fault(XMLRPCMethodResponse *response) { if (response == NULL || response->fault == NULL) return 0; return 1; } static long xmlrpc_response_get_faultcode(XMLRPCMethodResponse *faultresponse) { if (! xmlrpc_response_is_fault(faultresponse)) { error(0, "XMLRPC response is not fault response."); return -1; } return xmlrpc_fault_get_code(faultresponse->fault); } static Octstr *xmlrpc_response_get_faultstring(XMLRPCMethodResponse *faultresponse) { if (! xmlrpc_response_is_fault(faultresponse)) { error(0, "XMLRPC response is not fault response."); return NULL; } return xmlrpc_fault_get_string(faultresponse->fault); } static Octstr *xmlrpc_response_print(XMLRPCMethodResponse *response, int level) { Octstr *body = NULL, *os_value = NULL; if (response->fault == NULL && response->param != NULL) { os_value = xmlrpc_value_print(response->param, level + 6); body = octstr_format("%*s\n" "%*s\n%*s\n" "%S" "%*s\n%*s\n" "%*s\n", level, "", level+2, "", level+4, "", os_value, level+4, "", level+2, "", level, ""); } else if (response->fault != NULL && response->param == NULL) { os_value = xmlrpc_fault_print(response->fault, level + 2); body = octstr_format("%*s\n" "%S" "%*s\n", level, "", os_value, level, ""); } octstr_destroy(os_value); return body; } /*------------------------------------- * Document */ XMLRPCDocument *xmlrpc_doc_create(void) { XMLRPCDocument *xrdoc = gw_malloc(sizeof(XMLRPCDocument)); xrdoc->d_type = xr_undefined; xrdoc->parse_status = XMLRPC_COMPILE_OK; xrdoc->parse_error = NULL; xrdoc->methodcall = NULL; xrdoc->methodresponse = NULL; return xrdoc; } XMLRPCDocument *xmlrpc_doc_create_call(Octstr *name) { XMLRPCDocument *xrdoc; xrdoc = xmlrpc_doc_create(); xrdoc->d_type = xr_methodcall; xrdoc->methodcall = xmlrpc_call_create(name); return xrdoc; } XMLRPCDocument *xmlrpc_doc_create_response(void) { XMLRPCDocument *xrdoc; xrdoc = xmlrpc_doc_create(); xrdoc->d_type = xr_methodresponse; xrdoc->methodresponse = xmlrpc_response_create(); return xrdoc; } XMLRPCDocument *xmlrpc_doc_create_faultresponse(long faultcode, Octstr *faultstring) { XMLRPCDocument *xrdoc; XMLRPCMethodResponse *response; xrdoc = xmlrpc_doc_create_response(); response = xrdoc->methodresponse; response->fault = xmlrpc_fault_create(faultcode, faultstring); return xrdoc; } XMLRPCDocument *xmlrpc_doc_parse(Octstr *post_body, int d_type) { XMLRPCDocument *xrdoc = xmlrpc_doc_create(); xmlDocPtr pDoc; size_t size; char *body; if (post_body == NULL) { xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_create("XMLRPC: (null) XML document given."); return xrdoc; } xrdoc->d_type = d_type; octstr_strip_blanks(post_body); octstr_shrink_blanks(post_body); size = octstr_len(post_body); body = octstr_get_cstr(post_body); /* parse XML document to a XML tree */ pDoc = xmlParseMemory(body, size); if (!pDoc) { xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_create("XMLRPC: not valid XML document given."); return xrdoc; } parse_document(pDoc, xrdoc); xmlFreeDoc(pDoc); return xrdoc; } /* Destroy XMLRPCDocument object */ void xmlrpc_doc_destroy(XMLRPCDocument *xrdoc, int d_type) { if (xrdoc == NULL) return; if (xrdoc->d_type != d_type) warning(0, "Destroying document with different type then given."); xmlrpc_call_destroy(xrdoc->methodcall); xmlrpc_response_destroy(xrdoc->methodresponse); octstr_destroy(xrdoc->parse_error); gw_free(xrdoc); } /* Add given param to XMLRPCDocument object. * Return 0 if ok or -1 if something wrong (e.g. xrdoc is null or faultresponse) */ int xmlrpc_doc_add_value(XMLRPCDocument *xrdoc, int d_type, XMLRPCValue *value) { if (xrdoc == NULL) return -1; if (xrdoc->d_type != d_type && d_type != xr_undefined) { error(0, "Wrong xmlrpc document type. Param not added."); return -1; } if (xrdoc->d_type == xr_methodresponse) { if (xmlrpc_response_add_param(xrdoc->methodresponse, value) < 0) return -1; } else if (xrdoc->d_type == xr_methodcall) { if (xmlrpc_call_add_param(xrdoc->methodcall, value) < 0) return -1; } else { error(0, "Unknown xmlrpc document type. Param not added."); return -1; } return 0; } /* Add a scalar param to MethodCall/MethodResponse. * Return 0 if ok or -1 if something wrong (e.g. xrdoc is null or faultresponse) */ int xmlrpc_doc_add_scalar(XMLRPCDocument *xrdoc, int d_type, int type, void *arg) { XMLRPCValue *param; param = xmlrpc_create_scalar_value(type, arg); if (xmlrpc_doc_add_value(xrdoc, d_type, param) < 0) { xmlrpc_value_destroy(param); return -1; } return 0; } /* Create Octstr (text/xml string) out of given XMLRPCDocument. Caller * must free returned Octstr */ Octstr *xmlrpc_doc_print(XMLRPCDocument *xrdoc, int d_type, int level) { Octstr *body = NULL, *pref = NULL; if (xrdoc == NULL) return NULL; if (xrdoc->d_type != d_type) { error(0, "Wrong xmlrpc document type."); return NULL; } if (xrdoc->d_type == xr_methodresponse) { body = xmlrpc_response_print(xrdoc->methodresponse, level); } else if (xrdoc->d_type == xr_methodcall) { body = xmlrpc_call_print(xrdoc->methodcall, level); } else { error(0, "Unknown xmlrpc document type."); } if (body != NULL) { pref = octstr_format("%*s\n", level, ""); octstr_insert(body, pref, 0); octstr_destroy(pref); } return body; } /* Send XMLRPCDocument to given URL with given Headers. * Return 0 if all went fine, -1 if failure. As user reference, uses *void */ int xmlrpc_doc_send(XMLRPCDocument *xrdoc, int d_type, HTTPCaller *http_ref, Octstr *url, List *headers, void *ref) { Octstr *body; if (http_ref == NULL || xrdoc == NULL) return -1; if (xrdoc->d_type != d_type) { error(0, "Wrong xmlrpc document type."); return -1; } if (headers == NULL) headers = gwlist_create(); http_header_remove_all(headers, "Content-Type"); http_header_add(headers, "Content-Type", "text/xml"); /* * XML-RPC specs say we at least need Host and User-Agent * HTTP headers to be defined. * These are set anyway within gwlib/http.c:build_request() */ body = xmlrpc_doc_print(xrdoc, d_type, 0); http_start_request(http_ref, HTTP_METHOD_POST, url, headers, body, 0, ref, NULL); octstr_destroy(body); /* XXX: should headers be destroyed here? */ /*http_destroy_headers(headers); */ return 0; } /*------------------------------------- * XMLRPCValue */ /* Create new value. Set type of it to xr_undefined, so it must be * set laterwards to correct one */ XMLRPCValue *xmlrpc_value_create(void) { XMLRPCValue *val = gw_malloc(sizeof(XMLRPCValue)); val->v_type = xr_undefined; val->v_scalar = NULL; val->v_array = NULL; val->v_struct = NULL; return val; } /* Destroy value with its information, recursively if need to */ void xmlrpc_value_destroy(XMLRPCValue *val) { if (val == NULL) return; switch(val->v_type) { case xr_scalar: xmlrpc_scalar_destroy(val->v_scalar); break; case xr_array: gwlist_destroy(val->v_array, xmlrpc_value_destroy_item); break; case xr_struct: dict_destroy(val->v_struct); break; } gw_free(val); } /* wrapper to destroy to be used with list */ void xmlrpc_value_destroy_item(void *val) { xmlrpc_value_destroy(val); } int xmlrpc_value_set_type(XMLRPCValue *val, int v_type) { if (val == NULL) return -1; switch(v_type) { case xr_scalar: case xr_array: case xr_struct: val->v_type = v_type; break; default: error(0, "XMLRPC: value type not supported."); return -1; } return 0; } int xmlrpc_value_set_content(XMLRPCValue *val, void *content) { if (val == NULL) return -1; switch(val->v_type) { case xr_scalar: val->v_scalar = (XMLRPCScalar *)content; break; case xr_array: val->v_array = (List *)content; break; case xr_struct: val->v_struct = (Dict *)content; break; default: error(0, "XMLRPC: value type not supported."); return -1; } return 0; } int xmlrpc_value_get_type(XMLRPCValue *val) { if (val == NULL) return -1; return val->v_type; } int xmlrpc_value_get_type_smart(XMLRPCValue *val) { int type = xmlrpc_value_get_type(val); if (type == xr_scalar) return xmlrpc_get_scalar_value_type(val); return type; } void *xmlrpc_value_get_content(XMLRPCValue *val) { if (val == NULL) return NULL; switch(val->v_type) { case xr_scalar: return val->v_scalar; case xr_array: return val->v_array; case xr_struct: return val->v_struct; default: error(0, "XMLRPC: value type not supported."); return NULL; } } Octstr *xmlrpc_value_print(XMLRPCValue *val, int level) { Octstr *body = NULL, *os = NULL; if (val == NULL) return NULL; switch(val->v_type) { case xr_scalar: os = xmlrpc_scalar_print(val->v_scalar, level+2); break; case xr_struct: os = xmlrpc_print_struct(val->v_struct, level+2); break; case xr_array: os = xmlrpc_print_array(val->v_array, level+2); break; default: return NULL; } if (os != NULL) { body = octstr_format("%*s\n%S%*s\n", level, "", os, level, ""); octstr_destroy(os); } return body; } /*------------------------------------- * XMLRPCScalar */ /* Create new scalar of given type with given argument */ XMLRPCScalar *xmlrpc_scalar_create(int type, void *arg) { XMLRPCScalar *scalar = gw_malloc(sizeof(XMLRPCScalar)); scalar->s_type = type; scalar->s_int = 0; scalar->s_bool = 0; scalar->s_double = 0.0; scalar->s_str = NULL; scalar->s_date = NULL; scalar->s_base64 = NULL; if (arg == NULL) { #ifdef XR_ENABLE_EMPTY_STRING_VALUES if (scalar->s_type != xr_string) { #endif error(0,"XML-RPC: scalar value may not be null!"); xmlrpc_scalar_destroy(scalar); return NULL; #ifdef XR_ENABLE_EMPTY_STRING_VALUES } #endif } switch (type) { case xr_int: if (arg != NULL) scalar->s_int = *(long*)arg; break; case xr_bool: if (arg != NULL) scalar->s_bool = *(int*)arg; break; case xr_double: if (arg != NULL) scalar->s_double = *(double*)arg; break; case xr_string: scalar->s_str = octstr_duplicate((Octstr *)arg); break; case xr_date: scalar->s_date = octstr_duplicate((Octstr *)arg); break; case xr_base64: scalar->s_base64 = octstr_duplicate((Octstr *)arg); break; default: error(0,"XML-RPC: scalar type not supported!"); xmlrpc_scalar_destroy(scalar); return NULL; } return scalar; } /* Destroy scalar */ void xmlrpc_scalar_destroy(XMLRPCScalar *scalar) { if (scalar == NULL) return; octstr_destroy(scalar->s_str); octstr_destroy(scalar->s_date); octstr_destroy(scalar->s_base64); gw_free(scalar); } int xmlrpc_scalar_get_type(XMLRPCScalar *scalar) { if (scalar == NULL) return -1; return scalar->s_type; } void *xmlrpc_scalar_get_content(XMLRPCScalar *scalar, int s_type) { if (scalar == NULL) return NULL; if (scalar->s_type != s_type) { error(0, "XMLRPC: Scalar content request with bogus type"); return NULL; } switch (scalar->s_type) { case xr_int: return &(scalar->s_int); case xr_bool: return &(scalar->s_bool); case xr_double: return &(scalar->s_double); case xr_string: return scalar->s_str; case xr_date: return scalar->s_date; case xr_base64: return scalar->s_base64; default: error(0,"XML-RPC: scalar type not supported!"); return NULL; } } Octstr *xmlrpc_scalar_print(XMLRPCScalar *scalar, int level) { Octstr *os = NULL; if (scalar == NULL) return NULL; switch (scalar->s_type) { case xr_int: os = octstr_format("%*s%ld\n", level, "", scalar->s_int); break; case xr_bool: os = octstr_format("%*s%d\n", level, "", scalar->s_bool); break; case xr_double: os = octstr_format("%*s%d\n", level, "", scalar->s_double); break; case xr_string: if (scalar->s_str == NULL) { #ifdef XR_ENABLE_EMPTY_STRING_VALUES os = octstr_format("%*s\n", level, ""); #endif } else { Octstr *tmp = octstr_duplicate(scalar->s_str); octstr_convert_to_html_entities(tmp); os = octstr_format("%*s%S\n", level, "", tmp); octstr_destroy(tmp); } break; case xr_date: os = octstr_format("%*s%S\n", level, "", scalar->s_date); break; case xr_base64: os = octstr_format("%*s%S\n", level, "", scalar->s_base64); break; } return os; } /*------------------------------------- * XMLRPCMember - internal functions */ /* Create new member with undefined name and value */ static XMLRPCMember *xmlrpc_member_create(void) { XMLRPCMember *member = gw_malloc(sizeof(XMLRPCMember)); member->name = NULL; member->value = NULL; return member; } /* Destroy member and if destroy_value != 0 destroy its content */ static void xmlrpc_member_destroy(XMLRPCMember *member, int destroy_value) { if (member == NULL) return; octstr_destroy(member->name); if (destroy_value == 1) xmlrpc_value_destroy(member->value); gw_free(member); } /*------------------------------------------------- * Utilities to make things easier */ Octstr *xmlrpc_get_call_name(XMLRPCDocument *call) { if (call == NULL || call->methodcall == NULL) return NULL; return xmlrpc_call_get_name(call->methodcall); } /*** PARAMS HANDLING ***/ int xmlrpc_count_params(XMLRPCDocument *xrdoc) { if (xrdoc == NULL) return -1; if (xrdoc->d_type == xr_methodcall && xrdoc->methodcall != NULL) return gwlist_len(xrdoc->methodcall->params); else if (xrdoc->d_type == xr_methodresponse && xrdoc->methodresponse != NULL) return (xrdoc->methodresponse->param != NULL ? 1 : 0); return -1; } XMLRPCValue *xmlrpc_get_param(XMLRPCDocument *xrdoc, int i) { if (xrdoc == NULL) return NULL; if (xrdoc->d_type == xr_methodcall && xrdoc->methodcall != NULL) return gwlist_len(xrdoc->methodcall->params) > i ? gwlist_get(xrdoc->methodcall->params, i) : NULL; else if (xrdoc->d_type == xr_methodresponse && xrdoc->methodresponse != NULL && i == 0) return xrdoc->methodresponse->param; return NULL; } int xmlrpc_get_type_param(XMLRPCDocument *xrdoc, int i) { XMLRPCValue *param = xmlrpc_get_param(xrdoc, i); return xmlrpc_value_get_type(param); } void *xmlrpc_get_content_param(XMLRPCDocument *xrdoc, int i) { XMLRPCValue *param = xmlrpc_get_param(xrdoc, i); return xmlrpc_value_get_content(param); } /*** STRUCT VALUE HANDLING ***/ XMLRPCValue *xmlrpc_create_struct_value(int count_members) { XMLRPCValue *value = xmlrpc_value_create(); int len = (count_members > 0 ? count_members : OPTIMAL_STRUCT_SIZE); value->v_type = xr_struct; value->v_struct = dict_create(len, xmlrpc_value_destroy_item); return value; } long xmlrpc_count_members(XMLRPCValue *xrstruct) { if (xrstruct == NULL || xrstruct->v_type != xr_struct) return -1; return dict_key_count(xrstruct->v_struct); } int xmlrpc_add_member(XMLRPCValue *xrstruct, Octstr *name, XMLRPCValue *value) { if (xrstruct == NULL || xrstruct->v_type != xr_struct || name == NULL || value == NULL) return -1; return dict_put_once(xrstruct->v_struct, name, value); } int xmlrpc_add_member_scalar(XMLRPCValue *xrstruct, Octstr *name, int type, void *arg) { XMLRPCValue *value = xmlrpc_create_scalar_value(type, arg); int status; status = xmlrpc_add_member(xrstruct, name, value); if (status < 0) xmlrpc_value_destroy(value); return status; } XMLRPCValue *xmlrpc_get_member(XMLRPCValue *xrstruct, Octstr *name) { if (xrstruct == NULL || xrstruct->v_type != xr_struct || name == NULL) return NULL; return dict_get(xrstruct->v_struct, name); } int xmlrpc_get_member_type(XMLRPCValue *xrstruct, Octstr *name) { XMLRPCValue *value = xmlrpc_get_member(xrstruct, name); return xmlrpc_value_get_type(value); } void *xmlrpc_get_member_content(XMLRPCValue *xrstruct, Octstr *name) { XMLRPCValue *value = xmlrpc_get_member(xrstruct, name); return xmlrpc_value_get_content(value); } Octstr *xmlrpc_print_struct(Dict *v_struct, int level) { Octstr *body, *os_val, *key; List *keys; XMLRPCValue *member_val; if (v_struct == NULL || dict_key_count(v_struct) == 0) return NULL; keys = dict_keys(v_struct); body = octstr_format("%*s\n", level, ""); while ((key = gwlist_consume(keys)) != NULL) { member_val = dict_get(v_struct, key); os_val = xmlrpc_value_print(member_val, level+4); if (os_val == NULL) { gwlist_destroy(keys, octstr_destroy_item); octstr_destroy(key); octstr_destroy(body); return NULL; } octstr_format_append(body, "%*s\n" "%*s%S\n%S" "%*s\n", level+2, "", level+4, "", key, os_val, level+2, ""); octstr_destroy(key); octstr_destroy(os_val); } gwlist_destroy(keys, octstr_destroy_item); octstr_format_append(body, "%*s\n", level, ""); return body; } /*** ARRAY VALUE HANDLING ***/ XMLRPCValue *xmlrpc_create_array_value(void) { XMLRPCValue *value = xmlrpc_value_create(); value->v_type = xr_array; value->v_array = gwlist_create(); return value; } int xmlrpc_count_elements(XMLRPCValue *xrarray) { if (xrarray == NULL || xrarray->v_type != xr_array) return -1; return gwlist_len(xrarray->v_array); } int xmlrpc_add_element(XMLRPCValue *xrarray, XMLRPCValue *value) { if (xrarray == NULL || xrarray->v_type != xr_array || value == NULL) return -1; gwlist_produce(xrarray->v_array, value); return 1; } int xmlrpc_add_element_scalar(XMLRPCValue *xrarray, int type, void *arg) { XMLRPCValue *value = xmlrpc_create_scalar_value(type, arg); int status; status = xmlrpc_add_element(xrarray, value); if (status < 0) xmlrpc_value_destroy(value); return status; } XMLRPCValue *xmlrpc_get_element(XMLRPCValue *xrarray, int i) { if (xrarray == NULL || xrarray->v_type != xr_array || i < 0) return NULL; return gwlist_get(xrarray->v_array, i); } int xmlrpc_get_element_type(XMLRPCValue *xrarray, int i) { XMLRPCValue *value = xmlrpc_get_element(xrarray, i); return xmlrpc_value_get_type(value); } void *xmlrpc_get_element_content(XMLRPCValue *xrarray, int i) { XMLRPCValue *value = xmlrpc_get_element(xrarray, i); return xmlrpc_value_get_content(value); } Octstr *xmlrpc_print_array(List *v_array, int level) { Octstr *body, *os_element; XMLRPCValue *element = NULL; int i; if (v_array == NULL) return NULL; body = octstr_format("%*s\n%*s\n", level, "", level+2, ""); for(i = 0; i < gwlist_len(v_array); i++) { element = gwlist_get(v_array, i); os_element = xmlrpc_value_print(element, level+4); if (os_element == NULL) { octstr_destroy(body); return NULL; } octstr_append(body, os_element); octstr_destroy(os_element); } octstr_format_append(body, "%*s\n%*s\n", level+2, "", level, ""); return body; } /*** SCALAR VALUE HANDLING ***/ XMLRPCValue *xmlrpc_create_scalar_value(int type, void *arg) { XMLRPCValue *value = xmlrpc_value_create(); value->v_type = xr_scalar; value->v_scalar = xmlrpc_scalar_create(type, arg); return value; } XMLRPCValue *xmlrpc_create_double_value(double val) { return xmlrpc_create_scalar_value(xr_double, &val); } XMLRPCValue *xmlrpc_create_int_value(long val) { return xmlrpc_create_scalar_value(xr_int, &val); } XMLRPCValue *xmlrpc_create_string_value(Octstr *val) { return xmlrpc_create_scalar_value(xr_string, val); } /*** FAULT HANDLING ***/ int xmlrpc_is_fault(XMLRPCDocument *response) { if (response == NULL || response->d_type != xr_methodresponse) return 0; return xmlrpc_response_is_fault(response->methodresponse); } long xmlrpc_get_faultcode(XMLRPCDocument *faultresponse) { if (! xmlrpc_is_fault(faultresponse)) { error(0, "XMLRPC object is not fault response."); return -1; } return xmlrpc_response_get_faultcode(faultresponse->methodresponse); } Octstr *xmlrpc_get_faultstring(XMLRPCDocument *faultresponse) { if (! xmlrpc_is_fault(faultresponse)) { error(0, "XMLRPC object is not fault response."); return NULL; } return xmlrpc_response_get_faultstring(faultresponse->methodresponse); } /*** PARSE STATUS HANDLING***/ int xmlrpc_parse_status(XMLRPCDocument *xrdoc) { if (xrdoc == NULL) return -1; return xrdoc->parse_status; } Octstr *xmlrpc_parse_error(XMLRPCDocument *xrdoc) { if (xrdoc == NULL) return NULL; return octstr_duplicate(xrdoc->parse_error); } /*------------------------------------------------- * Internal parser functions */ static int parse_document(xmlDocPtr document, XMLRPCDocument *xrdoc) { xmlNodePtr node; Octstr *name; int n = 0, status = 0; node = xmlDocGetRootElement(document); /* * check if this is at least a valid root element */ if (node == NULL || node->name == NULL) { error(0, "XMLRPC: XML document - not valid root node!"); xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); return -1; } name = octstr_create(node->name); if (octstr_len(name) == 0) { octstr_destroy(name); return -1; } if ((xrdoc->d_type == xr_methodcall || xrdoc->d_type == xr_undefined) && octstr_case_compare(name, octstr_imm("METHODCALL")) == 0) { xrdoc->d_type = xr_methodcall; xrdoc->methodcall = xmlrpc_call_create(NULL); octstr_destroy(name); status = parse_methodcall(document, node->xmlChildrenNode, xrdoc, xrdoc->methodcall); if (status < 0) { xmlrpc_call_destroy(xrdoc->methodcall); xrdoc->methodcall = NULL; if (xrdoc->parse_status == XMLRPC_COMPILE_OK) { xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); } } else if ((xrdoc->methodcall->method_name) == NULL) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: tag expected!"); status = -1; } return status; } else if ((xrdoc->d_type == xr_methodresponse || xrdoc->d_type == xr_undefined) && octstr_case_compare(name, octstr_imm("METHODRESPONSE")) == 0) { xrdoc->d_type = xr_methodresponse; xrdoc->methodresponse = xmlrpc_response_create(); octstr_destroy(name); status = parse_methodresponse(document, node->xmlChildrenNode, xrdoc, xrdoc->methodresponse, &n); if (status < 0) { xmlrpc_response_destroy(xrdoc->methodresponse); xrdoc->methodresponse = NULL; } return status; } else { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: wrong root element <%s>, " "<%s> expected!", octstr_get_cstr(name), (xrdoc->d_type == xr_methodcall ? "methodCall" : "methodResponse")); octstr_destroy(name); return -1; } } static int parse_methodcall(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMethodCall *methodcall) { int status = 0; /* call for the parser function of the node type. */ switch (node->type) { case XML_ELEMENT_NODE: status = parse_methodcall_element(doc, node, xrdoc, methodcall); break; case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_PI_NODE: /* Text nodes, comments and PIs are ignored. */ break; /* * XML has also many other node types, these are not needed with * XML-RPC. Therefore they are assumed to be an error. */ default: xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown XML node " "in the XML-RPC source."); return -1; break; } if (node->next != NULL) return parse_methodcall(doc, node->next, xrdoc, methodcall); return status; } static int parse_methodcall_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMethodCall *methodcall) { Octstr *name; xmlChar *content_buff; size_t i; /* * check if the element is allowed at this level */ if (node->name == NULL) { error(0, "XMLRPC: XML methodcall element nodes without name!"); xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); return -1; } name = octstr_create(node->name); if (octstr_len(name) == 0) { octstr_destroy(name); return -1; } i = 0; while (i < NUMBER_OF_METHODCALL_ELEMENTS) { if (octstr_case_compare(name, octstr_imm(methodcall_elements[i].name)) == 0) break; ++i; } if (i == NUMBER_OF_METHODCALL_ELEMENTS) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown tag '%s' in XML source " "at level ", octstr_get_cstr(name)); octstr_destroy(name); return -1; } octstr_destroy(name); /* * now check which type it is and process * * valid tags at this level are: * methodName [0] * params [1] */ if (i == 0) { /* this has been the tag */ if (methodcall->method_name == NULL) { /*only one tag allowed*/ content_buff = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if (content_buff != NULL) { methodcall->method_name = octstr_create(content_buff); xmlFree(content_buff); } else { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: empty tag in XML source " "at level "); return -1; } } else { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: duplicated tag in XML source " "at level "); octstr_destroy(name); return -1; } } else { /* * ok, this has to be an tag, otherwise we would * have returned previosly */ return parse_params(doc, node->xmlChildrenNode, xrdoc, methodcall->params); } return 0; } static int parse_methodresponse(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMethodResponse *methodresponse, int* n) { int status = 0; /* call for the parser function of the node type. */ switch (node->type) { case XML_ELEMENT_NODE: if (*n > 0) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unexpected XML node <%s> " "in the XML-RPC source.", node->name); return -1; } status = parse_methodresponse_element(doc, node, xrdoc, methodresponse); (*n)++; break; case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_PI_NODE: /* Text nodes, comments and PIs are ignored. */ break; /* * XML has also many other node types, these are not needed with * XML-RPC. Therefore they are assumed to be an error. */ default: xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown XML node " "in the XML-RPC source."); return -1; break; } if (node->next != NULL) { if (parse_methodresponse(doc, node->next, xrdoc, methodresponse, n) == -1) { return -1; } } return status; } static int parse_methodresponse_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMethodResponse *methodresponse) { Octstr *name; size_t i; int status; /* * check if the element is allowed at this level */ if (node->name == NULL) { error(0, "XMLRPC: XML methodResponse element nodes without name!"); xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); return -1; } name = octstr_create(node->name); if (octstr_len(name) == 0) { octstr_destroy(name); return -1; } i = 0; while (i < NUMBER_OF_METHODRESPONSE_ELEMENTS) { if (octstr_case_compare(name, octstr_imm(methodresponse_elements[i].name)) == 0) break; ++i; } if (i == NUMBER_OF_METHODRESPONSE_ELEMENTS) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown tag '%s' in XML source " "at level ", octstr_get_cstr(name)); octstr_destroy(name); return -1; } octstr_destroy(name); /* * now check which type it is and process * * valid tags at this level are: * fault [0] * params [1] */ if (i == 0) { /* this has been the tag */ methodresponse->fault = xmlrpc_fault_create(0, NULL); return parse_fault(doc, node->xmlChildrenNode, xrdoc, methodresponse->fault); } else { /* * ok, this has to be an tag, otherwise we would * have returned previosly */ List *params = gwlist_create();; status = parse_params(doc, node->xmlChildrenNode, xrdoc, params); if (status < 0) return -1; if (gwlist_len(params) != 1) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler:wrong number of params " "at level "); gwlist_destroy(params, xmlrpc_value_destroy_item); return -1; } methodresponse->param = gwlist_consume(params); gwlist_destroy(params, xmlrpc_value_destroy_item); return status; } } static int parse_params(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *params) { int status = 0; /* call for the parser function of the node type. */ switch (node->type) { case XML_ELEMENT_NODE: status = parse_params_element(doc, node, xrdoc, params); break; case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_PI_NODE: /* Text nodes, comments and PIs are ignored. */ break; /* * XML has also many other node types, these are not needed with * XML-RPC. Therefore they are assumed to be an error. */ default: xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown XML node in XML-RPC source."); return -1; break; } if (node->next != NULL) if (parse_params(doc, node->next, xrdoc, params) == -1) return -1; return status; } static int parse_params_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *params) { Octstr *name; size_t i; int n = 0; /* * check if the element is allowed at this level * within we only have one or more */ if (node->name == NULL) { error(0, "XMLRPC: XML params element nodes without name!"); xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); return -1; } name = octstr_create(node->name); if (octstr_len(name) == 0) { octstr_destroy(name); return -1; } i = 0; while (i < NUMBER_OF_PARAMS_ELEMENTS) { if (octstr_case_compare(name, octstr_imm(params_elements[i].name)) == 0) break; ++i; } if (i == NUMBER_OF_PARAMS_ELEMENTS) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown tag '%s' " "in XML source at level ", octstr_get_cstr(name)); octstr_destroy(name); return -1; } octstr_destroy(name); /* * now check which type it is and process * * valid tags at this level are: * param [0] */ if (i == 0) { /* this has been a tag */ if (parse_param(doc, node->xmlChildrenNode, xrdoc, params, &n) == -1) return -1; } else { /* we should never be here */ xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bogus parsing exception in parse_params!"); return -1; } return 0; } static int parse_param(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *params, int *n) { int status = 0; /* call for the parser function of the node type. */ switch (node->type) { case XML_ELEMENT_NODE: /* a can only have one value element type */ if ((*n) > 0) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: param may only have one value!"); return -1; } status = parse_param_element(doc, node, xrdoc, params); (*n)++; break; case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_PI_NODE: /* Text nodes, comments and PIs are ignored. */ break; /* * XML has also many other node types, these are not needed with * XML-RPC. Therefore they are assumed to be an error. */ default: xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: Unknown XML node in the XML-RPC source."); return -1; break; } if (node->next != NULL) if (parse_param(doc, node->next, xrdoc, params, n) == -1) return -1; return status; } static int parse_param_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *params) { Octstr *name; size_t i; XMLRPCValue *value; /* * check if the element is allowed at this level */ if (node->name == NULL) { error(0, "XMLRPC: XML param element nodes without name!"); xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); return -1; } name = octstr_create(node->name); if (octstr_len(name) == 0) { octstr_destroy(name); return -1; } i = 0; while (i < NUMBER_OF_PARAM_ELEMENTS) { if (octstr_case_compare(name, octstr_imm(param_elements[i].name)) == 0) break; ++i; } if (i == NUMBER_OF_PARAM_ELEMENTS) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown tag '%s' " "in XML source at level ", octstr_get_cstr(name)); octstr_destroy(name); return -1; } octstr_destroy(name); /* * now check which type it is and process * * valid tags at this level are: * value [0] */ if (i == 0) { /* this has been a tag */ value = xmlrpc_value_create(); if (parse_value(doc, node->xmlChildrenNode, xrdoc, value) == -1) { xmlrpc_value_destroy(value); return -1; } gwlist_append(params, value); } else { /* we should never be here */ xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bogus parsing exception in parse_param!"); return -1; } return 0; } static int parse_value(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCValue *value) { int status = 0; /* call for the parser function of the node type. */ switch (node->type) { case XML_ELEMENT_NODE: status = parse_value_element(doc, node, xrdoc, value); break; case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_PI_NODE: /* Text nodes, comments and PIs are ignored. */ break; /* * XML has also many other node types, these are not needed with * XML-RPC. Therefore they are assumed to be an error. */ default: xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: Unknown XML node in the XML-RPC source."); return -1; break; } if (node->next != NULL) if (parse_value(doc, node->next, xrdoc, value) == -1) return -1; return status; } static int parse_value_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCValue *xrvalue) { Octstr *name; Octstr *value = NULL; xmlChar *content_buff; long lval = 0; double dval = 0.0; size_t i; /* * check if the element is allowed at this level */ if (node->name == NULL) { error(0, "XMLRPC: XML value element nodes without name!"); xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); return -1; } name = octstr_create(node->name); if (octstr_len(name) == 0) { octstr_destroy(name); return -1; } i = 0; while (i < NUMBER_OF_VALUE_ELEMENTS) { if (octstr_case_compare(name, octstr_imm(value_elements[i].name)) == 0) break; ++i; } if (i == NUMBER_OF_VALUE_ELEMENTS) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown tag '%s' " "in XML source at level ", octstr_get_cstr(name)); octstr_destroy(name); return -1; } octstr_destroy(name); content_buff = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if (content_buff != NULL) { value = octstr_create(content_buff); xmlFree(content_buff); } /* * now check which type it is and process * * valid tags at this level are: * i4, int * boolean * string * double * dateTime.iso8601 * base64 * struct * array */ switch (value_elements[i].s_type) { /* * scalar types */ case xr_int: case xr_bool: case xr_double: case xr_date: case xr_base64: #ifndef XR_ENABLE_EMPTY_STRING_VALUES case xr_string: #endif if (value == NULL) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: no value for '%s'", node->name); return -1; } break; } switch (value_elements[i].s_type) { /* * scalar types */ case xr_int: if (value != NULL && octstr_parse_long(&lval, value, 0, 10) < 0) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: could not parse int value '%s'", octstr_get_cstr(value)); octstr_destroy(value); return -1; } xrvalue->v_type = xr_scalar; xrvalue->v_scalar = xmlrpc_scalar_create(xr_int, (void *) &lval); break; case xr_bool: if (value != NULL && octstr_parse_long(&lval, value, 0, 10) < 0) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: could not parse boolean value '%s'", octstr_get_cstr(value)); octstr_destroy(value); return -1; } xrvalue->v_type = xr_scalar; xrvalue->v_scalar = xmlrpc_scalar_create(xr_bool, (void *) &lval); break; case xr_double: if (value != NULL && octstr_parse_double(&dval, value, 0) < 0) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: could not parse double value '%s'", octstr_get_cstr(value)); octstr_destroy(value); return -1; } xrvalue->v_type = xr_scalar; xrvalue->v_scalar = xmlrpc_scalar_create(xr_double, (void *) &dval); break; case xr_string: xrvalue->v_type = xr_scalar; xrvalue->v_scalar = xmlrpc_scalar_create(xr_string, (void *) value); break; case xr_date: xrvalue->v_type = xr_scalar; xrvalue->v_scalar = xmlrpc_scalar_create(xr_date, (void *) value); break; case xr_base64: xrvalue->v_type = xr_scalar; xrvalue->v_scalar = xmlrpc_scalar_create(xr_base64, (void *) value); break; case xr_struct: xrvalue->v_type = xr_struct; xrvalue->v_struct = dict_create(OPTIMAL_STRUCT_SIZE, xmlrpc_value_destroy_item); if (parse_struct(doc, node->xmlChildrenNode, xrdoc, xrvalue->v_struct) == -1) { octstr_destroy(value); return -1; } break; case xr_array: xrvalue->v_type = xr_array; xrvalue->v_array = gwlist_create(); if (parse_array(doc, node->xmlChildrenNode, xrdoc, xrvalue->v_array) == -1) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: could not parse array"); octstr_destroy(value); return -1; } break; default: xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bogus parsing exception in parse_value!"); return -1; } octstr_destroy(value); return 0; } static int parse_struct(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, Dict *members) { int status = 0; /* call for the parser function of the node type. */ switch (node->type) { case XML_ELEMENT_NODE: status = parse_struct_element(doc, node, xrdoc, members); break; case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_PI_NODE: /* Text nodes, comments and PIs are ignored. */ break; /* * XML has also many other node types, these are not needed with * XML-RPC. Therefore they are assumed to be an error. */ default: xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: Unknown XML node in the XML-RPC source."); return -1; break; } if (node->next != NULL) if (parse_struct(doc, node->next, xrdoc, members) == -1) return -1; return status; } static int parse_struct_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, Dict *members) { Octstr *name; size_t i; XMLRPCMember *member; /* * check if the element is allowed at this level */ if (node->name == NULL) { error(0, "XMLRPC: XML struct element nodes without name!"); xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); return -1; } name = octstr_create(node->name); if (octstr_len(name) == 0) { octstr_destroy(name); return -1; } i = 0; while (i < NUMBER_OF_STRUCT_ELEMENTS) { if (octstr_case_compare(name, octstr_imm(struct_elements[i].name)) == 0) break; ++i; } if (i == NUMBER_OF_STRUCT_ELEMENTS) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown tag '%s' " "in XML source at level ", octstr_get_cstr(name)); octstr_destroy(name); return -1; } octstr_destroy(name); /* * now check which type it is and process * * valid tags at this level are: * member [0] */ if (i == 0) { /* this has been a tag */ member = xmlrpc_member_create(); if (parse_member(doc, node->xmlChildrenNode, xrdoc, member) == -1) { xmlrpc_member_destroy(member, 1); return -1; } if (! dict_put_once(members, member->name, member->value)) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: at least two members have same name."); xmlrpc_member_destroy(member, 1); return -1; } xmlrpc_member_destroy(member, 0); } else { /* we should never be here */ xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bogus parsing exception in parse_struct!"); return -1; } return 0; } static int parse_member(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMember *member) { int status = 0; /* call for the parser function of the node type. */ switch (node->type) { case XML_ELEMENT_NODE: status = parse_member_element(doc, node, xrdoc, member); break; case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_PI_NODE: /* Text nodes, comments and PIs are ignored. */ break; /* * XML has also many other node types, these are not needed with * XML-RPC. Therefore they are assumed to be an error. */ default: xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: Unknown XML node in the XML-RPC source."); return -1; break; } if (node->next != NULL) if (parse_member(doc, node->next, xrdoc, member) == -1) return -1; return status; } static int parse_member_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCMember *member) { Octstr *name; xmlChar *content_buff; size_t i; /* * check if the element is allowed at this level */ if (node->name == NULL) { error(0, "XMLRPC: XML member element nodes without name!"); xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); return -1; } name = octstr_create(node->name); if (octstr_len(name) == 0) { octstr_destroy(name); return -1; } i = 0; while (i < NUMBER_OF_MEMBER_ELEMENTS) { if (octstr_case_compare(name, octstr_imm(member_elements[i].name)) == 0) break; ++i; } if (i == NUMBER_OF_MEMBER_ELEMENTS) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown tag '%s' " "in XML source at level ", octstr_get_cstr(name)); octstr_destroy(name); return -1; } octstr_destroy(name); /* * now check which type it is and process * * valid tags at this level are: * name [0] * value [1] */ if (i == 0) { /* this has been a tag */ if (member->name != NULL) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: duplicated tag '' " "in XML source at level "); return -1; } content_buff = xmlNodeListGetString(doc, node->xmlChildrenNode, 1); if (content_buff != NULL) { member->name = octstr_create(content_buff); xmlFree(content_buff); } else { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: empty tag in XML source " "at level "); return -1; } } else { member->value = xmlrpc_value_create(); if (parse_value(doc, node->xmlChildrenNode, xrdoc, member->value) == -1) { xmlrpc_value_destroy(member->value); member->value = NULL; return -1; } } return 0; } static int parse_array(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *elements) { int status = 0; /* call for the parser function of the node type. */ switch (node->type) { case XML_ELEMENT_NODE: status = parse_array_element(doc, node, xrdoc, elements); break; case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_PI_NODE: /* Text nodes, comments and PIs are ignored. */ break; /* * XML has also many other node types, these are not needed with * XML-RPC. Therefore they are assumed to be an error. */ default: xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: Unknown XML node in the XML-RPC source."); return -1; break; } if (node->next != NULL) if (parse_array(doc, node->next, xrdoc, elements) == -1) return -1; return status; } static int parse_array_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *elements) { Octstr *name; size_t i; /* * check if the element is allowed at this level */ if (node->name == NULL) { error(0, "XMLRPC: XML array element nodes without name!"); xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); return -1; } name = octstr_create(node->name); if (octstr_len(name) == 0) { octstr_destroy(name); return -1; } i = 0; while (i < NUMBER_OF_ARRAY_ELEMENTS) { if (octstr_case_compare(name, octstr_imm(array_elements[i].name)) == 0) break; ++i; } if (i == NUMBER_OF_ARRAY_ELEMENTS) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown tag '%s' " "in XML source at level ", octstr_get_cstr(name)); octstr_destroy(name); return -1; } octstr_destroy(name); /* * now check which type it is and process * * valid tags at this level are: * data [0] */ if (i == 0) { /* this has been a tag */ if (parse_data(doc, node->xmlChildrenNode, xrdoc, elements) == -1) return -1; } else { /* we should never be here */ xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bogus parsing exception in parse_array!"); return -1; } return 0; } static int parse_data(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *elements) { int status = 0; /* call for the parser function of the node type. */ switch (node->type) { case XML_ELEMENT_NODE: status = parse_data_element(doc, node, xrdoc, elements); break; case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_PI_NODE: /* Text nodes, comments and PIs are ignored. */ break; /* * XML has also many other node types, these are not needed with * XML-RPC. Therefore they are assumed to be an error. */ default: xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: Unknown XML node in the XML-RPC source."); return -1; break; } if (node->next != NULL) if (parse_data(doc, node->next, xrdoc, elements) == -1) return -1; return status; } static int parse_data_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, List *elements) { Octstr *name; XMLRPCValue *value; size_t i; /* * check if the element is allowed at this level */ if (node->name == NULL) { error(0, "XMLRPC: XML data element nodes without name!"); xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); return -1; } name = octstr_create(node->name); if (octstr_len(name) == 0) { octstr_destroy(name); return -1; } i = 0; while (i < NUMBER_OF_DATA_ELEMENTS) { if (octstr_case_compare(name, octstr_imm(data_elements[i].name)) == 0) break; ++i; } if (i == NUMBER_OF_DATA_ELEMENTS) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown tag '%s' " "in XML source at level ", octstr_get_cstr(name)); octstr_destroy(name); return -1; } octstr_destroy(name); /* * now check which type it is and process * * valid tags at this level are: * value [0] */ if (i == 0) { /* this has been a tag */ value = xmlrpc_value_create(); if (parse_value(doc, node->xmlChildrenNode, xrdoc, value) == -1) { xmlrpc_value_destroy(value); return -1; } gwlist_append(elements, value); } else { /* we should never be here */ xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bogus parsing exception in parse_array!"); return -1; } return 0; } static int parse_fault(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCFault *fault) { int status = 0; /* call for the parser function of the node type. */ switch (node->type) { case XML_ELEMENT_NODE: status = parse_fault_element(doc, node, xrdoc, fault); break; case XML_TEXT_NODE: case XML_COMMENT_NODE: case XML_PI_NODE: /* Text nodes, comments and PIs are ignored. */ break; /* * XML has also many other node types, these are not needed with * XML-RPC. Therefore they are assumed to be an error. */ default: xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: Unknown XML node in the XML-RPC source."); return -1; break; } if (node->next != NULL) if (parse_fault(doc, node->next, xrdoc, fault) == -1) return -1; return status; } static int parse_fault_element(xmlDocPtr doc, xmlNodePtr node, XMLRPCDocument *xrdoc, XMLRPCFault *fault) { Octstr *name; XMLRPCValue *value, *v_code, *v_string; size_t i; /* * check if the element is allowed at this level */ if (node->name == NULL) { error(0, "XMLRPC: XML fault element nodes without name!"); xrdoc->parse_status = XMLRPC_XMLPARSE_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bad XML"); return -1; } name = octstr_create(node->name); if (octstr_len(name) == 0) { octstr_destroy(name); return -1; } i = 0; while (i < NUMBER_OF_FAULT_ELEMENTS) { if (octstr_case_compare(name, octstr_imm(fault_elements[i].name)) == 0) break; ++i; } if (i == NUMBER_OF_FAULT_ELEMENTS) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: unknown tag '%s' " "in XML source at level ", octstr_get_cstr(name)); octstr_destroy(name); return -1; } octstr_destroy(name); /* * now check which type it is and process * * valid tags at this level are: * value [0] */ if (i == 0) { /* this has been a tag */ value = xmlrpc_value_create(); if (parse_value(doc, node->xmlChildrenNode, xrdoc, value) == -1) { xmlrpc_value_destroy(value); return -1; } /* must be : * * * faultCode * ... * * * faultString * ... * * */ if (xmlrpc_value_get_type(value) != xr_struct || (v_code = xmlrpc_get_member(value, octstr_imm("faultCode"))) == NULL || xmlrpc_value_get_type_smart(v_code) != xr_int || (v_string = xmlrpc_get_member(value, octstr_imm("faultString"))) == NULL || xmlrpc_value_get_type_smart(v_string) != xr_string || xmlrpc_count_members(value) != 2) { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: bogus value " "in XML source at level "); xmlrpc_value_destroy(value); return -1; } fault->f_code = xmlrpc_scalar_get_int((XMLRPCScalar *) xmlrpc_value_get_content(v_code)); fault->f_string = xmlrpc_scalar_get_string((XMLRPCScalar *) xmlrpc_value_get_content(v_string)); xmlrpc_value_destroy(value); } else { xrdoc->parse_status = XMLRPC_PARSING_FAILED; xrdoc->parse_error = octstr_format("XML-RPC compiler: duplicated tag '' " "in XML source at level "); return -1; } return 0; }