/* ====================================================================
* 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;
}