cvs: php4 /ext/xmlrpc CREDITS EXPERIMENTAL Makefile.in config.m4 libs.mk php_config.h.in php_xmlrpc.h xmlrpc-epi-php.c /ext/xmlrpc/libxmlrpc
Makefile.in README acinclude.m4 base64.c base64.h encodings.c encodings.h libs.mk queue.c queue.h simplestring.c simplestring.h system_methods.c
system_methods_private.h xml_element.c xml_element.h xml_to_dandarpc.c xml_to_dandarpc.h xml_to_xmlrpc.c xml_to_xmlrpc.h xmlrpc.c xmlrpc.h xmlrpc.m4
xmlrpc_introspection.c xmlrpc_introspection.h xmlrpc_introspection_private.h xmlrpc_private.h
| From: | Dan Libby | Date: | Thu, 06 Sep 2001 04:13:30 +0000 |
| Subject: | cvs: php4 /ext/xmlrpc CREDITS EXPERIMENTAL Makefile.in config.m4 libs.mk php_config.h.in php_xmlrpc.h xmlrpc-epi-php.c /ext/xmlrpc/libxmlrpc Makefile.in README acinclude.m4 base64.c base64.h encodings.c encodings.h libs.mk queue.c queue.h simplestring.c simplestring.h system_methods.c system_methods_private.h xml_element.c xml_element.h xml_to_dandarpc.c xml_to_dandarpc.h xml_to_xmlrpc.c xml_to_xmlrpc.h xmlrpc.c xmlrpc.h xmlrpc.m4 xmlrpc_introspection.c xmlrpc_introspection.h xmlrpc_introspection_private.h xmlrpc_private.h |
||
| Groups: | php.cvs | ||
| Request: | Send a blank email to php-cvs+get-7239@lists.php.net to get a copy of this message | ||
danda Thu Sep 6 00:13:30 2001 EDT
Added files:
/php4/ext/xmlrpc CREDITS EXPERIMENTAL Makefile.in config.m4 libs.mk
php_config.h.in php_xmlrpc.h xmlrpc-epi-php.c
/php4/ext/xmlrpc/libxmlrpc Makefile.in README acinclude.m4 base64.c
base64.h encodings.c encodings.h libs.mk
queue.c queue.h simplestring.c
simplestring.h system_methods.c
system_methods_private.h xml_element.c
xml_element.h xml_to_dandarpc.c
xml_to_dandarpc.h xml_to_xmlrpc.c
xml_to_xmlrpc.h xmlrpc.c xmlrpc.h
xmlrpc.m4 xmlrpc_introspection.c
xmlrpc_introspection.h
xmlrpc_introspection_private.h
xmlrpc_private.h
Log:
adding xmlrpc extension, per Stig's request
Index: php4/ext/xmlrpc/Makefile.in +++ php4/ext/xmlrpc/Makefile.in # $Id: Makefile.in,v 1.1 2001/09/06 04:13:29 danda Exp $ LTLIBRARY_NAME = libxmlrpc.la LTLIBRARY_SOURCES = xmlrpc-epi-php.c LTLIBRARY_SHARED_NAME = xmlrpc.la EXTRA_INCLUDES = -I$(XMLRPC_INC_DIR) LTLIBRARY_LIBADD = $(XMLRPC_LIBADD) LTLIBRARY_SHARED_LIBADD = $(XMLRPC_SHARED_LIBADD) SUBDIRS = $(XMLRPC_SUBDIRS) include $(top_srcdir)/build/dynlib.mk Index: php4/ext/xmlrpc/config.m4 +++ php4/ext/xmlrpc/config.m4 dnl $Id: config.m4,v 1.1 2001/09/06 04:13:29 danda Exp $ sinclude(ext/xmlrpc/libxmlrpc/acinclude.m4) sinclude(ext/xmlrpc/libxmlrpc/xmlrpc.m4) sinclude(libxmlrpc/acinclude.m4) sinclude(libxmlrpc/xmlrpc.m4) AC_DEFUN(XMLRPC_LIB_CHK,[ str="$XMLRPC_DIR/$1/libxmlrpc.*" for j in `echo $str`; do if test -r $j; then XMLRPC_LIB_DIR=$XMLRPC_DIR/$1 break 2 fi done ]) PHP_ARG_WITH(xmlrpc, for XMLRPC-EPI support, [ --with-xmlrpc[=DIR] Include XMLRPC-EPI support. DIR is the XMLRPC-EPI base directory. If unspecified, the bundled XMLRPC-EPI library will be used.], yes) PHP_ARG_WITH(expat-dir, libexpat dir for XMLRPC-EPI [ --with-expat-dir=DIR XMLRPC-EPI: libexpat dir for XMLRPC-EPI]) if test "$PHP_XMLRPC" != "no"; then AC_DEFINE(HAVE_XMLRPC, 1, [Whether you have XMLRPC-EPI]) PHP_EXTENSION(xmlrpc,$ext_shared) dnl check for iconv found_iconv=no AC_CHECK_LIB(c, iconv_open, found_iconv=yes) if test "$found_iconv" = "no"; then for i in /usr /usr/local $ICONV_DIR; do if test -f $i/lib/libconv.a -o -f $i/lib/libiconv.s?; then PHP_ADD_LIBRARY_WITH_PATH(iconv, $i/lib, XMLRPC_SHARED_LIBADD) found_iconv=yes fi done fi if test "$found_iconv" = "no"; then AC_MSG_ERROR(iconv not found, in order to build XMLRPC-EPI you need the iconv library) fi dnl check for expat testval=no for i in $PHP_EXPAT_DIR; do if test -f $i/lib/libexpat.a -o -f $i/lib/libexpat.s?; then AC_DEFINE(HAVE_LIBEXPAT2,1,[ ]) PHP_ADD_LIBRARY_WITH_PATH(expat, $i/lib, XMLRPC_SHARED_LIBADD) PHP_ADD_INCLUDE($i/include) testval=yes fi done if test "$testval" = "no"; then PHP_ADD_LIBRARY(xmlparse) PHP_ADD_LIBRARY(xmltok) fi fi if test "$PHP_XMLRPC" = "yes"; then XMLRPC_CHECKS XMLRPC_LIBADD=libxmlrpc/libxmlrpc.la XMLRPC_SHARED_LIBADD=libxmlrpc/libxmlrpc.la XMLRPC_SUBDIRS=libxmlrpc PHP_SUBST(XMLRPC_LIBADD) PHP_SUBST(XMLRPC_SUBDIRS) LIB_BUILD($ext_builddir/libxmlrpc,$ext_shared,yes) PHP_ADD_INCLUDE($ext_srcdir/libxmlrpc) XMLRPC_MODULE_TYPE=builtin elif test "$PHP_XMLRPC" != "no"; then for i in $PHP_XMLRPC; do if test -r $i/include/xmlrpc/xmlrpc.h; then XMLRPC_DIR=$i XMLRPC_INC_DIR=$i/include/xmlrpc elif test -r $i/include/xmlrpc.h; then XMLRPC_DIR=$i XMLRPC_INC_DIR=$i/include fi done if test -z "$XMLRPC_DIR"; then AC_MSG_ERROR(Cannot find header files under $PHP_XMLRPC) fi XMLRPC_MODULE_TYPE=external for i in lib lib/xmlrpc; do XMLRPC_LIB_CHK($i) done if test -z "$XMLRPC_LIB_DIR"; then AC_MSG_ERROR(Cannot find xmlrpc library under $XMLRPC_DIR) fi if test "$PHP_ZLIB_DIR" != "no"; then PHP_ADD_LIBRARY(z,, XMLRPC_SHARED_LIBADD) XMLRPC_LIBS="-L$PHP_ZLIB_DIR -z" fi PHP_ADD_LIBRARY_WITH_PATH(xmlrpc, $XMLRPC_LIB_DIR, XMLRPC_SHARED_LIBADD) XMLRPC_LIBS="-L$XMLRPC_LIB_DIR -lxmlrpc $XMLRPC_LIBS" PHP_ADD_INCLUDE($XMLRPC_INC_DIR) XMLRPC_INCLUDE="-I$XMLRPC_INC_DIR" else XMLRPC_MODULE_TYPE=none fi PHP_SUBST(XMLRPC_SHARED_LIBADD) PHP_SUBST_OLD(XMLRPC_MODULE_TYPE) PHP_SUBST_OLD(XMLRPC_LIBS) PHP_SUBST_OLD(XMLRPC_INCLUDE) Index: php4/ext/xmlrpc/libs.mk +++ php4/ext/xmlrpc/libs.mk include $(top_builddir)/config_vars.mk LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X) LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo) $(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES) $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD) targets = $(LTLIBRARY_NAME) Index: php4/ext/xmlrpc/php_config.h.in +++ php4/ext/xmlrpc/php_config.h.in /* php_config.h.in. Generated automatically from configure.in by autoheader. */ /* Define if your C compiler doesn't accept -c and -o together. */ #undef NO_MINUS_C_MINUS_O /* Whether you have XMLRPC */ #undef HAVE_XMLRPC /* Whether to build xmlrpc as dynamic module */ #undef COMPILE_DL_XMLRPC Index: php4/ext/xmlrpc/php_xmlrpc.h +++ php4/ext/xmlrpc/php_xmlrpc.h /* This file is part of, or distributed with, libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* auto-generated portions of this file are also subject to the php license */ /* +----------------------------------------------------------------------+ | PHP version 4.0 | +----------------------------------------------------------------------+ | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: | | | +----------------------------------------------------------------------+ */ #ifndef _PHP_XMLRPC_H #define _PHP_XMLRPC_H /* You should tweak config.m4 so this symbol (or some else suitable) gets defined. */ #if 1 /* HAVE_XMLRPC */ extern zend_module_entry xmlrpc_module_entry; #define phpext_xmlrpc_ptr &xmlrpc_module_entry #ifdef PHP_WIN32 #define PHP_XMLRPC_API __declspec(dllexport) #else #define PHP_XMLRPC_API #endif PHP_MINIT_FUNCTION(xmlrpc); PHP_MSHUTDOWN_FUNCTION(xmlrpc); PHP_RINIT_FUNCTION(xmlrpc); PHP_RSHUTDOWN_FUNCTION(xmlrpc); PHP_MINFO_FUNCTION(xmlrpc); PHP_FUNCTION(xmlrpc_encode); PHP_FUNCTION(xmlrpc_decode); PHP_FUNCTION(xmlrpc_decode_request); PHP_FUNCTION(xmlrpc_encode_request); PHP_FUNCTION(xmlrpc_get_type); PHP_FUNCTION(xmlrpc_set_type); PHP_FUNCTION(xmlrpc_server_create); PHP_FUNCTION(xmlrpc_server_destroy); PHP_FUNCTION(xmlrpc_server_register_method); PHP_FUNCTION(xmlrpc_server_call_method); PHP_FUNCTION(xmlrpc_parse_method_descriptions); PHP_FUNCTION(xmlrpc_server_add_introspection_data); PHP_FUNCTION(xmlrpc_server_register_introspection_callback); /* Fill in this structure and use entries in it for thread safety instead of using true globals. */ typedef struct { /* You can use the next one as type if your module registers any resources. Oh, you can of course rename it to something more suitable, add list entry types or remove it if it not needed. It's just an example. */ int le_xmlrpc_server; } php_xmlrpc_globals; /* In every function that needs to use variables in php_xmlrpc_globals, do call XMLRPCLS_FETCH(); after declaring other variables used by that function, and always refer to them as XMLRPCG(variable). You are encouraged to rename these macros something shorter, see examples in any other php module directory. */ #ifdef ZTS #define XMLRPCG(v) (xmlrpc_globals->v) #define XMLRPCLS_FETCH() php_xmlrpc_globals *xmlrpc_globals = ts_resource(gd_xmlrpc_id) #else #define XMLRPCG(v) (xmlrpc_globals.v) #define XMLRPCLS_FETCH() #endif #else #define phpext_xmlrpc_ptr NULL #endif #endif /* _PHP_XMLRPC_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ Index: php4/ext/xmlrpc/xmlrpc-epi-php.c +++ php4/ext/xmlrpc/xmlrpc-epi-php.c /* This file is part of, or distributed with, libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* auto-generated portions of this file are also subject to the php license */ /* +----------------------------------------------------------------------+ | PHP version 4.0 | +----------------------------------------------------------------------+ | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: | | | +----------------------------------------------------------------------+ */ #include "php.h" #include "php_ini.h" #include "php_xmlrpc.h" #include "xmlrpc.h" /* You should tweak config.m4 so this symbol (or some else suitable) gets defined. */ #ifdef ZTS int xmlrpc_globals_id; #else php_xmlrpc_globals xmlrpc_globals; #endif /* Every user visible function must have an entry in xmlrpc_functions[]. */ function_entry xmlrpc_functions[] = { PHP_FE(xmlrpc_encode, NULL) PHP_FE(xmlrpc_decode, NULL) PHP_FE(xmlrpc_decode_request, NULL) PHP_FE(xmlrpc_encode_request, NULL) PHP_FE(xmlrpc_get_type, NULL) PHP_FE(xmlrpc_set_type, NULL) PHP_FE(xmlrpc_server_create, NULL) PHP_FE(xmlrpc_server_destroy, NULL) PHP_FE(xmlrpc_server_register_method, NULL) PHP_FE(xmlrpc_server_call_method, NULL) PHP_FE(xmlrpc_parse_method_descriptions, NULL) PHP_FE(xmlrpc_server_add_introspection_data, NULL) PHP_FE(xmlrpc_server_register_introspection_callback, NULL) {NULL, NULL, NULL} /* Must be the last line in xmlrpc_functions[] */ }; zend_module_entry xmlrpc_module_entry = { "xmlrpc", xmlrpc_functions, PHP_MINIT(xmlrpc), PHP_MSHUTDOWN(xmlrpc), PHP_RINIT(xmlrpc), /* Replace with NULL if there's nothing to do at request start */ PHP_RSHUTDOWN(xmlrpc), /* Replace with NULL if there's nothing to do at request end */ PHP_MINFO(xmlrpc), STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_XMLRPC ZEND_GET_MODULE(xmlrpc) #endif /* Remove comments and fill if you need to have entries in php.ini PHP_INI_BEGIN() PHP_INI_END() */ /******************************* * local structures and defines * *******************************/ // per server data typedef struct _xmlrpc_server_data { pval* method_map; pval* introspection_map; XMLRPC_SERVER server_ptr; } xmlrpc_server_data; // how to format output typedef struct _php_output_options { int b_php_out; STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out; } php_output_options; // data passed to C callback typedef struct _xmlrpc_callback_data { pval* xmlrpc_method; pval* php_function; pval* caller_params; pval* return_data; xmlrpc_server_data* server; char php_executed; } xmlrpc_callback_data; #define PHP_EXT_VERSION "0.41" // output options #define OUTPUT_TYPE_KEY "output_type" #define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1) #define OUTPUT_TYPE_VALUE_PHP "php" #define OUTPUT_TYPE_VALUE_XML "xml" #define VERBOSITY_KEY "verbosity" #define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1) #define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space" #define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only" #define VERBOSITY_VALUE_PRETTY "pretty" #define ESCAPING_KEY "escaping" #define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1) #define ESCAPING_VALUE_CDATA "cdata" #define ESCAPING_VALUE_NON_ASCII "non-ascii" #define ESCAPING_VALUE_NON_PRINT "non-print" #define ESCAPING_VALUE_MARKUP "markup" #define VERSION_KEY "version" #define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1) #define VERSION_VALUE_SIMPLE "simple" #define VERSION_VALUE_XMLRPC "xmlrpc" #define ENCODING_KEY "encoding" #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1) #define ENCODING_DEFAULT "iso-8859-1" // value types #define OBJECT_TYPE_ATTR "xmlrpc_type" #define OBJECT_VALUE_ATTR "scalar" /*********************** * forward declarations * ***********************/ XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue); static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data); /********************* * startup / shutdown * *********************/ static void destroy_server_data(xmlrpc_server_data *server) { if(server) { XMLRPC_ServerDestroy(server->server_ptr); zval_dtor(server->method_map); FREE_ZVAL(server->method_map); zval_dtor(server->introspection_map); FREE_ZVAL(server->introspection_map); efree(server); } } /* called when server is being destructed. either when xmlrpc_server_destroy * is called, or when request ends. */ static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc) { if(rsrc && rsrc->ptr) { destroy_server_data((xmlrpc_server_data*)rsrc->ptr); } } /* module init */ PHP_MINIT_FUNCTION(xmlrpc) { /* Remove comments if you have entries in php.ini REGISTER_INI_ENTRIES(); */ XMLRPCG(le_xmlrpc_server) = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number); return SUCCESS; } /* module shutdown */ PHP_MSHUTDOWN_FUNCTION(xmlrpc) { /* Remove comments if you have entries in php.ini UNREGISTER_INI_ENTRIES(); */ return SUCCESS; } /* Remove if there's nothing to do at request start */ PHP_RINIT_FUNCTION(xmlrpc) { return SUCCESS; } /* Remove if there's nothing to do at request end */ PHP_RSHUTDOWN_FUNCTION(xmlrpc) { return SUCCESS; } /* display info in phpinfo() */ PHP_MINFO_FUNCTION(xmlrpc) { php_info_print_table_start(); php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString()); php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION); php_info_print_table_row(2, "author", "Dan Libby"); php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net"); php_info_print_table_row(2, "open sourced by", "Epinions.com"); php_info_print_table_end(); /* DISPLAY_INI_ENTRIES(); */ } /******************* * random utilities * *******************/ /* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc). * Could easily be further generalized to work with objects. */ static int add_long(pval* list, char* id, int num) { if(id) return add_assoc_long(list, id, num); else return add_next_index_long(list, num); } static int add_double(pval* list, char* id, double num) { if(id) return add_assoc_double(list, id, num); else return add_next_index_double(list, num); } static int add_string(pval* list, char* id, char* string, int duplicate) { if(id) return add_assoc_string(list, id, string, duplicate); else return add_next_index_string(list, string, duplicate); } static int add_stringl(pval* list, char* id, char* string, uint length, int duplicate) { if(id) return add_assoc_stringl(list, id, string, length, duplicate); else return add_next_index_stringl(list, string, length, duplicate); } static int add_pval(pval* list, const char* id, pval** val) { if(list && val) { if(id) return zend_hash_update(list->value.ht, (char*)id, strlen(id)+1, (void *) val, sizeof(pval **), NULL); else return zend_hash_next_index_insert(list->value.ht, (void *) val, sizeof(pval **), NULL); } } #if ZEND_MODULE_API_NO >= 20001222 #define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0) #else #define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index) #endif /************************* * input / output options * *************************/ /* parse an array (user input) into output options suitable for use by xmlrpc engine * and determine whether to return data as xml or php vars */ static void set_output_options(php_output_options* options, pval* output_opts) { if(options) { /* defaults */ options->b_php_out = 0; options->xmlrpc_out.version = xmlrpc_version_1_0; options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT; options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty; options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping; if(output_opts && output_opts->type == IS_ARRAY) { pval** val; /* verbosity of generated xml */ if(zend_hash_find(output_opts->value.ht, OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**)&val) == SUCCESS) { if((*val)->type == IS_STRING) { if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_PHP)) { options->b_php_out = 1; } else if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_XML)) { options->b_php_out = 0; } } } /* verbosity of generated xml */ if(zend_hash_find(output_opts->value.ht, VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, (void**)&val) == SUCCESS) { if((*val)->type == IS_STRING) { if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NO_WHITE_SPACE)) { options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space; } else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NEWLINES_ONLY)) { options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only; } else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_PRETTY)) { options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty; } } } /* version of xml to output */ if(zend_hash_find(output_opts->value.ht, VERSION_KEY, VERSION_KEY_LEN + 1, (void**)&val) == SUCCESS) { if((*val)->type == IS_STRING) { if(!strcmp((*val)->value.str.val, VERSION_VALUE_XMLRPC)) { options->xmlrpc_out.version = xmlrpc_version_1_0; } else if(!strcmp((*val)->value.str.val, VERSION_VALUE_SIMPLE)) { options->xmlrpc_out.version = xmlrpc_version_simple; } } } /* encoding code set */ if(zend_hash_find(output_opts->value.ht, ENCODING_KEY, ENCODING_KEY_LEN + 1, (void**)&val) == SUCCESS) { if((*val)->type == IS_STRING) { options->xmlrpc_out.xml_elem_opts.encoding = estrdup((*val)->value.str.val); } } /* escaping options */ if(zend_hash_find(output_opts->value.ht, ESCAPING_KEY, ESCAPING_KEY_LEN + 1, (void**)&val) == SUCCESS) { /* multiple values allowed. check if array */ if((*val)->type == IS_ARRAY) { pval** iter_val; zend_hash_internal_pointer_reset((*val)->value.ht); options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping; while(1) { if(zend_hash_get_current_data((*val)->value.ht, (void**)&iter_val) == SUCCESS) { if((*iter_val)->type == IS_STRING && (*iter_val)->value.str.val) { if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_CDATA)) { options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping; } else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) { options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping; } else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) { options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping; } else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_MARKUP)) { options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping; } } } else { break; } zend_hash_move_forward((*val)->value.ht); } } /* else, check for single value */ else if((*val)->type == IS_STRING) { if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_CDATA)) { options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping; } else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) { options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping; } else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) { options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping; } else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_MARKUP)) { options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping; } } } } } } /****************** * encode / decode * ******************/ /* php arrays have no distinction between array and struct types. * they even allow mixed. Thus, we determine the type by iterating * through the entire array and figuring out each element. * room for some optimation here if we stop after a specific # of elements. */ static XMLRPC_VECTOR_TYPE determine_vector_type(HashTable *ht) { int bArray = 0, bStruct = 0, bMixed = 0; unsigned long num_index; char* my_key; zend_hash_internal_pointer_reset(ht); while(1) { int res = my_zend_hash_get_current_key(ht, &my_key, &num_index); if(res == HASH_KEY_IS_LONG) { if(bStruct) { bMixed = 1; break; } bArray = 1; } else if(res == HASH_KEY_NON_EXISTANT) { break; } else if(res == HASH_KEY_IS_STRING) { if(bArray) { bMixed = 1; break; } bStruct = 1; } zend_hash_move_forward(ht); } return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array); } /* recursively convert php values into xmlrpc values */ static XMLRPC_VALUE PHP_to_XMLRPC_worker(const char* key, pval* in_val, int depth) { XMLRPC_VALUE xReturn = NULL; if(in_val) { pval* val = NULL; XMLRPC_VALUE_TYPE type = get_pval_xmlrpc_type(in_val, &val); if(val) { switch(type) { case xmlrpc_base64: if(val->type == IS_NULL) { xReturn = XMLRPC_CreateValueBase64(key, "", 1); } else { xReturn = XMLRPC_CreateValueBase64(key, val->value.str.val, val->value.str.len); } break; case xmlrpc_datetime: convert_to_string(val); xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, val->value.str.val); break; case xmlrpc_boolean: convert_to_boolean(val); xReturn = XMLRPC_CreateValueBoolean(key, val->value.lval); break; case xmlrpc_int: convert_to_long(val); xReturn = XMLRPC_CreateValueInt(key, val->value.lval); break; case xmlrpc_double: convert_to_double(val); xReturn = XMLRPC_CreateValueDouble(key, val->value.dval); break; case xmlrpc_string: convert_to_string(val); xReturn = XMLRPC_CreateValueString(key, val->value.str.val, val->value.str.len); break; case xmlrpc_vector: { unsigned long num_index; pval** pIter; char* my_key; convert_to_array(val); xReturn = XMLRPC_CreateVector(key, determine_vector_type(val->value.ht)); zend_hash_internal_pointer_reset(val->value.ht); while(1) { int res = my_zend_hash_get_current_key(val->value.ht, &my_key, &num_index); if(res == HASH_KEY_IS_LONG) { if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) { XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(0, *pIter, depth++)); } } else if(res == HASH_KEY_NON_EXISTANT) { break; } else if(res == HASH_KEY_IS_STRING) { if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) { XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(my_key, *pIter, depth++)); } } zend_hash_move_forward(val->value.ht); } } break; default: break; } } } return xReturn; } static XMLRPC_VALUE PHP_to_XMLRPC(pval* root_val) { return PHP_to_XMLRPC_worker(NULL, root_val, 0); } /* recursively convert xmlrpc values into php values */ static pval* XMLRPC_to_PHP(XMLRPC_VALUE el) { pval* elem = NULL; char* pBuf; const char* pStr; if(el) { XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el); MAKE_STD_ZVAL(elem); /* init. very important. spent a frustrating day finding this out. */ switch(type) { case xmlrpc_empty: elem->type = IS_NULL; break; case xmlrpc_string: pStr = XMLRPC_GetValueString(el); if(pStr) { elem->value.str.len = XMLRPC_GetValueStringLen(el); elem->value.str.val = estrndup(pStr, elem->value.str.len); elem->type = IS_STRING; } break; case xmlrpc_int: elem->value.lval = XMLRPC_GetValueInt(el); elem->type = IS_LONG; break; case xmlrpc_boolean: elem->value.lval = XMLRPC_GetValueBoolean(el); elem->type = IS_BOOL; break; case xmlrpc_double: elem->value.dval = XMLRPC_GetValueDouble(el); elem->type = IS_DOUBLE; break; case xmlrpc_datetime: elem->value.str.len = XMLRPC_GetValueStringLen(el); elem->value.str.val = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), elem->value.str.len); elem->type = IS_STRING; break; case xmlrpc_base64: pStr = XMLRPC_GetValueBase64(el); if(pStr) { elem->value.str.len = XMLRPC_GetValueStringLen(el); elem->value.str.val = estrndup(pStr, elem->value.str.len); elem->type = IS_STRING; } break; case xmlrpc_vector: if(array_init(elem) == SUCCESS) { XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el); while( xIter ) { pval* val = XMLRPC_to_PHP(xIter); if(val) { add_pval(elem, XMLRPC_GetValueID(xIter), &val); } xIter = XMLRPC_VectorNext(el); } } break; default: break; } set_pval_xmlrpc_type(elem, type); } return elem; } /* {{{ proto string xmlrpc_encode_request(string method, mixed params) generate xml for a method request */ PHP_FUNCTION(xmlrpc_encode_request) { XMLRPC_REQUEST xRequest = NULL; pval* method, *vals, *out_opts; char* outBuf; php_output_options out; if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &method, &vals, &out_opts) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } set_output_options(&out, (ARG_COUNT(ht) == 3) ? out_opts : 0); if(return_value_used) { xRequest = XMLRPC_RequestNew(); if(xRequest) { XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out); if(method->type == IS_NULL) { XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response); } else { XMLRPC_RequestSetMethodName(xRequest, method->value.str.val); XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call); } if(vals->type != IS_NULL) { XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(vals)); } outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0); if(outBuf) { RETVAL_STRING(outBuf, 1); free(outBuf); } XMLRPC_RequestFree(xRequest, 1); } } } /* {{{ proto string xmlrpc_encode(mixed value) generate xml for a PHP value */ PHP_FUNCTION(xmlrpc_encode) { XMLRPC_VALUE xOut = NULL; pval* arg1, *out_opts; php_output_options out; char* outBuf; if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } if( return_value_used ) { /* convert native php type to xmlrpc type */ xOut = PHP_to_XMLRPC(arg1); /* generate raw xml from xmlrpc data */ outBuf = XMLRPC_VALUE_ToXML(xOut, 0); if(xOut) { if(outBuf) { RETVAL_STRING(outBuf, 1); free(outBuf); } /* cleanup */ XMLRPC_CleanupValue(xOut); } } } /* }}} */ pval* decode_request_worker(pval* xml_in, pval* encoding_in, pval* method_name_out) { pval* retval = NULL; XMLRPC_REQUEST response; STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {0}; opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in->value.str.val) : ENCODING_DEFAULT; /* generate XMLRPC_REQUEST from raw xml */ response = XMLRPC_REQUEST_FromXML(xml_in->value.str.val, xml_in->value.str.len, &opts); if(response) { /* convert xmlrpc data to native php types */ retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response)); if(XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) { if(method_name_out) { convert_to_string(method_name_out); method_name_out->type = IS_STRING; method_name_out->value.str.val = estrdup(XMLRPC_RequestGetMethodName(response)); method_name_out->value.str.len = strlen(method_name_out->value.str.val); } } /* dust, sweep, and mop */ XMLRPC_RequestFree(response, 1); } return retval; } /* {{{ proto array xmlrpc_decode_request(string xml, string& method, [string encoding]) decode xml into native php types */ PHP_FUNCTION(xmlrpc_decode_request) { pval* xml, *method, *encoding = NULL; if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &xml, &method, &encoding) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } #if ZEND_MODULE_API_NO < 20010901 if (!ParameterPassedByReference(ht,2)) { zend_error(E_WARNING,"second argument to xmlrpc_decode_request() passed by value, expecting reference"); } #endif convert_to_string(xml); convert_to_string(method); if(ARG_COUNT(ht) == 3) { convert_to_string(encoding); } if(return_value_used) { pval* retval = decode_request_worker(xml, encoding, method); if(retval) { *return_value = *retval; zval_copy_ctor(return_value); } } } /* }}} */ /* {{{ proto array xmlrpc_decode(string xml, [string encoding]) decode xml into native php types */ PHP_FUNCTION(xmlrpc_decode) { pval* arg1, *arg2 = NULL; if( !(ARG_COUNT(ht) == 1 || ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg1, &arg2) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } convert_to_string(arg1); if(ARG_COUNT(ht) == 2) { convert_to_string(arg2); } if(return_value_used) { pval* retval = decode_request_worker(arg1, arg2, NULL); if(retval) { *return_value = *retval; zval_copy_ctor(return_value); } } } /* }}} */ /************************* * server related methods * *************************/ /* {{{ proto handle xmlrpc_server_create() create an xmlrpc server */ PHP_FUNCTION(xmlrpc_server_create) { if(ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } if(return_value_used) { pval *method_map, *introspection_map; MAKE_STD_ZVAL(method_map); MAKE_STD_ZVAL(introspection_map); if(array_init(method_map) == SUCCESS && array_init(introspection_map) == SUCCESS) { /* allocate server data. free'd in destroy_server_data() */ xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data)); if(server) { server->method_map = method_map; server->introspection_map = introspection_map; server->server_ptr = XMLRPC_ServerCreate(); XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback); /* store for later use */ ZEND_REGISTER_RESOURCE(return_value,server, XMLRPCG(le_xmlrpc_server)); } } } } /* {{{ proto void xmlrpc_server_destroy(handle server) destroy server resources */ PHP_FUNCTION(xmlrpc_server_destroy) { pval* arg1; int bSuccess = FAILURE; if(ARG_COUNT(ht) != 1 || getParameters(ht, 1, &arg1) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } if(arg1->type == IS_RESOURCE) { int type; xmlrpc_server_data *server = zend_list_find(arg1->value.lval, &type); if(server && type == XMLRPCG(le_xmlrpc_server)) { bSuccess = zend_list_delete(arg1->value.lval); /* called by hashtable destructor * destroy_server_data(server); */ } } RETVAL_LONG(bSuccess == SUCCESS); } /* called by xmlrpc C engine as method handler for all registered methods. * it then calls the corresponding PHP function to handle the method. */ static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data) { pval *retval_ptr; xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data; pval* xmlrpc_params; pval* callback_params[3]; /* convert xmlrpc to native php types */ xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest)); /* setup data hoojum */ callback_params[0] = pData->xmlrpc_method; callback_params[1] = xmlrpc_params; callback_params[2] = pData->caller_params; /* Use same C function for all methods */ /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */ call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params); pData->php_executed = 1; } /* called by the C server when it first receives an introspection request. We pass this on to * our PHP listeners, if any */ static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) { pval *retval_ptr, **php_function; pval* callback_params[1]; xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data; MAKE_STD_ZVAL(retval_ptr); retval_ptr->type = IS_NULL; /* setup data hoojum */ callback_params[0] = pData->caller_params; /* loop through and call all registered callbacks */ zend_hash_internal_pointer_reset(pData->server->introspection_map->value.ht); while(1) { if(zend_hash_get_current_data(pData->server->introspection_map->value.ht, (void**)&php_function) == SUCCESS) { /* php func prototype: function string user_func($user_params) */ if(call_user_function(CG(function_table), NULL, *php_function, retval_ptr, 1, callback_params) == SUCCESS) { XMLRPC_VALUE xData; STRUCT_XMLRPC_ERROR err = {0}; /* return value should be a string */ convert_to_string(retval_ptr); xData = XMLRPC_IntrospectionCreateDescription(retval_ptr->value.str.val, &err); if(xData) { if(!XMLRPC_ServerAddIntrospectionData(server, xData)) { zend_error(E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", (*php_function)->value.str.val); } XMLRPC_CleanupValue(xData); } else { /* could not create description */ if(err.xml_elem_error.parser_code) { zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to add introspection data returned from %s()", err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, (*php_function)->value.str.val); } else { zend_error(E_WARNING, "Unable to add introspection data returned from %s()", (*php_function)->value.str.val); } } } else { /* user func failed */ zend_error(E_WARNING, "Error calling user introspection callback: %s()", (*php_function)->value.str.val); } } else { break; } zend_hash_move_forward(pData->server->introspection_map->value.ht); } /* so we don't call the same callbacks ever again */ zend_hash_clean(pData->server->introspection_map->value.ht); } /* {{{ proto boolean xmlrpc_server_register_method(handle server, string method_name, string function) register a php function to handle method matching method_name */ PHP_FUNCTION(xmlrpc_server_register_method) { pval* method_key, *method_name, *handle, *method_name_save; int type; xmlrpc_server_data* server; /* get some params. should be 3 */ if( !(ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &handle, &method_key, &method_name) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } server = zend_list_find(handle->value.lval, &type); if(type == XMLRPCG(le_xmlrpc_server)) { /* register with C engine. every method just calls our standard callback, * and it then dispatches to php as necessary */ if(XMLRPC_ServerRegisterMethod(server->server_ptr, method_key->value.str.val, php_xmlrpc_callback)) { /* save for later use */ MAKE_STD_ZVAL(method_name_save); *method_name_save = *method_name; zval_copy_ctor(method_name_save); /* register our php method */ add_pval(server->method_map, method_key->value.str.val, &method_name_save); RETURN_BOOL(1); } } RETURN_BOOL(0); } /* {{{ proto boolean xmlrpc_server_register_introspection_callback(handle server, string function) register a php function to generate documentation */ PHP_FUNCTION(xmlrpc_server_register_introspection_callback) { pval* method_key, *method_name, *handle, *method_name_save; int type; xmlrpc_server_data* server; /* get some params. should be 2 */ if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &method_name) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } server = zend_list_find(handle->value.lval, &type); if(type == XMLRPCG(le_xmlrpc_server)) { { /* save for later use */ MAKE_STD_ZVAL(method_name_save); *method_name_save = *method_name; zval_copy_ctor(method_name_save); /* register our php method */ add_pval(server->introspection_map, NULL, &method_name_save); RETURN_BOOL(1); } } RETURN_BOOL(0); } /* this function is itchin for a re-write */ /* {{{ proto mixed xmlrpc_server_call_method(handle server, string xml, mixed user_data, [array output_options]) parse xml request and call method */ PHP_FUNCTION(xmlrpc_server_call_method) { xmlrpc_callback_data data = {0}; XMLRPC_REQUEST xRequest; STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts; xmlrpc_server_data* server; pval *rawxml, *caller_params, *handle, *output_opts; int type; php_output_options out; /* get params. 3 or 4 params ok */ if(ARG_COUNT(ht) == 4) { if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params, &output_opts) != SUCCESS) { WRONG_PARAM_COUNT; } /* user output options */ set_output_options(&out, output_opts); } else if(ARG_COUNT(ht) == 3) { if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params) != SUCCESS) { WRONG_PARAM_COUNT; } /* user output options */ set_output_options(&out, NULL); } else { WRONG_PARAM_COUNT; } server = zend_list_find(handle->value.lval, &type); if(type == XMLRPCG(le_xmlrpc_server)) { /* HACK: use output encoding for now */ input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding); /* generate an XMLRPC_REQUEST from the raw xml input */ xRequest = XMLRPC_REQUEST_FromXML(rawxml->value.str.val, rawxml->value.str.len, &input_opts); if(xRequest) { /* check if we have a method name -- indicating success and all manner of good things */ if(XMLRPC_RequestGetMethodName(xRequest)) { pval** php_function, *returned = NULL; XMLRPC_VALUE xAnswer = NULL; MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */ MAKE_STD_ZVAL(data.return_data); data.return_data->type = IS_NULL; /* in case value is never init'd, we don't dtor to think it is a string or something */ data.xmlrpc_method->type = IS_NULL; /* setup some data to pass to the callback function */ data.xmlrpc_method->value.str.val = estrdup(XMLRPC_RequestGetMethodName(xRequest)); data.xmlrpc_method->value.str.len = strlen(data.xmlrpc_method->value.str.val); data.xmlrpc_method->type = IS_STRING; data.caller_params = caller_params; data.php_executed = 0; data.server = server; /* check if the called method has been previous registered */ if(zend_hash_find(server->method_map->value.ht, data.xmlrpc_method->value.str.val, data.xmlrpc_method->value.str.len + 1, (void**)&php_function) == SUCCESS) { data.php_function = *php_function; } /* We could just call the php method directly ourselves at this point, but we do this * with a C callback in case the xmlrpc library ever implements some cool usage stats, * or somesuch. */ xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data); if(xAnswer) { if(out.b_php_out) { zval_dtor(data.return_data); FREE_ZVAL(data.return_data); data.return_data = XMLRPC_to_PHP(xAnswer); } } else if(data.php_executed) { if(!out.b_php_out) { xAnswer = PHP_to_XMLRPC(data.return_data); } } /* should we return data as xml? */ if(!out.b_php_out) { XMLRPC_REQUEST xResponse = XMLRPC_RequestNew(); if(xResponse) { char* outBuf = 0; int buf_len = 0; /* set some required request hoojum */ XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out); XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response); XMLRPC_RequestSetData(xResponse, xAnswer); /* generate xml */ outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len); if(outBuf) { RETVAL_STRINGL(outBuf, buf_len, 1); free(outBuf); } /* cleanup after ourselves. what a sty! */ XMLRPC_RequestFree(xResponse, 0); } } /* or as native php types? */ else { *return_value = *data.return_data; zval_copy_ctor(return_value); } /* cleanup after ourselves. what a sty! */ zval_dtor(data.xmlrpc_method); FREE_ZVAL(data.xmlrpc_method); zval_dtor(data.return_data); FREE_ZVAL(data.return_data); if(xAnswer) { XMLRPC_CleanupValue(xAnswer); } } XMLRPC_RequestFree(xRequest, 1); } } } /* {{{ proto int xmlrpc_server_add_introspection_data(handle server, array desc) add introspection documentation */ PHP_FUNCTION(xmlrpc_server_add_introspection_data) { pval *method, *handle, *desc; int type; xmlrpc_server_data* server; /* get some params. should be 2 */ if ( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &desc) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } server = zend_list_find(handle->value.lval, &type); if (type == XMLRPCG(le_xmlrpc_server)) { XMLRPC_VALUE xDesc = PHP_to_XMLRPC(desc); if (xDesc) { int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc); XMLRPC_CleanupValue(xDesc); RETURN_LONG(retval); } } RETURN_LONG(0); } /* {{{ proto array xmlrpc_parse_method_descriptions(string xml) decode xml into a list of method descriptions */ PHP_FUNCTION(xmlrpc_parse_method_descriptions) { pval* arg1, *arg2, *retval; if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } convert_to_string(arg1); if(return_value_used) { STRUCT_XMLRPC_ERROR err = {0}; XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1->value.str.val, &err); if(xVal) { retval = XMLRPC_to_PHP(xVal); if(retval) { *return_value = *retval; zval_copy_ctor(return_value); } /* dust, sweep, and mop */ XMLRPC_CleanupValue(xVal); } else { /* could not create description */ if(err.xml_elem_error.parser_code) { zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to create introspection data", err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error); } else { zend_error(E_WARNING, "Invalid xml structure. Unable to create introspection data"); } zend_error(E_WARNING, "xml parse error. no method description created"); } } } /************ * type data * ************/ #define XMLRPC_TYPE_COUNT 9 #define XMLRPC_VECTOR_TYPE_COUNT 4 #define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT) /* return a string matching a given xmlrpc type */ static const char** get_type_str_mapping() { static const char* str_mapping[TYPE_STR_MAP_SIZE]; static int first = 1; if(first) { /* warning. do not add/delete without changing size define */ str_mapping[xmlrpc_none] = "none"; str_mapping[xmlrpc_empty] = "empty"; str_mapping[xmlrpc_base64] = "base64"; str_mapping[xmlrpc_boolean] = "boolean"; str_mapping[xmlrpc_datetime] = "datetime"; str_mapping[xmlrpc_double] = "double"; str_mapping[xmlrpc_int] = "int"; str_mapping[xmlrpc_string] = "string"; str_mapping[xmlrpc_vector] = "vector"; str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none"; str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array"; str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed"; str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct"; first = 0; } return (const char**)str_mapping; } /* map an xmlrpc type to a string */ const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) { const char** str_mapping = get_type_str_mapping(); if(vtype == xmlrpc_vector_none) { return str_mapping[type]; } else { return str_mapping[XMLRPC_TYPE_COUNT + vtype]; } } /* map a string to an xmlrpc type */ XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) { const char** str_mapping = get_type_str_mapping(); int i; if(str) { for(i = 0; i < XMLRPC_TYPE_COUNT; i++) { if(!strcmp(str_mapping[i], str)) { return (XMLRPC_VALUE_TYPE)i; } } } return xmlrpc_none; } /* map a string to an xmlrpc vector type */ XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) { const char** str_mapping = get_type_str_mapping(); int i; if(str) { for(i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) { if(!strcmp(str_mapping[i], str)) { return (XMLRPC_VECTOR_TYPE)(i - XMLRPC_TYPE_COUNT); } } } return xmlrpc_none; } /* set a given value to a particular type. * note: this only works on strings, and only for date and base64, * which do not have native php types. black magic lies herein. */ int set_pval_xmlrpc_type(pval* value, XMLRPC_VALUE_TYPE type) { int bSuccess = FAILURE; /* we only really care about strings because they can represent * base64 and datetime. all other types have corresponding php types */ if(value->type == IS_STRING) { if(type == xmlrpc_base64 || type == xmlrpc_datetime) { const char* typestr = xmlrpc_type_as_str(type, xmlrpc_vector_none); pval* type; MAKE_STD_ZVAL(type); type->type = IS_STRING; type->value.str.val = estrdup(typestr); type->value.str.len = strlen(typestr); convert_to_object(value); bSuccess = zend_hash_update(value->value.obj.properties, OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL); } } return bSuccess; } /* return xmlrpc type of a php value */ XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue) { XMLRPC_VALUE_TYPE type = xmlrpc_none; if(value) { switch(value->type) { case IS_NULL: type = xmlrpc_base64; break; #ifndef BOOL_AS_LONG /* Right thing to do, but it breaks some legacy code. */ case IS_BOOL: type = xmlrpc_boolean; break; #else case IS_BOOL: #endif case IS_LONG: case IS_RESOURCE: type = xmlrpc_int; break; case IS_DOUBLE: type = xmlrpc_double; break; case IS_CONSTANT: type = xmlrpc_string; break; case IS_STRING: type = xmlrpc_string; break; case IS_ARRAY: case IS_CONSTANT_ARRAY: type = xmlrpc_vector; break; case IS_OBJECT: { pval** attr; type = xmlrpc_vector; if(zend_hash_find(value->value.obj.properties, OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**)&attr) == SUCCESS) { if((*attr)->type == IS_STRING) { type = xmlrpc_str_as_type((*attr)->value.str.val); } } break; } } /* if requested, return an unmolested (magic removed) copy of the value */ if(newvalue) { pval** val; if( (type == xmlrpc_base64 && value->type != IS_NULL) || type == xmlrpc_datetime) { if(zend_hash_find(value->value.obj.properties, OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), (void**)&val) == SUCCESS) { *newvalue = *val; } } else { *newvalue = value; } } } return type; } /* {{{ proto bool xmlrpc_set_type(string value, string type) set xmlrpc type, base64 or datetime, for a php string value */ PHP_FUNCTION(xmlrpc_set_type) { pval* arg, *type; XMLRPC_VALUE_TYPE vtype; if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg, &type) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } #if ZEND_MODULE_API_NO < 20010901 if (!ParameterPassedByReference(ht,1)) { zend_error(E_WARNING,"first argument to xmlrpc_set_type() passed by value, expecting reference"); } #endif convert_to_string(type); vtype = xmlrpc_str_as_type(type->value.str.val); if(vtype != xmlrpc_none) { if(set_pval_xmlrpc_type(arg, vtype) == SUCCESS) { RETURN_TRUE; } } else { zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type->value.str.val); } RETURN_FALSE; } /* {{{ proto string xmlrpc_get_type(mixed value) get xmlrpc type for a php value. especially useful for base64 and datetime strings */ PHP_FUNCTION(xmlrpc_get_type) { pval* arg; XMLRPC_VALUE_TYPE type; XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none; if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } type = get_pval_xmlrpc_type(arg, 0); if(type == xmlrpc_vector) { vtype = determine_vector_type(arg->value.ht); } RETURN_STRING((char*)xmlrpc_type_as_str(type, vtype), 1); } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ Index: php4/ext/xmlrpc/libxmlrpc/Makefile.in +++ php4/ext/xmlrpc/libxmlrpc/Makefile.in LTLIBRARY_NAME = libxmlrpc.la LTLIBRARY_SOURCES = base64.c simplestring.c xml_to_dandarpc.c \ xmlrpc_introspection.c encodings.c system_methods.c \ xml_to_xmlrpc.c queue.c xml_element.c xmlrpc.c DEFS = -DVERSION="0.41" include $(top_srcdir)/build/dynlib.mk Index: php4/ext/xmlrpc/libxmlrpc/acinclude.m4 +++ php4/ext/xmlrpc/libxmlrpc/acinclude.m4 # Local macros for automake & autoconf AC_DEFUN(XMLRPC_FUNCTION_CHECKS,[ # Standard XMLRPC list AC_CHECK_FUNCS( \ strtoul strtoull snprintf \ strstr strpbrk strerror\ memcpy memmove) ]) AC_DEFUN(XMLRPC_HEADER_CHECKS,[ AC_HEADER_STDC AC_CHECK_HEADERS(xmlparse.h xmltok.h stdlib.h strings.h string.h) ]) AC_DEFUN(XMLRPC_TYPE_CHECKS,[ AC_REQUIRE([AC_C_CONST]) AC_REQUIRE([AC_C_INLINE]) AC_CHECK_SIZEOF(char, 1) AC_CHECK_SIZEOF(int, 4) AC_CHECK_SIZEOF(long, 4) AC_CHECK_SIZEOF(long long, 8) AC_TYPE_SIZE_T AC_HEADER_TIME AC_TYPE_UID_T XMLRPC_CHECK_ULONG XMLRPC_CHECK_UCHAR XMLRPC_CHECK_UINT XMLRPC_CHECK_INT_8_16_32 XMLRPC_TYPE_QSORT ]) Index: php4/ext/xmlrpc/libxmlrpc/base64.c +++ php4/ext/xmlrpc/libxmlrpc/base64.c static const char rcsid[] = "#(@) $Id: base64.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; /* Encode or decode file as MIME base64 (RFC 1341) by John Walker http://www.fourmilab.ch/ This program is in the public domain. */ #include <stdio.h> /* ENCODE -- Encode binary file into base64. */ #include <stdlib.h> #include "base64.h" static unsigned char dtable[512]; void buffer_new(struct buffer_st *b) { b->length = 512; b->data = malloc(sizeof(char)*(b->length)); b->data[0] = 0; b->ptr = b->data; b->offset = 0; } void buffer_add(struct buffer_st *b, char c) { *(b->ptr++) = c; b->offset++; if (b->offset == b->length) { b->length += 512; b->data = realloc(b->data, b->length); b->ptr = b->data + b->offset; } } void buffer_delete(struct buffer_st *b) { free(b->data); b->length = 0; b->offset = 0; b->ptr = NULL; b->data = NULL; } void base64_encode(struct buffer_st *b, const char *source, int length) { int i, hiteof = 0; int offset = 0; int olen; char *dest; olen = 0; buffer_new(b); /* Fill dtable with character encodings. */ for (i = 0; i < 26; i++) { dtable[i] = 'A' + i; dtable[26 + i] = 'a' + i; } for (i = 0; i < 10; i++) { dtable[52 + i] = '0' + i; } dtable[62] = '+'; dtable[63] = '/'; while (!hiteof) { unsigned char igroup[3], ogroup[4]; int c, n; igroup[0] = igroup[1] = igroup[2] = 0; for (n = 0; n < 3; n++) { c = *(source++); offset++; if (offset > length) { hiteof = 1; break; } igroup[n] = (unsigned char) c; } if (n > 0) { ogroup[0] = dtable[igroup[0] >> 2]; ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)]; ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)]; ogroup[3] = dtable[igroup[2] & 0x3F]; /* Replace characters in output stream with "=" pad characters if fewer than three characters were read from the end of the input stream. */ if (n < 3) { ogroup[3] = '='; if (n < 2) { ogroup[2] = '='; } } for (i = 0; i < 4; i++) { buffer_add(b, ogroup[i]); if (!(b->offset % 72)) { // buffer_add(b, '\r'); buffer_add(b, '\n'); } } } } // buffer_add(b, '\r'); buffer_add(b, '\n'); } void base64_decode(struct buffer_st *bfr, const char *source, int length) { int i; int offset = 0; int endoffile; int count; buffer_new(bfr); for (i = 0; i < 255; i++) { dtable[i] = 0x80; } for (i = 'A'; i <= 'Z'; i++) { dtable[i] = 0 + (i - 'A'); } for (i = 'a'; i <= 'z'; i++) { dtable[i] = 26 + (i - 'a'); } for (i = '0'; i <= '9'; i++) { dtable[i] = 52 + (i - '0'); } dtable['+'] = 62; dtable['/'] = 63; dtable['='] = 0; endoffile = 0; /*CONSTANTCONDITION*/ while (1) { unsigned char a[4], b[4], o[3]; for (i = 0; i < 4; i++) { int c; while (1) { c = *(source++); offset++; if (offset > length) endoffile = 1; if (isspace(c) || c == '\n' || c == '\r') continue; break; } if (endoffile) { /* if (i > 0) { fprintf(stderr, "Input file incomplete.\n"); exit(1); } */ return; } if (dtable[c] & 0x80) { /* fprintf(stderr, "Offset %i length %i\n", offset, length); fprintf(stderr, "character '%c:%x:%c' in input file.\n", c, c, dtable[c]); exit(1); */ i--; continue; } a[i] = (unsigned char) c; b[i] = (unsigned char) dtable[c]; } o[0] = (b[0] << 2) | (b[1] >> 4); o[1] = (b[1] << 4) | (b[2] >> 2); o[2] = (b[2] << 6) | b[3]; i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3); count = 0; while (count < i) { buffer_add(bfr, o[count++]); } if (i < 3) { return; } } } Index: php4/ext/xmlrpc/libxmlrpc/base64.h +++ php4/ext/xmlrpc/libxmlrpc/base64.h /* Encode or decode file as MIME base64 (RFC 1341) by John Walker http://www.fourmilab.ch/ This program is in the public domain. */ struct buffer_st { char *data; int length; char *ptr; int offset; }; void buffer_new(struct buffer_st *b); void buffer_add(struct buffer_st *b, char c); void buffer_delete(struct buffer_st *b); void base64_encode(struct buffer_st *b, const char *source, int length); void base64_decode(struct buffer_st *b, const char *source, int length); /* #define DEBUG_MALLOC */ #ifdef DEBUG_MALLOC void *_malloc_real(size_t s, char *file, int line); void _free_real(void *p, char *file, int line); #define malloc(s) _malloc_real(s,__FILE__,__LINE__) #define free(p) _free_real(p, __FILE__,__LINE__) #endif Index: php4/ext/xmlrpc/libxmlrpc/encodings.c +++ php4/ext/xmlrpc/libxmlrpc/encodings.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ static const char rcsid[] = "#(@) $Id: encodings.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; #include <errno.h> #include <iconv.h> #include "encodings.h" static char* convert(const char* src, int src_len, int *new_len, const char* from_enc, const char* to_enc) { char* outbuf = 0; if(src && src_len && from_enc && to_enc) { int outlenleft = src_len; int outlen = src_len; int inlenleft = src_len; iconv_t ic = iconv_open(to_enc, from_enc); char* src_ptr = (char*)src; char* out_ptr = 0; if(ic != (iconv_t)-1) { size_t st; outbuf = (char*)malloc(outlen + 1); if(outbuf) { out_ptr = (char*)outbuf; while(inlenleft) { st = iconv(ic, &src_ptr, &inlenleft, &out_ptr, &outlenleft); if(st == -1) { if(errno == E2BIG) { int diff = out_ptr - outbuf; outlen += inlenleft; outlenleft += inlenleft; outbuf = (char*)realloc(outbuf, outlen + 1); if(!outbuf) { break; } out_ptr = outbuf + diff; } else { free(outbuf); outbuf = 0; break; } } } } iconv_close(ic); } outlen -= outlenleft; if(new_len) { *new_len = outbuf ? outlen : 0; } if(outbuf) { outbuf[outlen] = 0; } } return outbuf; } /* returns a new string that must be freed */ char* utf8_encode(const char *s, int len, int *newlen, const char* encoding) { return convert(s, len, newlen, encoding, "UTF-8"); } /* returns a new string, possibly decoded */ char* utf8_decode(const char *s, int len, int *newlen, const char* encoding) { return convert(s, len, newlen, "UTF-8", encoding); } Index: php4/ext/xmlrpc/libxmlrpc/encodings.h +++ php4/ext/xmlrpc/libxmlrpc/encodings.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef __ENCODINGS__H #define __ENCODINGS__H /* these defines are for legacy purposes. */ #define encoding_utf_8 "UTF-8" typedef const char* ENCODING_ID; #define utf8_get_encoding_id_string(desired_enc) ((const char*)desired_enc) #define utf8_get_encoding_id_from_string(id_string) ((ENCODING_ID)id_string) char* utf8_encode(const char *s, int len, int *newlen, ENCODING_ID encoding); char* utf8_decode(const char *s, int len, int *newlen, ENCODING_ID encoding); #endif /* __ENCODINGS__H */ Index: php4/ext/xmlrpc/libxmlrpc/libs.mk +++ php4/ext/xmlrpc/libxmlrpc/libs.mk include $(top_builddir)/config_vars.mk LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X) LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo) $(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES) $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD) targets = $(LTLIBRARY_NAME) Index: php4/ext/xmlrpc/libxmlrpc/queue.c +++ php4/ext/xmlrpc/libxmlrpc/queue.c static const char rcsid[] = "#(@) $Id: queue.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; /* * Date last modified: Jan 2001 * Modifications by Dan Libby (dan@libby.com), including: * - various fixes, null checks, etc * - addition of Q_Iter funcs, macros */ /*-************************************************************** * * File : q.c * * Author: Peter Yard [1993.01.02] -- 02 Jan 1993 * * Disclaimer: This code is released to the public domain. * * Description: * Generic double ended queue (Deque pronounced DEK) for handling * any data types, with sorting. * * By use of various functions in this module the caller * can create stacks, queues, lists, doubly linked lists, * sorted lists, indexed lists. All lists are dynamic. * * It is the responsibility of the caller to malloc and free * memory for insertion into the queue. A pointer to the object * is used so that not only can any data be used but various kinds * of data can be pushed on the same queue if one so wished e.g. * various length string literals mixed with pointers to structures * or integers etc. * * Enhancements: * A future improvement would be the option of multiple "cursors" * so that multiple locations could occur in the one queue to allow * placemarkers and additional flexibility. Perhaps even use queue * itself to have a list of cursors. * * Usage: * * /x init queue x/ * queue q; * Q_Init(&q); * * To create a stack : * * Q_PushHead(&q, &mydata1); /x push x/ * Q_PushHead(&q, &mydata2); * ..... * data_ptr = Q_PopHead(&q); /x pop x/ * ..... * data_ptr = Q_Head(&q); /x top of stack x/ * * To create a FIFO: * * Q_PushHead(&q, &mydata1); * ..... * data_ptr = Q_PopTail(&q); * * To create a double list: * * data_ptr = Q_Head(&q); * .... * data_ptr = Q_Next(&q); * data_ptr = Q_Tail(&q); * if (Q_IsEmpty(&q)) .... * ..... * data_ptr = Q_Previous(&q); * * To create a sorted list: * * Q_PushHead(&q, &mydata1); /x push x/ * Q_PushHead(&q, &mydata2); * ..... * if (!Q_Sort(&q, MyFunction)) * .. error .. * * /x fill in key field of mydata1. * * NB: Q_Find does linear search * x/ * * if (Q_Find(&q, &mydata1, MyFunction)) * { * /x found it, queue cursor now at correct record x/ * /x can retrieve with x/ * data_ptr = Q_Get(&q); * * /x alter data , write back with x/ * Q_Put(&q, data_ptr); * } * * /x Search with binary search x/ * if (Q_Seek(&q, &mydata, MyFunction)) * /x etc x/ * * ****************************************************************/ #include <stdlib.h> #include "queue.h" static void QuickSort(void *list[], int low, int high, int (*Comp)(const void *, const void *)); static int Q_BSearch(queue *q, void *key, int (*Comp)(const void *, const void *)); /* The index: a pointer to pointers */ static void **index; static datanode **posn_index; /*** * ** function : Q_Init * ** purpose : Initialise queue object and pointers. * ** parameters : 'queue' pointer. * ** returns : True_ if init successful else False_ * ** comments : ***/ int Q_Init(queue *q) { if(q) { q->head = q->tail = NULL; q->cursor = q->head; q->size = 0; q->sorted = False_; } return True_; } /*** * ** function : Q_AtHead * ** purpose : tests if cursor is at head of queue * ** parameters : 'queue' pointer. * ** returns : boolean - True_ is at head else False_ * ** comments : * ***/ int Q_AtHead(queue *q) { return(q && q->cursor == q->head); } /*** * ** function : Q_AtTail * ** purpose : boolean test if cursor at tail of queue * ** parameters : 'queue' pointer to test. * ** returns : True_ or False_ * ** comments : * ***/ int Q_AtTail(queue *q) { return(q && q->cursor == q->tail); } /*** * ** function : Q_IsEmpty * ** purpose : test if queue has nothing in it. * ** parameters : 'queue' pointer * ** returns : True_ if IsEmpty queue, else False_ * ** comments : * ***/ inline int Q_IsEmpty(queue *q) { return(!q || q->size == 0); } /*** * ** function : Q_Size * ** purpose : return the number of elements in the queue * ** parameters : queue pointer * ** returns : number of elements * ** comments : * ***/ int Q_Size(queue *q) { return q ? q->size : 0; } /*** * ** function : Q_Head * ** purpose : position queue cursor to first element (head) of queue. * ** parameters : 'queue' pointer * ** returns : pointer to data at head. If queue is IsEmpty returns NULL * ** comments : * ***/ void *Q_Head(queue *q) { if(Q_IsEmpty(q)) return NULL; q->cursor = q->head; return q->cursor->data; } /*** * ** function : Q_Tail * ** purpose : locate cursor at tail of queue. * ** parameters : 'queue' pointer * ** returns : pointer to data at tail , if queue IsEmpty returns NULL * ** comments : * ***/ void *Q_Tail(queue *q) { if(Q_IsEmpty(q)) return NULL; q->cursor = q->tail; return q->cursor->data; } /*** * ** function : Q_PushHead * ** purpose : put a data pointer at the head of the queue * ** parameters : 'queue' pointer, void pointer to the data. * ** returns : True_ if success else False_ if unable to push data. * ** comments : * ***/ int Q_PushHead(queue *q, void *d) { if(q && d) { node *n; datanode *p; p = malloc(sizeof(datanode)); if(p == NULL) return False_; n = q->head; q->head = (node*)p; q->head->prev = NULL; if(q->size == 0) { q->head->next = NULL; q->tail = q->head; } else { q->head->next = (datanode*)n; n->prev = q->head; } q->head->data = d; q->size++; q->cursor = q->head; q->sorted = False_; return True_; } return False_; } /*** * ** function : Q_PushTail * ** purpose : put a data element pointer at the tail of the queue * ** parameters : queue pointer, pointer to the data * ** returns : True_ if data pushed, False_ if data not inserted. * ** comments : * ***/ int Q_PushTail(queue *q, void *d) { if(q && d) { node *p; datanode *n; n = malloc(sizeof(datanode)); if(n == NULL) return False_; p = q->tail; q->tail = (node *)n; if(q->size == 0) { q->tail->prev = NULL; q->head = q->tail; } else { q->tail->prev = (datanode *)p; p->next = q->tail; } q->tail->next = NULL; q->tail->data = d; q->cursor = q->tail; q->size++; q->sorted = False_; return True_; } } /*** * ** function : Q_PopHead * ** purpose : remove and return the top element at the head of the * queue. * ** parameters : queue pointer * ** returns : pointer to data element or NULL if queue is IsEmpty. * ** comments : * ***/ void *Q_PopHead(queue *q) { datanode *n; void *d; if(Q_IsEmpty(q)) return NULL; d = q->head->data; n = q->head->next; free(q->head); q->size--; if(q->size == 0) q->head = q->tail = q->cursor = NULL; else { q->head = (node *)n; q->head->prev = NULL; q->cursor = q->head; } q->sorted = False_; return d; } /*** * ** function : Q_PopTail * ** purpose : remove element from tail of queue and return data. * ** parameters : queue pointer * ** returns : pointer to data element that was at tail. NULL if queue * IsEmpty. * ** comments : * ***/ void *Q_PopTail(queue *q) { datanode *p; void *d; if(Q_IsEmpty(q)) return NULL; d = q->tail->data; p = q->tail->prev; free(q->tail); q->size--; if(q->size == 0) q->head = q->tail = q->cursor = NULL; else { q->tail = (node *)p; q->tail->next = NULL; q->cursor = q->tail; } q->sorted = False_; return d; } /*** * ** function : Q_Next * ** purpose : Move to the next element in the queue without popping * ** parameters : queue pointer. * ** returns : pointer to data element of new element or NULL if end * of the queue. * ** comments : This uses the cursor for the current position. Q_Next * only moves in the direction from the head of the queue * to the tail. ***/ void *Q_Next(queue *q) { if(!q) return NULL; if(q->cursor->next == NULL) return NULL; q->cursor = (node *)q->cursor->next; return q->cursor->data ; } /*** * ** function : Q_Previous * ** purpose : Opposite of Q_Next. Move to next element closer to the * head of the queue. * ** parameters : pointer to queue * ** returns : pointer to data of new element else NULL if queue IsEmpty * ** comments : Makes cursor move towards the head of the queue. * ***/ void *Q_Previous(queue *q) { if(!q) return NULL; if(q->cursor->prev == NULL) return NULL; q->cursor = (node *)q->cursor->prev; return q->cursor->data; } void *Q_Iter_Del(queue *q, q_iter iter) { void *d; datanode *n, *p; if(!q) return NULL; if(iter == NULL) return NULL; if(iter == (q_iter)q->head) return Q_PopHead(q); if(iter == (q_iter)q->tail) return Q_PopTail(q); n = ((node*)iter)->next; p = ((node*)iter)->prev; d = ((node*)iter)->data; free(iter); if(p) { p->next = n; } if (q->cursor == (node*)iter) { if (p) { q->cursor = p; } else { q->cursor = n; } } if (n != NULL) { n->prev = p; } q->size--; q->sorted = False_; return d; } /*** * ** function : Q_DelCur * ** purpose : Delete the current queue element as pointed to by * the cursor. * ** parameters : queue pointer * ** returns : pointer to data element. * ** comments : WARNING! It is the responsibility of the caller to * free any memory. Queue cannot distinguish between * pointers to literals and malloced memory. * ***/ void *Q_DelCur(queue* q) { if(q) { return Q_Iter_Del(q, (q_iter)q->cursor); } return 0; } /*** * ** function : Q_Destroy * ** purpose : Free all queue resources * ** parameters : queue pointer * ** returns : null. * ** comments : WARNING! It is the responsibility of the caller to * free any memory. Queue cannot distinguish between * pointers to literals and malloced memory. * ***/ void Q_Destroy(queue *q) { while(!Q_IsEmpty(q)) { Q_PopHead(q); } } /*** * ** function : Q_Get * ** purpose : get the pointer to the data at the cursor location * ** parameters : queue pointer * ** returns : data element pointer * ** comments : * ***/ void *Q_Get(queue *q) { if(!q) return NULL; if(q->cursor == NULL) return NULL; return q->cursor->data; } /*** * ** function : Q_Put * ** purpose : replace pointer to data with new pointer to data. * ** parameters : queue pointer, data pointer * ** returns : boolean- True_ if successful, False_ if cursor at NULL * ** comments : * ***/ int Q_Put(queue *q, void *data) { if(q && data) { if(q->cursor == NULL) return False_; q->cursor->data = data; return True_; } return False_; } /*** * ** function : Q_Find * ** purpose : Linear search of queue for match with key in *data * ** parameters : queue pointer q, data pointer with data containing key * comparison function here called Comp. * ** returns : True_ if found , False_ if not in queue. * ** comments : Useful for small queues that are constantly changing * and would otherwise need constant sorting with the * Q_Seek function. * For description of Comp see Q_Sort. * Queue cursor left on position found item else at end. * ***/ int Q_Find(queue *q, void *data, int (*Comp)(const void *, const void *)) { void *d; if (q == NULL) { return False_; } d = Q_Head(q); do { if(Comp(d, data) == 0) return True_; d = Q_Next(q); } while(!Q_AtTail(q)); if(Comp(d, data) == 0) return True_; return False_; } /*======== Sorted Queue and Index functions ========= */ static void QuickSort(void *list[], int low, int high, int (*Comp)(const void *, const void *)) { int flag = 1, i, j; void *key, *temp; if(low < high) { i = low; j = high + 1; key = list[ low ]; while(flag) { i++; while(Comp(list[i], key) < 0) i++; j--; while(Comp(list[j], key) > 0) j--; if(i < j) { temp = list[i]; list[i] = list[j]; list[j] = temp; } else flag = 0; } temp = list[low]; list[low] = list[j]; list[j] = temp; QuickSort(list, low, j-1, Comp); QuickSort(list, j+1, high, Comp); } } /*** * ** function : Q_Sort * ** purpose : sort the queue and allow index style access. * ** parameters : queue pointer, comparison function compatible with * with 'qsort'. * ** returns : True_ if sort succeeded. False_ if error occurred. * ** comments : Comp function supplied by caller must return * -1 if data1 < data2 * 0 if data1 == data2 * +1 if data1 > data2 * * for Comp(data1, data2) * * If queue is already sorted it frees the memory of the * old index and starts again. * ***/ int Q_Sort(queue *q, int (*Comp)(const void *, const void *)) { int i; void *d; datanode *dn; /* if already sorted free memory for tag array */ if(q->sorted) { free(index); free(posn_index); q->sorted = False_; } /* Now allocate memory of array, array of pointers */ index = malloc(q->size * sizeof(q->cursor->data)); if(index == NULL) return False_; posn_index = malloc(q->size * sizeof(q->cursor)); if(posn_index == NULL) { free(index); return False_; } /* Walk queue putting pointers into array */ d = Q_Head(q); for(i=0; i < q->size; i++) { index[i] = d; posn_index[i] = q->cursor; d = Q_Next(q); } /* Now sort the index */ QuickSort(index, 0, q->size - 1, Comp); /* Rearrange the actual queue into correct order */ dn = q->head; i = 0; while(dn != NULL) { dn->data = index[i++]; dn = dn->next; } /* Re-position to original element */ if(d != NULL) Q_Find(q, d, Comp); else Q_Head(q); q->sorted = True_; return True_; } /*** * ** function : Q_BSearch * ** purpose : binary search of queue index for node containing key * ** parameters : queue pointer 'q', data pointer of key 'key', * Comp comparison function. * ** returns : integer index into array of node pointers, * or -1 if not found. * ** comments : see Q_Sort for description of 'Comp' function. * ***/ static int Q_BSearch( queue *q, void *key, int (*Comp)(const void *, const void*)) { int low, mid, hi, val; low = 0; hi = q->size - 1; while(low <= hi) { mid = (low + hi) / 2; val = Comp(key, index[ mid ]); if(val < 0) hi = mid - 1; else if(val > 0) low = mid + 1; else /* Success */ return mid; } /* Not Found */ return -1; } /*** * ** function : Q_Seek * ** purpose : use index to locate data according to key in 'data' * ** parameters : queue pointer 'q', data pointer 'data', Comp comparison * function. * ** returns : pointer to data or NULL if could not find it or could * not sort queue. * ** comments : see Q_Sort for description of 'Comp' function. * ***/ void *Q_Seek(queue *q, void *data, int (*Comp)(const void *, const void *)) { int idx; if (q == NULL) { return NULL; } if(!q->sorted) { if(!Q_Sort(q, Comp)) return NULL; } idx = Q_BSearch(q, data, Comp); if(idx < 0) return NULL; q->cursor = posn_index[idx]; return index[idx]; } /*** * ** function : Q_Insert * ** purpose : Insert an element into an indexed queue * ** parameters : queue pointer 'q', data pointer 'data', Comp comparison * function. * ** returns : pointer to data or NULL if could not find it or could * not sort queue. * ** comments : see Q_Sort for description of 'Comp' function. * WARNING! This code can be very slow since each new * element means a new Q_Sort. Should only be used for * the insertion of the odd element ,not the piecemeal * building of an entire queue. ***/ int Q_Insert(queue *q, void *data, int (*Comp)(const void *, const void *)) { if (q == NULL) { return False_; } Q_PushHead(q, data); if(!Q_Sort(q, Comp)) return False_; return True_; } /* read only funcs for iterating through queue. above funcs modify queue */ q_iter Q_Iter_Head(queue *q) { return q ? (q_iter)q->head : NULL; } q_iter Q_Iter_Tail(queue *q) { return q ? (q_iter)q->tail : NULL; } q_iter Q_Iter_Next(q_iter qi) { return qi ? (q_iter)((node*)qi)->next : NULL; } q_iter Q_Iter_Prev(q_iter qi) { return qi ? (q_iter)((node*)qi)->prev : NULL; } void * Q_Iter_Get(q_iter qi) { return qi ? ((node*)qi)->data : NULL; } int Q_Iter_Put(q_iter qi, void* data) { if(qi) { ((node*)qi)->data = data; return True_; } return False_; } Index: php4/ext/xmlrpc/libxmlrpc/queue.h +++ php4/ext/xmlrpc/libxmlrpc/queue.h /* * Date last modified: Jan 2001 * Modifications by Dan Libby (dan@libby.com), including: * - various fixes, null checks, etc * - addition of Q_Iter funcs, macros */ /* * File : q.h * * Peter Yard 02 Jan 1993. * * Disclaimer: This code is released to the public domain. */ #ifndef Q__H #define Q__H #ifndef False_ #define False_ 0 #endif #ifndef True_ #define True_ 1 #endif typedef struct nodeptr datanode; typedef struct nodeptr { void *data ; datanode *prev, *next ; } node ; /* For external use with Q_Iter* funcs */ typedef struct nodeptr* q_iter; typedef struct { node *head, *tail, *cursor; int size, sorted, item_deleted; } queue; typedef struct { void *dataptr; node *loc ; } index_elt ; int Q_Init(queue *q); void Q_Destroy(queue *q); int Q_IsEmpty(queue *q); int Q_Size(queue *q); int Q_AtHead(queue *q); int Q_AtTail(queue *q); int Q_PushHead(queue *q, void *d); int Q_PushTail(queue *q, void *d); void *Q_Head(queue *q); void *Q_Tail(queue *q); void *Q_PopHead(queue *q); void *Q_PopTail(queue *q); void *Q_Next(queue *q); void *Q_Previous(queue *q); void *Q_DelCur(queue *q); void *Q_Get(queue *q); int Q_Put(queue *q, void *data); int Q_Sort(queue *q, int (*Comp)(const void *, const void *)); int Q_Find(queue *q, void *data, int (*Comp)(const void *, const void *)); void *Q_Seek(queue *q, void *data, int (*Comp)(const void *, const void *)); int Q_Insert(queue *q, void *data, int (*Comp)(const void *, const void *)); /* read only funcs for iterating through queue. above funcs modify queue */ q_iter Q_Iter_Head(queue *q); q_iter Q_Iter_Tail(queue *q); q_iter Q_Iter_Next(q_iter qi); q_iter Q_Iter_Prev(q_iter qi); void* Q_Iter_Get(q_iter qi); int Q_Iter_Put(q_iter qi, void* data); // not read only! here for completeness. void* Q_Iter_Del(queue *q, q_iter iter); // not read only! here for completeness. /* Fast (macro'd) versions of above */ #define Q_Iter_Head_F(q) (q ? (q_iter)((queue*)q)->head : NULL) #define Q_Iter_Tail_F(q) (q ? (q_iter)((queue*)q)->tail : NULL) #define Q_Iter_Next_F(qi) (qi ? (q_iter)((node*)qi)->next : NULL) #define Q_Iter_Prev_F(qi) (qi ? (q_iter)((node*)qi)->prev : NULL) #define Q_Iter_Get_F(qi) (qi ? ((node*)qi)->data : NULL) #endif /* Q__H */ Index: php4/ext/xmlrpc/libxmlrpc/simplestring.c +++ php4/ext/xmlrpc/libxmlrpc/simplestring.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ static const char rcsid[] = "#(@) $Id: simplestring.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; #define SIMPLESTRING_INCR 32 /****h* ABOUT/simplestring * NAME * simplestring * AUTHOR * Dan Libby, aka danda (dan@libby.com) * CREATION DATE * 06/2000 * HISTORY * 10/15/2000 -- danda -- adding robodoc documentation * PORTABILITY * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just * about anything with minor mods. * NOTES * This code was written primarily for xmlrpc, but has found some other uses. * * simplestring is, as the name implies, a simple API for dealing with C strings. * Why would I write yet another string API? Because I couldn't find any that were * a) free / GPL, b) simple/lightweight, c) fast, not doing unneccesary strlens all * over the place. So. It is simple, and it seems to work, and it is pretty fast. * * Oh, and it is also binary safe, ie it can handle strings with embedded NULLs, * so long as the real length is passed in. * * And the masses rejoiced. * * BUGS * there must be some. ******/ #include "simplestring.h" #define my_free(thing) if(thing) {free(thing); thing = 0;} /*----------------------** * Begin String Functions * *-----------------------*/ /****f* FUNC/simplestring_init * NAME * simplestring_init * SYNOPSIS * void simplestring_init(simplestring* string) * FUNCTION * initialize string * INPUTS * string - pointer to a simplestring struct that will be initialized * RESULT * void * NOTES * SEE ALSO * simplestring_free () * simplestring_clear () * SOURCE */ void simplestring_init(simplestring* string) { memset(string, 0, sizeof(simplestring)); } /******/ static void simplestring_init_str(simplestring* string) { string->str = (char*)malloc(SIMPLESTRING_INCR); if(string->str) { string->str[0] = 0; string->len = 0; string->size = SIMPLESTRING_INCR; } else { string->size = 0; } } /****f* FUNC/simplestring_clear * NAME * simplestring_clear * SYNOPSIS * void simplestring_clear(simplestring* string) * FUNCTION * clears contents of a string * INPUTS * string - the string value to clear * RESULT * void * NOTES * This function is very fast as it does not de-allocate any memory. * SEE ALSO * * SOURCE */ void simplestring_clear(simplestring* string) { if(string->str) { string->str[0] = 0; } string->len = 0; } /******/ /****f* FUNC/simplestring_free * NAME * simplestring_free * SYNOPSIS * void simplestring_free(simplestring* string) * FUNCTION * frees contents of a string, if any. Does *not* free the simplestring struct itself. * INPUTS * string - value containing string to be free'd * RESULT * void * NOTES * caller is responsible for allocating and freeing simplestring* struct itself. * SEE ALSO * simplestring_init () * SOURCE */ void simplestring_free(simplestring* string) { if(string && string->str) { my_free(string->str); string->len = 0; } } /******/ /****f* FUNC/simplestring_addn * NAME * simplestring_addn * SYNOPSIS * void simplestring_addn(simplestring* string, const char* add, int add_len) * FUNCTION * copies n characters from source to target string * INPUTS * target - target string * source - source string * add_len - number of characters to copy * RESULT * void * NOTES * SEE ALSO * simplestring_add () * SOURCE */ void simplestring_addn(simplestring* target, const char* source, int add_len) { if(target && source) { if(!target->str) { simplestring_init_str(target); } if(target->len + add_len + 1 > target->size) { /* newsize is current length + new length */ int newsize = target->len + add_len + 1; int incr = target->size * 2; /* align to SIMPLESTRING_INCR increments */ newsize = newsize - (newsize % incr) + incr; target->str = (char*)realloc(target->str, newsize); target->size = target->str ? newsize : 0; } if(target->str) { if(add_len) { memcpy(target->str + target->len, source, add_len); } target->len += add_len; target->str[target->len] = 0; /* null terminate */ } } } /******/ /****f* FUNC/simplestring_add * NAME * simplestring_add * SYNOPSIS * void simplestring_add(simplestring* string, const char* add) * FUNCTION * appends a string of unknown length from source to target * INPUTS * target - the target string to append to * source - the source string of unknown length * RESULT * void * NOTES * SEE ALSO * simplestring_addn () * SOURCE */ void simplestring_add(simplestring* target, const char* source) { if(target && source) { simplestring_addn(target, source, strlen(source)); } } /******/ /*---------------------- * End String Functions * *--------------------**/ Index: php4/ext/xmlrpc/libxmlrpc/simplestring.h +++ php4/ext/xmlrpc/libxmlrpc/simplestring.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef __SIMPLESTRING_H__ #define __SIMPLESTRING_H__ /*-******************************** * begin simplestring header stuff * **********************************/ #ifdef __cplusplus extern "C" { #endif /****s* struct/simplestring * NAME * simplestring * NOTES * represents a string efficiently for fast appending, etc. * SOURCE */ typedef struct _simplestring { char* str; /* string buf */ int len; /* length of string/buf */ int size; /* size of allocated buffer */ } simplestring; /******/ #ifndef NULL #define NULL 0 #endif void simplestring_init(simplestring* string); void simplestring_clear(simplestring* string); void simplestring_free(simplestring* string); void simplestring_addn(simplestring* string, const char* add, int add_len); #ifdef __cplusplus } #endif /*-****************************** * end simplestring header stuff * ********************************/ #endif /* __SIMPLESTRING_H__ */ Index: php4/ext/xmlrpc/libxmlrpc/system_methods.c +++ php4/ext/xmlrpc/libxmlrpc/system_methods.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /****h* ABOUT/system_methods * AUTHOR * Dan Libby, aka danda (dan@libby.com) * HISTORY * 4/28/2001 -- danda -- adding system.multicall and separating out system methods. * TODO * NOTES *******/ #include "queue.h" #include "xmlrpc.h" #include "xmlrpc_private.h" #include "xmlrpc_introspection_private.h" #include "system_methods_private.h" #include <string.h> #include <stdlib.h> #include <stdarg.h> static const char* xsm_introspection_xml = "<?xml version='1.0' ?>" "<introspection version='1.0'>" "<typeList>" "<typeDescription name='system.value' basetype='struct' desc='description of a value'>" "<value type='string' name='name' optional='yes'>value identifier</value>" "<value type='string' name='type'>value's xmlrpc or user-defined type</value>" "<value type='string' name='description'>value's textual description</value> " "<value type='boolean' name='optional'>true if value is optional, else it is required</value> " "<value type='any' name='member' optional='yes'>a child of this element. n/a for scalar types</value> " "</typeDescription>" "<typeDescription name='system.valueList' basetype='array' desc='list of value descriptions'>" "<value type='system.value'/>" "</typeDescription>" "<typeDescription name='system.stringList' basetype='array' desc='list of strings'>" "<value type='string'/>" "</typeDescription>" "</typeList>" "<methodList>" "<!-- system.describeMethods -->" "<methodDescription name='system.describeMethods'>" "<author>Dan Libby</author>" "<purpose>fully describes the methods and types implemented by this XML-RPC server.</purpose>" "<version>1.1</version>" "<signatures>" "<signature>" "<params>" "<value type='array' name='methodList' optional='yes' desc='a list of methods to be described. if omitted, all are described.'>" "<value type='string'>a valid method name</value>" "</value>" "</params>" "<returns>" "<value type='struct' desc='contains methods list and types list'>" "<value type='array' name='methodList' desc='a list of methods'>" "<value type='struct' desc='representation of a single method'>" "<value type='string' name='name'>method name</value>" "<value type='string' name='version' optional='yes'>method version</value>" "<value type='string' name='author' optional='yes'>method author</value>" "<value type='string' name='purpose' optional='yes'>method purpose</value>" "<value type='array' name='signatures' desc='list of method signatures'>" "<value type='struct' desc='representation of a single signature'>" "<value type='system.valueList' name='params' optional='yes'>parameter list</value>" "<value type='system.valueList' name='returns' optional='yes'>return value list</value>" "</value>" "</value>" "<value type='system.stringList' name='bugs' optional='yes'>list of known bugs</value>" "<value type='system.stringList' name='errors' optional='yes'>list of possible errors and error codes</value>" "<value type='system.stringList' name='examples' optional='yes'>list of examples</value>" "<value type='system.stringList' name='history' optional='yes'>list of modifications</value>" "<value type='system.stringList' name='notes' optional='yes'>list of notes</value>" "<value type='system.stringList' name='see' optional='yes'>see also. list of related methods</value>" "<value type='system.stringList' name='todo' optional='yes'>list of unimplemented features</value>" "</value>" "</value>" "<value type='array' name='typeList' desc='a list of type descriptions. Typically used for referencing complex types'>" "<value type='system.value'>a type description</value>" "</value>" "</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.listMethods' />" "<item name='system.methodSignature' />" "<item name='system.methodHelp' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "<!-- system.listMethods -->" "<methodDescription name='system.listMethods'>" "<author>Dan Libby</author>" "<purpose>enumerates the methods implemented by this XML-RPC server.</purpose>" "<version>1.0</version>" "<signatures>" "<signature>" "<returns>" "<value type='array' desc='an array of strings'>" "<value type='string'>name of a method implemented by the server.</value>" "</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.describeMethods' />" "<item name='system.methodSignature' />" "<item name='system.methodHelp' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "<!-- system.methodHelp -->" "<methodDescription name='system.methodHelp'>" "<author>Dan Libby</author>" "<purpose>provides documentation string for a single method</purpose>" "<version>1.0</version>" "<signatures>" "<signature>" "<params>" "<value type='string' name='methodName'>name of the method for which documentation is desired</value>" "</params>" "<returns>" "<value type='string'>help text if defined for the method passed, otherwise an empty string</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.listMethods' />" "<item name='system.methodSignature' />" "<item name='system.methodHelp' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "<!-- system.methodSignature -->" "<methodDescription name='system.methodSignature'>" "<author>Dan Libby</author>" "<purpose>provides 1 or more signatures for a single method</purpose>" "<version>1.0</version>" "<signatures>" "<signature>" "<params>" "<value type='string' name='methodName'>name of the method for which documentation is desired</value>" "</params>" "<returns>" "<value type='array' desc='a list of arrays, each representing a signature'>" "<value type='array' desc='a list of strings. the first element represents the method return value. subsequent elements represent parameters.'>" "<value type='string'>a string indicating the xmlrpc type of a value. one of: string, int, double, base64, datetime, array, struct</value>" "</value>" "</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.listMethods' />" "<item name='system.methodHelp' />" "<item name='system.describeMethods' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "<!-- system.multiCall -->" "<methodDescription name='system.multiCall'>" "<author>Dan Libby</author>" "<purpose>executes multiple methods in sequence and returns the results</purpose>" "<version>1.0</version>" "<signatures>" "<signature>" "<params>" "<value type='array' name='methodList' desc='an array of method call structs'>" "<value type='struct' desc='a struct representing a single method call'>" "<value type='string' name='methodName' desc='name of the method to be executed'/>" "<value type='array' name='params' desc='an array representing the params to a method. sub-elements should match method signature'/>" "</value>" "</value>" "</params>" "<returns>" "<value type='array' desc='an array of method responses'>" "<value type='array' desc='an array containing a single value, which is the method's response'/>" "</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.listMethods' />" "<item name='system.methodHelp' />" "<item name='system.describeMethods' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "<!-- system.getCapabilities -->" "<methodDescription name='system.getCapabilities'>" "<author>Dan Libby</author>" "<purpose>returns a list of capabilities supported by this server</purpose>" "<version>1.0</version>" "<notes><item>spec url: http://groups.yahoo.com/group/xml-rpc/message/2897</item></notes>" "<signatures>" "<signature>" "<returns>" "<value type='struct' desc='list of capabilities, each with a unique key defined by the capability's spec'>" "<value type='struct' desc='definition of a single capability'>" "<value type='string' name='specURL'>www address of the specification defining this capability</value>" "<value type='int' name='specVersion'>version of the spec that this server's implementation conforms to</value>" "</value>" "</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.listMethods' />" "<item name='system.methodHelp' />" "<item name='system.describeMethods' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "</methodList>" "</introspection>"; /* forward declarations for static (non public, non api) funcs */ static XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); static XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); /*-******************* * System Methods API * *********************/ static void xsm_lazy_doc_methods_cb(XMLRPC_SERVER server, void* userData) { XMLRPC_VALUE xDesc = XMLRPC_IntrospectionCreateDescription(xsm_introspection_xml, NULL); XMLRPC_ServerAddIntrospectionData(server, xDesc); XMLRPC_CleanupValue(xDesc); } void xsm_register(XMLRPC_SERVER server) { xi_register_system_methods(server); XMLRPC_ServerRegisterMethod(server, xsm_token_system_multicall, xsm_system_multicall_cb); XMLRPC_ServerRegisterMethod(server, xsm_token_system_get_capabilities, xsm_system_get_capabilities_cb); /* callback for documentation generation should it be requested */ XMLRPC_ServerRegisterIntrospectionCallback(server, xsm_lazy_doc_methods_cb); } XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { XMLRPC_VALUE xArray = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)); XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_array); if (xArray) { XMLRPC_VALUE xMethodIter = XMLRPC_VectorRewind(xArray); while (xMethodIter) { XMLRPC_REQUEST request = XMLRPC_RequestNew(); if(request) { const char* methodName = XMLRPC_VectorGetStringWithID(xMethodIter, "methodName"); XMLRPC_VALUE params = XMLRPC_VectorGetValueWithID(xMethodIter, "params"); if(methodName && params) { XMLRPC_VALUE xRandomArray = XMLRPC_CreateVector(0, xmlrpc_vector_array); XMLRPC_RequestSetMethodName(request, methodName); XMLRPC_RequestSetData(request, params); XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); XMLRPC_AddValueToVector(xRandomArray, XMLRPC_ServerCallMethod(server, request, userData)); XMLRPC_AddValueToVector(xReturn, xRandomArray); } XMLRPC_RequestFree(request, 1); } xMethodIter = XMLRPC_VectorNext(xArray); } } return xReturn; } XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_struct); XMLRPC_VALUE xFaults = XMLRPC_CreateVector("faults_interop", xmlrpc_vector_struct); XMLRPC_VALUE xIntro = XMLRPC_CreateVector("introspection", xmlrpc_vector_struct); /* support for fault spec */ XMLRPC_VectorAppendString(xFaults, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php", 0); XMLRPC_VectorAppendInt(xFaults, "specVersion", 20010516); /* support for introspection spec */ XMLRPC_VectorAppendString(xIntro, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.introspection.php", 0); XMLRPC_VectorAppendInt(xIntro, "specVersion", 20010516); XMLRPC_AddValuesToVector(xReturn, xFaults, xIntro, NULL); return xReturn; } /*-*********************** * End System Methods API * *************************/ Index: php4/ext/xmlrpc/libxmlrpc/system_methods_private.h +++ php4/ext/xmlrpc/libxmlrpc/system_methods_private.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Dan Libby, Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* IMPORTANT! * * only non-public things should be in this file. It is fine for any .c file * in xmlrpc/src to include it, but users of the public API should never * include it, and thus *.h files that are part of the public API should * never include it, or they would break if this file is not present. */ #ifndef __SYSTEM_METHODS_PRIVATE_H /* * Avoid include redundancy. */ #define __SYSTEM_METHODS_PRIVATE_H /*---------------------------------------------------------------------------- * system_methods_private.h * * Purpose: * define non-public system.* methods * Comments: * xsm = xmlrpc system methods */ /*---------------------------------------------------------------------------- * Constants */ #define xsm_token_system_multicall "system.multiCall" #define xsm_token_system_get_capabilities "system.getCapabilities" /*---------------------------------------------------------------------------- * Includes */ /*---------------------------------------------------------------------------- * Structures */ /*---------------------------------------------------------------------------- * Globals */ /*---------------------------------------------------------------------------- * Functions */ void xsm_register(XMLRPC_SERVER server); int xsm_is_system_method(XMLRPC_Callback cb); /*---------------------------------------------------------------------------- * Macros */ #endif /* __SYSTEM_METHODS_PRIVATE_H */ Index: php4/ext/xmlrpc/libxmlrpc/xml_element.c +++ php4/ext/xmlrpc/libxmlrpc/xml_element.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ static const char rcsid[] = "#(@) $Id: xml_element.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; /****h* ABOUT/xml_element * NAME * xml_element * AUTHOR * Dan Libby, aka danda (dan@libby.com) * CREATION DATE * 06/2000 * HISTORY * 10/15/2000 -- danda -- adding robodoc documentation * TODO * Nicer external API. Get rid of macros. Make opaque types, etc. * PORTABILITY * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just * about anything with minor mods. * NOTES * This code incorporates ideas from expat-ensor from http://xml.ensor.org. * * It was coded primarily to act as a go-between for expat and xmlrpc. To this * end, it stores xml elements, their sub-elements, and their attributes in an * in-memory tree. When expat is done parsing, the tree can be walked, thus * retrieving the values. The code can also be used to build a tree via API then * write out the tree to a buffer, thus "serializing" the xml. * * It turns out this is useful for other purposes, such as parsing config files. * YMMV. * * Some Features: * - output option for xml escaping data. Choices include no escaping, entity escaping, * or CDATA sections. * - output option for character encoding. Defaults to (none) utf-8. * - output option for verbosity/readability. ultra-compact, newlines, pretty/level indented. * * BUGS * there must be some. ******/ #include <stdlib.h> #include <string.h> #include "xml_element.h" #include "queue.h" #include "expat.h" #include "encodings.h" #define my_free(thing) if(thing) {free(thing); thing = 0;} #define XML_DECL_START "<?xml" #define XML_DECL_START_LEN sizeof(XML_DECL_START) - 1 #define XML_DECL_VERSION "version='1.0'" #define XML_DECL_VERSION_LEN sizeof(XML_DECL_VERSION) - 1 #define XML_DECL_ENCODING_ATTR "encoding" #define XML_DECL_ENCODING_ATTR_LEN sizeof(XML_DECL_ENCODING_ATTR) - 1 #define XML_DECL_ENCODING_DEFAULT "utf-8" #define XML_DECL_ENCODING_DEFAULT_LEN sizeof(XML_DECL_ENCODING_DEFAULT) - 1 #define XML_DECL_END "?>" #define XML_DECL_END_LEN sizeof(XML_DECL_END) - 1 #define START_TOKEN_BEGIN "<" #define START_TOKEN_BEGIN_LEN sizeof(START_TOKEN_BEGIN) - 1 #define START_TOKEN_END ">" #define START_TOKEN_END_LEN sizeof(START_TOKEN_END) - 1 #define EMPTY_START_TOKEN_END "/>" #define EMPTY_START_TOKEN_END_LEN sizeof(EMPTY_START_TOKEN_END) - 1 #define END_TOKEN_BEGIN "</" #define END_TOKEN_BEGIN_LEN sizeof(END_TOKEN_BEGIN) - 1 #define END_TOKEN_END ">" #define END_TOKEN_END_LEN sizeof(END_TOKEN_END) - 1 #define ATTR_DELIMITER "\"" #define ATTR_DELIMITER_LEN sizeof(ATTR_DELIMITER) - 1 #define CDATA_BEGIN "<![CDATA[" #define CDATA_BEGIN_LEN sizeof(CDATA_BEGIN) - 1 #define CDATA_END "]]>" #define CDATA_END_LEN sizeof(CDATA_END) - 1 #define EQUALS "=" #define EQUALS_LEN sizeof(EQUALS) - 1 #define WHITESPACE " " #define WHITESPACE_LEN sizeof(WHITESPACE) - 1 #define NEWLINE "\n" #define NEWLINE_LEN sizeof(NEWLINE) - 1 #define MAX_VAL_BUF 144 #define SCALAR_STR "SCALAR" #define SCALAR_STR_LEN sizeof(SCALAR_STR) - 1 #define VECTOR_STR "VECTOR" #define VECTOR_STR_LEN sizeof(VECTOR_STR) - 1 #define RESPONSE_STR "RESPONSE" #define RESPONSE_STR_LEN sizeof(RESPONSE_STR) - 1 /*----------------------------- - Begin xml_element Functions - -----------------------------*/ /****f* xml_element/xml_elem_free_non_recurse * NAME * xml_elem_free_non_recurse * SYNOPSIS * void xml_elem_free_non_recurse(xml_element* root) * FUNCTION * free a single xml element. child elements will not be freed. * INPUTS * root - the element to free * RESULT * void * NOTES * SEE ALSO * xml_elem_free () * xml_elem_new () * SOURCE */ void xml_elem_free_non_recurse(xml_element* root) { if(root) { xml_element_attr* attrs = Q_Head(&root->attrs); while(attrs) { my_free(attrs->key); my_free(attrs->val); my_free(attrs); attrs = Q_Next(&root->attrs); } Q_Destroy(&root->children); Q_Destroy(&root->attrs); my_free((char*)root->name); simplestring_free(&root->text); my_free(root); } } /******/ /****f* xml_element/xml_elem_free * NAME * xml_elem_free * SYNOPSIS * void xml_elem_free(xml_element* root) * FUNCTION * free an xml element and all of its child elements * INPUTS * root - the root of an xml tree you would like to free * RESULT * void * NOTES * SEE ALSO * xml_elem_free_non_recurse () * xml_elem_new () * SOURCE */ void xml_elem_free(xml_element* root) { if(root) { xml_element* kids = Q_Head(&root->children); while(kids) { xml_elem_free(kids); kids = Q_Next(&root->children); } xml_elem_free_non_recurse(root); } } /******/ /****f* xml_element/xml_elem_new * NAME * xml_elem_new * SYNOPSIS * xml_element* xml_elem_new() * FUNCTION * allocates and initializes a new xml_element * INPUTS * none * RESULT * xml_element* or NULL. NULL indicates an out-of-memory condition. * NOTES * SEE ALSO * xml_elem_free () * xml_elem_free_non_recurse () * SOURCE */ xml_element* xml_elem_new() { xml_element* elem = calloc(1, sizeof(xml_element)); if(elem) { Q_Init(&elem->children); Q_Init(&elem->attrs); simplestring_init(&elem->text); /* init empty string in case we don't find any char data */ simplestring_addn(&elem->text, "", 0); } return elem; } /******/ static int xml_elem_writefunc(int (*fptr)(void *data, const char *text, int size), const char *text, void *data, int len) { return fptr && text ? fptr(data, text, len ? len : strlen(text)) : 0; } static int create_xml_escape(char *pString, unsigned char c) { int counter = 0; pString[counter++] = '&'; pString[counter++] = '#'; if(c >= 100) { pString[counter++] = c / 100 + '0'; c = c % 100; } if(c >= 10) { pString[counter++] = c / 10 + '0'; c = c % 10; } pString[counter++] = c + '0'; pString[counter++] = ';'; return counter; } #define non_ascii(c) (c > 127) #define non_print(c) (!isprint(c)) #define markup(c) (c == '&' || c == '\"' || c == '>' || c == '<') #define entity_length(c) ( (c >= 100) ? 3 : ((c >= 10) ? 2 : 1) ) + 3; /* "&#" + c + ";" */ /* * xml_elem_entity_escape * * Purpose: * escape reserved xml chars and non utf-8 chars as xml entities * Comments: * The return value may be a new string, or null if no * conversion was performed. In the latter case, *newlen will * be 0. * Flags (to escape) * xml_elem_no_escaping = 0x000, * xml_elem_entity_escaping = 0x002, // escape xml special chars as entities * xml_elem_non_ascii_escaping = 0x008, // escape chars above 127 * xml_elem_cdata_escaping = 0x010, // wrap in cdata */ static char* xml_elem_entity_escape(const char* buf, int old_len, int *newlen, XML_ELEM_ESCAPING flags) { char *pRetval = 0; int iNewBufLen=0; #define should_escape(c, flag) ( ((flag & xml_elem_markup_escaping) && markup(c)) || \ ((flag & xml_elem_non_ascii_escaping) && non_ascii(c)) || \ ((flag & xml_elem_non_print_escaping) && non_print(c)) ) if(buf && *buf) { const unsigned char *bufcopy; char *NewBuffer; int ToBeXmlEscaped=0; int iLength; bufcopy = buf; iLength= old_len ? old_len : strlen(buf); while(*bufcopy) { if( should_escape(*bufcopy, flags) ) { /* the length will increase by length of xml escape - the character length */ iLength += entity_length(*bufcopy); ToBeXmlEscaped=1; } bufcopy++; } if(ToBeXmlEscaped) { NewBuffer= malloc(iLength+1); if(NewBuffer) { bufcopy=buf; while(*bufcopy) { if(should_escape(*bufcopy, flags)) { iNewBufLen += create_xml_escape(NewBuffer+iNewBufLen,*bufcopy); } else { NewBuffer[iNewBufLen++]=*bufcopy; } bufcopy++; } NewBuffer[iNewBufLen] = 0; pRetval = NewBuffer; } } } if(newlen) { *newlen = iNewBufLen; } return pRetval; } static void xml_element_serialize(xml_element *el, int (*fptr)(void *data, const char *text, int size), void *data, XML_ELEM_OUTPUT_OPTIONS options, int depth) { int i; static STRUCT_XML_ELEM_OUTPUT_OPTIONS default_opts = {xml_elem_pretty, xml_elem_markup_escaping | xml_elem_non_print_escaping, XML_DECL_ENCODING_DEFAULT}; static char whitespace[] = " " " " " "; depth++; if(!el) { fprintf(stderr, "Nothing to write\n"); return; } if(!options) { options = &default_opts; } /* print xml declaration if at root level */ if(depth == 1) { xml_elem_writefunc(fptr, XML_DECL_START, data, XML_DECL_START_LEN); xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); xml_elem_writefunc(fptr, XML_DECL_VERSION, data, XML_DECL_VERSION_LEN); if(options->encoding && *options->encoding) { xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); xml_elem_writefunc(fptr, XML_DECL_ENCODING_ATTR, data, XML_DECL_ENCODING_ATTR_LEN); xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN); xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); xml_elem_writefunc(fptr, options->encoding, data, 0); xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); } xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); xml_elem_writefunc(fptr, XML_DECL_END, data, XML_DECL_END_LEN); if(options->verbosity != xml_elem_no_white_space) { xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); } } if(options->verbosity == xml_elem_pretty && depth > 2) { xml_elem_writefunc(fptr, whitespace, data, depth - 2); } /* begin element */ xml_elem_writefunc(fptr,START_TOKEN_BEGIN, data, START_TOKEN_BEGIN_LEN); if(el->name) { xml_elem_writefunc(fptr, el->name, data, 0); /* write attrs, if any */ if(Q_Size(&el->attrs)) { xml_element_attr* iter = Q_Head(&el->attrs); while( iter ) { xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); xml_elem_writefunc(fptr, iter->key, data, 0); xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN); xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); xml_elem_writefunc(fptr, iter->val, data, 0); xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); iter = Q_Next(&el->attrs); } } } else { xml_elem_writefunc(fptr, "None", data, 0); } /* if no text and no children, use abbreviated form, eg: <foo/> */ if(!el->text.len && !Q_Size(&el->children)) { xml_elem_writefunc(fptr, EMPTY_START_TOKEN_END, data, EMPTY_START_TOKEN_END_LEN); } /* otherwise, print element contents */ else { xml_elem_writefunc(fptr, START_TOKEN_END, data, START_TOKEN_END_LEN); /* print text, if any */ if(el->text.len) { char* escaped_str = el->text.str; int buflen = el->text.len; if(options->escaping && options->escaping != xml_elem_cdata_escaping) { escaped_str = xml_elem_entity_escape(el->text.str, buflen, &buflen, options->escaping ); if(!escaped_str) { escaped_str = el->text.str; } } if(options->escaping & xml_elem_cdata_escaping) { xml_elem_writefunc(fptr, CDATA_BEGIN, data, CDATA_BEGIN_LEN); } xml_elem_writefunc(fptr, escaped_str, data, buflen); if(escaped_str != el->text.str) { my_free(escaped_str); } if(options->escaping & xml_elem_cdata_escaping) { xml_elem_writefunc(fptr, CDATA_END, data, CDATA_END_LEN); } } /* no text, so print child elems */ else { xml_element *kids = Q_Head(&el->children); i = 0; while( kids ) { if(i++ == 0) { if(options->verbosity != xml_elem_no_white_space) { xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); } } xml_element_serialize(kids, fptr, data, options, depth); kids = Q_Next(&el->children); } if(i) { if(options->verbosity == xml_elem_pretty && depth > 2) { xml_elem_writefunc(fptr, whitespace, data, depth - 2); } } } xml_elem_writefunc(fptr, END_TOKEN_BEGIN, data, END_TOKEN_BEGIN_LEN); xml_elem_writefunc(fptr,el->name ? el->name : "None", data, 0); xml_elem_writefunc(fptr, END_TOKEN_END, data, END_TOKEN_END_LEN); } if(options->verbosity != xml_elem_no_white_space) { xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); } } /* print buf to file */ static file_out_fptr(void *f, const char *text, int size) { fputs(text, (FILE *)f); } /* print buf to simplestring */ static simplestring_out_fptr(void *f, const char *text, int size) { simplestring* buf = (simplestring*)f; if(buf) { simplestring_addn(buf, text, size); } } /****f* xml_element/xml_elem_serialize_to_string * NAME * xml_elem_serialize_to_string * SYNOPSIS * void xml_element_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len) * FUNCTION * writes element tree as XML into a newly allocated buffer * INPUTS * el - root element of tree * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS * buf_len - length of returned buffer, if not null. * RESULT * char* or NULL. Must be free'd by caller. * NOTES * SEE ALSO * xml_elem_serialize_to_stream () * xml_elem_parse_buf () * SOURCE */ char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len) { simplestring buf; simplestring_init(&buf); xml_element_serialize(el, simplestring_out_fptr, (void *)&buf, options, 0); if(buf_len) { *buf_len = buf.len; } return buf.str; } /******/ /****f* xml_element/xml_elem_serialize_to_stream * NAME * xml_elem_serialize_to_stream * SYNOPSIS * void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options) * FUNCTION * writes element tree as XML into a stream (typically an opened file) * INPUTS * el - root element of tree * output - stream handle * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS * RESULT * void * NOTES * SEE ALSO * xml_elem_serialize_to_string () * xml_elem_parse_buf () * SOURCE */ void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options) { xml_element_serialize(el, file_out_fptr, (void *)output, options, 0); } /******/ /*--------------------------* * End xml_element Functions * *--------------------------*/ /*---------------------- * Begin Expat Handlers * *---------------------*/ typedef struct _xml_elem_data { xml_element* root; xml_element* current; XML_ELEM_INPUT_OPTIONS input_options; int needs_enc_conversion; } xml_elem_data; /* expat start of element handler */ static void startElement(void *userData, const char *name, const char **attrs) { int i; xml_element *c; xml_elem_data* mydata = (xml_elem_data*)userData; const char** p = attrs; if(mydata) { c = mydata->current; mydata->current = xml_elem_new(); mydata->current->name = (char*)strdup(name); mydata->current->parent = c; /* init attrs */ while(p && *p) { xml_element_attr* attr = malloc(sizeof(xml_element_attr)); if(attr) { attr->key = strdup(*p); attr->val = strdup(*(p+1)); Q_PushTail(&mydata->current->attrs, attr); p += 2; } } } } /* expat end of element handler */ static void endElement(void *userData, const char *name) { xml_elem_data* mydata = (xml_elem_data*)userData; if(mydata && mydata->current && mydata->current->parent) { Q_PushTail(&mydata->current->parent->children, mydata->current); mydata->current = mydata->current->parent; } } /* expat char data handler */ static void charHandler(void *userData, const char *s, int len) { xml_elem_data* mydata = (xml_elem_data*)userData; if(mydata && mydata->current) { /* Check if we need to decode utf-8 parser output to another encoding */ if(mydata->needs_enc_conversion && mydata->input_options->encoding) { char* add_text = utf8_decode(s, len, &len, mydata->input_options->encoding); if(add_text) { simplestring_addn(&mydata->current->text, add_text, len); free(add_text); return; } } simplestring_addn(&mydata->current->text, s, len); } } /******/ /*-------------------* * End Expat Handlers * *-------------------*/ /*-------------------* * xml_elem_parse_buf * *-------------------*/ /****f* xml_element/xml_elem_parse_buf * NAME * xml_elem_parse_buf * SYNOPSIS * xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error) * FUNCTION * parse a buffer containing XML into an xml_element in-memory tree * INPUTS * in_buf - buffer containing XML document * len - length of buffer * options - input options. optional * error - error result data. optional. check if result is null. * RESULT * void * NOTES * The returned data must be free'd by caller * SEE ALSO * xml_elem_serialize_to_string () * xml_elem_free () * SOURCE */ xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error) { xml_element* xReturn = NULL; char buf[100] = ""; static STRUCT_XML_ELEM_INPUT_OPTIONS default_opts = {encoding_utf_8}; if(!options) { options = &default_opts; } if(in_buf) { XML_Parser parser; xml_elem_data mydata = {0}; parser = XML_ParserCreate(NULL); mydata.root = xml_elem_new(); mydata.current = mydata.root; mydata.input_options = options; mydata.needs_enc_conversion = options->encoding && strcmp(options->encoding, encoding_utf_8); XML_SetElementHandler(parser, startElement, endElement); XML_SetCharacterDataHandler(parser, charHandler); /* pass the xml_elem_data struct along */ XML_SetUserData(parser, (void*)&mydata); if(!len) { len = strlen(in_buf); } /* parse the XML */ if(XML_Parse(parser, in_buf, len, 1) == 0) { enum XML_Error err_code = XML_GetErrorCode(parser); int line_num = XML_GetCurrentLineNumber(parser); int col_num = XML_GetCurrentColumnNumber(parser); long byte_idx = XML_GetCurrentByteIndex(parser); int byte_total = XML_GetCurrentByteCount(parser); const char * error_str = XML_ErrorString(err_code); if(byte_idx >= 0) { snprintf(buf, sizeof(buf), "\n\tdata beginning %i before byte index: %s\n", byte_idx > 10 ? 10 : byte_idx, in_buf + (byte_idx > 10 ? byte_idx - 10 : byte_idx)); } fprintf(stderr, "expat reports error code %i\n" "\tdescription: %s\n" "\tline: %i\n" "\tcolumn: %i\n" "\tbyte index: %i\n" "\ttotal bytes: %i\n%s ", err_code, error_str, line_num, col_num, byte_idx, byte_total, buf); /* error condition */ if(error) { error->parser_code = (long)err_code; error->line = line_num; error->column = col_num; error->byte_index = byte_idx; error->parser_error = error_str; } } else { xReturn = (xml_element*)Q_Head(&mydata.root->children); } XML_ParserFree(parser); xml_elem_free_non_recurse(mydata.root); } return xReturn; } /******/ Index: php4/ext/xmlrpc/libxmlrpc/xml_element.h +++ php4/ext/xmlrpc/libxmlrpc/xml_element.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef __XML_ELEMENT_H__ #define __XML_ELEMENT_H__ /* includes */ #include <stdio.h> #include "queue.h" #include "simplestring.h" #include "encodings.h" #ifdef __cplusplus extern "C" { #endif /****d* enum/XML_ELEM_VERBOSITY * NAME * XML_ELEM_VERBOSITY * NOTES * verbosity/readability options for generated xml * SEE ALSO * XML_ELEM_OUTPUT_OPTIONS * SOURCE */ typedef enum _xml_elem_verbosity { xml_elem_no_white_space, /* compact xml with no white space */ xml_elem_newlines_only, /* add newlines for enhanced readability */ xml_elem_pretty /* add newlines and indent accordint to depth */ } XML_ELEM_VERBOSITY; /******/ /****d* enum/XML_ELEM_ESCAPING * NAME * XML_ELEM_ESCAPING * NOTES * xml escaping options for generated xml * SEE ALSO * XML_ELEM_OUTPUT_OPTIONS * SOURCE */ typedef enum _xml_elem_escaping { xml_elem_no_escaping = 0x000, xml_elem_markup_escaping = 0x002, /* entity escape xml special chars */ xml_elem_non_ascii_escaping = 0x008, /* entity escape chars above 127 */ xml_elem_non_print_escaping = 0x010, /* entity escape non print (illegal) chars */ xml_elem_cdata_escaping = 0x020, /* wrap in cdata section */ } XML_ELEM_ESCAPING; /******/ /****s* struct/XML_ELEM_OUTPUT_OPTIONS * NAME * XML_ELEM_OUTPUT_OPTIONS * NOTES * defines various output options * SOURCE */ typedef struct _xml_output_options { XML_ELEM_VERBOSITY verbosity; /* length/verbosity of xml */ XML_ELEM_ESCAPING escaping; /* how to escape special chars */ const char* encoding; /* <?xml encoding="<encoding>" ?> */ } STRUCT_XML_ELEM_OUTPUT_OPTIONS, *XML_ELEM_OUTPUT_OPTIONS; /******/ /****s* struct/XML_ELEM_INPUT_OPTIONS * NAME * XML_ELEM_INPUT_OPTIONS * NOTES * defines various input options * SOURCE */ typedef struct _xml_input_options { ENCODING_ID encoding; /* which encoding to use. */ } STRUCT_XML_ELEM_INPUT_OPTIONS, *XML_ELEM_INPUT_OPTIONS; /******/ /****s* struct/XML_ELEM_ERROR * NAME * XML_ELEM_ERROR * NOTES * defines an xml parser error * SOURCE */ typedef struct _xml_elem_error { int parser_code; const char* parser_error; long line; long column; long byte_index; } STRUCT_XML_ELEM_ERROR, *XML_ELEM_ERROR; /******/ /*-************************ * begin xml element stuff * **************************/ /****s* struct/xml_elem_attr * NAME * xml_elem_attr * NOTES * representation of an xml attribute, foo="bar" * SOURCE */ typedef struct _xml_element_attr { char* key; /* attribute key */ char* val; /* attribute value */ } xml_element_attr; /******/ /****s* struct/xml_elem_attr * NAME * xml_elem_attr * NOTES * representation of an xml element, eg <candidate name="Harry Browne" party="Libertarian"/> * SOURCE */ typedef struct _xml_element { const char* name; /* element identifier */ simplestring text; /* text contained between element begin/end pairs */ struct _xml_element* parent; /* element's parent */ queue attrs; /* attribute list */ queue children; /* child element list */ } xml_element; /******/ void xml_elem_free(xml_element* root); void xml_elem_free_non_recurse(xml_element* root); xml_element* xml_elem_new(); char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len); void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options); xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error); /*-********************** * end xml element stuff * ************************/ /*-********************** * Begin xml_element API * ************************/ /****d* VALUE/XMLRPC_MACROS * NAME * Some Helpful Macros * NOTES * Some macros for making life easier. Should be self-explanatory. * SEE ALSO * XMLRPC_AddValueToVector () * XMLRPC_VectorGetValueWithID_Case () * XMLRPC_VALUE * SOURCE */ #define xml_elem_next_element(el) ((el) ? (xml_element *)Q_Next(&el->children) : NULL) #define xml_elem_head_element(el) ((el) ? (xml_element *)Q_Head(&el->children) : NULL) #define xml_elem_next_attr(el) ((el) ? (xml_element_attr *)Q_Next(&el->attrs) : NULL) #define xml_elem_head_attr(el) ((el) ? (xml_element_attr *)Q_Head(&el->attrs) : NULL) #define xml_elem_get_name(el) (char *)((el) ? el->name : NULL) #define xml_elem_get_val(el) (char *)((el) ? el->text.str : NULL) /******/ /*-******************** * End xml_element API * **********************/ #ifdef __cplusplus } #endif #endif /* __XML_ELEMENT_H__ */ Index: php4/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c +++ php4/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #include <string.h> #include <stdlib.h> #include "xml_to_dandarpc.h" #include "base64.h" /* list of tokens used in vocab */ #define ELEM_METHODCALL "methodCall" #define ELEM_METHODNAME "methodName" #define ELEM_METHODRESPONSE "methodResponse" #define ELEM_ROOT "simpleRPC" #define ATTR_ARRAY "array" #define ATTR_BASE64 "base64" #define ATTR_BOOLEAN "boolean" #define ATTR_DATETIME "dateTime.iso8601" #define ATTR_DOUBLE "double" #define ATTR_ID "id" #define ATTR_INT "int" #define ATTR_MIXED "mixed" #define ATTR_SCALAR "scalar" #define ATTR_STRING "string" #define ATTR_STRUCT "struct" #define ATTR_TYPE "type" #define ATTR_VECTOR "vector" #define ATTR_VERSION "version" #define VAL_VERSION_0_9 "0.9" XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE xCurrent, xml_element* el) { if(!xCurrent) { xCurrent = XMLRPC_CreateValueEmpty(); } if(el->name) { const char* id = NULL; const char* type = NULL; xml_element_attr* attr_iter = Q_Head(&el->attrs); while(attr_iter) { if(!strcmp(attr_iter->key, ATTR_ID)) { id = attr_iter->val; } if(!strcmp(attr_iter->key, ATTR_TYPE)) { type = attr_iter->val; } attr_iter = Q_Next(&el->attrs); } if(id) { XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact); } if(!strcmp(el->name, ATTR_SCALAR)) { if(!type || !strcmp(type, ATTR_STRING)) { XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len); } else if(!strcmp(type, ATTR_INT)) { XMLRPC_SetValueInt(xCurrent, atoi(el->text.str)); } else if(!strcmp(type, ATTR_BOOLEAN)) { XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str)); } else if(!strcmp(type, ATTR_DOUBLE)) { XMLRPC_SetValueDouble(xCurrent, atof(el->text.str)); } else if(!strcmp(type, ATTR_DATETIME)) { XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str); } else if(!strcmp(type, ATTR_BASE64)) { struct buffer_st buf; base64_decode(&buf, el->text.str, el->text.len); XMLRPC_SetValueBase64(xCurrent, buf.data, buf.offset); buffer_delete(&buf); } } else if(!strcmp(el->name, ATTR_VECTOR)) { xml_element* iter = (xml_element*)Q_Head(&el->children); if(!type || !strcmp(type, ATTR_MIXED)) { XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); } else if(!strcmp(type, ATTR_ARRAY)) { XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); } else if(!strcmp(type, ATTR_STRUCT)) { XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct); } while( iter ) { XMLRPC_VALUE xNext = XMLRPC_CreateValueEmpty(); xml_element_to_DANDARPC_REQUEST_worker(request, xNext, iter); XMLRPC_AddValueToVector(xCurrent, xNext); iter = (xml_element*)Q_Next(&el->children); } } else { xml_element* iter = (xml_element*)Q_Head(&el->children); while( iter ) { xml_element_to_DANDARPC_REQUEST_worker(request, xCurrent, iter); iter = (xml_element*)Q_Next(&el->children); } if(!strcmp(el->name, ELEM_METHODCALL)) { if(request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); } } else if(!strcmp(el->name, ELEM_METHODRESPONSE)) { if(request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); } } else if(!strcmp(el->name, ELEM_METHODNAME)) { if(request) { XMLRPC_RequestSetMethodName(request, el->text.str); } } } } return xCurrent; } XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el) { return xml_element_to_DANDARPC_REQUEST_worker(NULL, NULL, el); } XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el) { if(request) { return XMLRPC_RequestSetData(request, xml_element_to_DANDARPC_REQUEST_worker(request, NULL, el)); } return NULL; } xml_element* DANDARPC_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) { #define BUF_SIZE 512 xml_element* root = NULL; if(node) { char buf[BUF_SIZE]; const char* id = XMLRPC_GetValueID(node); XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node); XMLRPC_REQUEST_OUTPUT_OPTIONS output = XMLRPC_RequestGetOutputOptions(request); int bNoAddType = (type == xmlrpc_string && request && output && output->xml_elem_opts.verbosity == xml_elem_no_white_space); xml_element* elem_val = xml_elem_new(); const char* pAttrType = NULL; xml_element_attr* attr_type = bNoAddType ? NULL : malloc(sizeof(xml_element_attr)); if(attr_type) { attr_type->key = strdup(ATTR_TYPE); attr_type->val = 0; Q_PushTail(&elem_val->attrs, attr_type); } elem_val->name = (type == xmlrpc_vector) ? strdup(ATTR_VECTOR) : strdup(ATTR_SCALAR); if(id && *id) { xml_element_attr* attr_id = malloc(sizeof(xml_element_attr)); if(attr_id) { attr_id->key = strdup(ATTR_ID); attr_id->val = strdup(id); Q_PushTail(&elem_val->attrs, attr_id); } } switch(type) { case xmlrpc_string: pAttrType = ATTR_STRING; simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); break; case xmlrpc_int: pAttrType = ATTR_INT; snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_boolean: pAttrType = ATTR_BOOLEAN; snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_double: pAttrType = ATTR_DOUBLE; snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_datetime: pAttrType = ATTR_DATETIME; simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node)); break; case xmlrpc_base64: { struct buffer_st buf; pAttrType = ATTR_BASE64; base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); simplestring_addn(&elem_val->text, buf.data, buf.offset ); buffer_delete(&buf); } break; case xmlrpc_vector: { XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node); XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); switch(my_type) { case xmlrpc_vector_array: pAttrType = ATTR_ARRAY; break; case xmlrpc_vector_mixed: pAttrType = ATTR_MIXED; break; case xmlrpc_vector_struct: pAttrType = ATTR_STRUCT; break; default: break; } /* recurse through sub-elements */ while( xIter ) { xml_element* next_el = DANDARPC_to_xml_element_worker(request, xIter); if(next_el) { Q_PushTail(&elem_val->children, next_el); } xIter = XMLRPC_VectorNext(node); } } break; default: break; } if(pAttrType && attr_type && !bNoAddType) { attr_type->val = strdup(pAttrType); } root = elem_val; } return root; } xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node) { return DANDARPC_to_xml_element_worker(NULL, node); } xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { xml_element* wrapper = NULL; xml_element* root = NULL; if(request) { XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request); const char* pStr = NULL; xml_element_attr* version = malloc(sizeof(xml_element_attr)); version->key = strdup(ATTR_VERSION); version->val = strdup(VAL_VERSION_0_9); wrapper = xml_elem_new(); if(request_type == xmlrpc_request_response) { pStr = ELEM_METHODRESPONSE; } else if(request_type == xmlrpc_request_call) { pStr = ELEM_METHODCALL; } if(pStr) { wrapper->name = strdup(pStr); } root = xml_elem_new(); root->name = strdup(ELEM_ROOT); Q_PushTail(&root->attrs, version); Q_PushTail(&root->children, wrapper); pStr = XMLRPC_RequestGetMethodName(request); if(pStr) { xml_element* method = xml_elem_new(); method->name = strdup(ELEM_METHODNAME); simplestring_add(&method->text, pStr); Q_PushTail(&wrapper->children, method); } Q_PushTail(&wrapper->children, DANDARPC_to_xml_element_worker(request, XMLRPC_RequestGetData(request))); } return root; } Index: php4/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.h +++ php4/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef XML_TO_DANDARPC_H #define XML_TO_DANDARPC_H #include "time.h" #include "xmlrpc.h" XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el); XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el); xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node); xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request); #endif /* XML_TO_DANDARPC_H */ Index: php4/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c +++ php4/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ static const char rcsid[] = "#(@) $Id: xml_to_xmlrpc.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; #include <string.h> #include <stdlib.h> #include "xml_to_xmlrpc.h" #include "base64.h" /* list of tokens used in vocab */ #define ELEM_ARRAY "array" #define ELEM_BASE64 "base64" #define ELEM_BOOLEAN "boolean" #define ELEM_DATA "data" #define ELEM_DATETIME "dateTime.iso8601" #define ELEM_DOUBLE "double" #define ELEM_FAULT "fault" #define ELEM_FAULTCODE "faultCode" #define ELEM_FAULTSTRING "faultString" #define ELEM_I4 "i4" #define ELEM_INT "int" #define ELEM_MEMBER "member" #define ELEM_METHODCALL "methodCall" #define ELEM_METHODNAME "methodName" #define ELEM_METHODRESPONSE "methodResponse" #define ELEM_NAME "name" #define ELEM_PARAM "param" #define ELEM_PARAMS "params" #define ELEM_STRING "string" #define ELEM_STRUCT "struct" #define ELEM_VALUE "value" XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) { if (!current_val) { /* This should only be the case for the first element */ current_val = XMLRPC_CreateValueEmpty(); } if (el->name) { if (!strcmp(el->name, ELEM_DATA) /* should be ELEM_ARRAY, but there is an extra level. weird */ || ((!strcmp(el->name, ELEM_PARAMS)) && (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call)) /* this "PARAMS" concept is silly. dave?! */ || !strcmp(el->name, ELEM_FAULT)) { /* so is this "FAULT" nonsense. */ xml_element* iter = (xml_element*)Q_Head(&el->children); XMLRPC_SetIsVector(current_val, xmlrpc_vector_array); while (iter) { XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty(); xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter); XMLRPC_AddValueToVector(current_val, xNextVal); iter = (xml_element*)Q_Next(&el->children); } } else if (!strcmp(el->name, ELEM_STRUCT)) { xml_element* iter = (xml_element*)Q_Head(&el->children); XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct); while ( iter ) { XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty(); xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter); XMLRPC_AddValueToVector(current_val, xNextVal); iter = (xml_element*)Q_Next(&el->children); } } else if (!strcmp(el->name, ELEM_STRING) || (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) { XMLRPC_SetValueString(current_val, el->text.str, el->text.len); } else if (!strcmp(el->name, ELEM_NAME)) { XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact); } else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) { XMLRPC_SetValueInt(current_val, atoi(el->text.str)); } else if (!strcmp(el->name, ELEM_BOOLEAN)) { XMLRPC_SetValueBoolean(current_val, atoi(el->text.str)); } else if (!strcmp(el->name, ELEM_DOUBLE)) { XMLRPC_SetValueDouble(current_val, atof(el->text.str)); } else if (!strcmp(el->name, ELEM_DATETIME)) { XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str); } else if (!strcmp(el->name, ELEM_BASE64)) { struct buffer_st buf; base64_decode(&buf, el->text.str, el->text.len); XMLRPC_SetValueBase64(current_val, buf.data, buf.offset); buffer_delete(&buf); } else { xml_element* iter; if (!strcmp(el->name, ELEM_METHODCALL)) { if (request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); } } else if (!strcmp(el->name, ELEM_METHODRESPONSE)) { if (request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); } } else if (!strcmp(el->name, ELEM_METHODNAME)) { if (request) { XMLRPC_RequestSetMethodName(request, el->text.str); } } iter = (xml_element*)Q_Head(&el->children); while ( iter ) { xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector, current_val, iter); iter = (xml_element*)Q_Next(&el->children); } } } return current_val; } XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el) { return xml_element_to_XMLRPC_REQUEST_worker(NULL, NULL, NULL, el); } XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el) { if (request) { return XMLRPC_RequestSetData(request, xml_element_to_XMLRPC_REQUEST_worker(request, NULL, NULL, el)); } return NULL; } xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node, XMLRPC_REQUEST_TYPE request_type, int depth) { #define BUF_SIZE 512 xml_element* root = NULL; if (node) { char buf[BUF_SIZE]; XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node); XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node); xml_element* elem_val = xml_elem_new(); /* special case for when root element is not an array */ if (depth == 0 && !(type == xmlrpc_vector && vtype == xmlrpc_vector_array && request_type == xmlrpc_request_call) ) { int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)); xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1); if (next_el) { Q_PushTail(&elem_val->children, next_el); } elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS); } else { switch (type) { case xmlrpc_string: elem_val->name = strdup(ELEM_STRING); simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); break; case xmlrpc_int: elem_val->name = strdup(ELEM_INT); snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_boolean: elem_val->name = strdup(ELEM_BOOLEAN); snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_double: elem_val->name = strdup(ELEM_DOUBLE); snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_datetime: elem_val->name = strdup(ELEM_DATETIME); simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node)); break; case xmlrpc_base64: { struct buffer_st buf; elem_val->name = strdup(ELEM_BASE64); base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); simplestring_addn(&elem_val->text, buf.data, buf.offset ); buffer_delete(&buf); } break; case xmlrpc_vector: { XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node); XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); xml_element* root_vector_elem = elem_val; switch (my_type) { case xmlrpc_vector_array: { if(depth == 0) { elem_val->name = strdup(ELEM_PARAMS); } else { /* Hi my name is Dave and I like to make things as confusing * as possible, thus I will throw in this 'data' element * where it absolutely does not belong just so that people * cannot code arrays and structs in a similar and straight * forward manner. Have a good day. * * GRRRRRRRRR! */ xml_element* data = xml_elem_new(); data->name = strdup(ELEM_DATA); elem_val->name = strdup(ELEM_ARRAY); Q_PushTail(&elem_val->children, data); root_vector_elem = data; } } break; case xmlrpc_vector_mixed: /* not officially supported */ case xmlrpc_vector_struct: elem_val->name = strdup(ELEM_STRUCT); break; default: break; } /* recurse through sub-elements */ while ( xIter ) { xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1); if (next_el) { Q_PushTail(&root_vector_elem->children, next_el); } xIter = XMLRPC_VectorNext(node); } } break; default: break; } } { XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector); if (depth == 1) { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); /* yet another hack for the "fault" crap */ if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) { root = value; } else { xml_element* param = xml_elem_new(); param->name = strdup(ELEM_PARAM); Q_PushTail(¶m->children, value); root = param; } Q_PushTail(&value->children, elem_val); } else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) { xml_element* member = xml_elem_new(); xml_element* name = xml_elem_new(); xml_element* value = xml_elem_new(); member->name = strdup(ELEM_MEMBER); name->name = strdup(ELEM_NAME); value->name = strdup(ELEM_VALUE); simplestring_add(&name->text, XMLRPC_GetValueID(node)); Q_PushTail(&member->children, name); Q_PushTail(&member->children, value); Q_PushTail(&value->children, elem_val); root = member; } else if (vtype == xmlrpc_vector_array) { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); Q_PushTail(&value->children, elem_val); root = value; } else if (vtype == xmlrpc_vector_none) { /* no parent. non-op */ root = elem_val; } else { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); Q_PushTail(&value->children, elem_val); root = value; } } } return root; } xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) { return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0); } xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { xml_element* wrapper = NULL; if (request) { const char* pStr = NULL; XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request); XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request); wrapper = xml_elem_new(); if (request_type == xmlrpc_request_call) { pStr = ELEM_METHODCALL; } else if (request_type == xmlrpc_request_response) { pStr = ELEM_METHODRESPONSE; } if (pStr) { wrapper->name = strdup(pStr); } pStr = XMLRPC_RequestGetMethodName(request); if (pStr) { xml_element* method = xml_elem_new(); method->name = strdup(ELEM_METHODNAME); simplestring_add(&method->text, pStr); Q_PushTail(&wrapper->children, method); } if (xParams) { Q_PushTail(&wrapper->children, XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0)); } else { /* Despite the spec, the xml-rpc list folk want me to send an empty params element */ xml_element* params = xml_elem_new(); params->name = strdup(ELEM_PARAMS); Q_PushTail(&wrapper->children, params); } } return wrapper; } Index: php4/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.h +++ php4/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef XML_TO_XMLRPC_H #define XML_TO_XMLRPC_H #include "time.h" #include "xmlrpc.h" XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el); XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el); xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node); xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request); #endif /* XML_TO_XMLRPC_H */ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc.c +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ static const char rcsid[] = "#(@) $Id: xmlrpc.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; /****h* ABOUT/xmlrpc * NAME * XMLRPC_VALUE * AUTHOR * Dan Libby, aka danda (dan@libby.com) * CREATION DATE * 9/1999 - 10/2000 * HISTORY * 09/1999 -- danda -- Initial API, before I even knew of standard XMLRPC vocab. Response only. * 06/2000 -- danda -- played with expat-ensor from www.ensor.org. Cool, but some flaws. * 07/2000 -- danda -- wrote new implementation to be compatible with xmlrpc standard and * incorporated some ideas from ensor, most notably the separation of * xml dom from xmlrpc api. * 08/2000 -- danda -- support for two vocabularies: danda-rpc and xml-rpc * 08/2000 -- danda -- PHP C extension that uses XMLRPC * 10/15/2000 -- danda -- adding robodoc documentation * TODO * Server method introspection. (Enumerate available methods, describe I/O) * PORTABILITY * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just * about anything with minor mods. * NOTES * Welcome to XMLRPC. For more info on the specification and history, see * http://www.xmlrpc.org. * * This code aims to be a full-featured C implementation of XMLRPC. It does not * have any networking code. Rather, it is intended to be plugged into apps * or libraries with existing networking facilities, eg PHP, apache, perl, mozilla, * home-brew application servers, etc. * * Usage Paradigm: * The user of this library will typically be implementing either an XMLRPC server, * an XMLRPC client, or both. The client will use the library to build an in-memory * representation of a request, and then serialize (encode) that request into XML. The * client will then send the XML to the server via external mechanism. The server will * de-serialize the XML back into an binary representation, call the appropriate registered * method -- thereby generating a response. The response will be serialized into XML and * sent back to the client. The client will de-serialize it into memory, and can * iterate through the results via API. * * Both the request and the response may consist of arbitrarily long, arbitrarily nested * values. The values may be one of several types, as defined by XMLRPC_VALUE_TYPE. * * Features and Architecture: * - The XML parsing (xml_element.c) is completely independent of the XMLRPC api. In fact, * it can be used as a standalone dom implementation. * - Because of this, the same XMLRPC data can be serialized into multiple xml vocabularies. * It is simply a matter of writing a transport. So far, two transports have been defined. * The default xmlrpc vocab (xml_to_xmlrpc.c), and simple-rpc (xml_to_dandarpc.c) which is * proprietary, but imho more readable, and nice for proprietary legacy reasons. * - Various output options, including: xml escaping via CDATA or entity, case folding, * vocab version, and character encoding. * - One to One mapping between C structures and actual values, unlike ensor which forces * one to understand the arcana of the xmlrpc vocab. * - support for mixed indexed/keyed vector types, making it more compatible with * languages such as PHP. * - quite speedy compared to implementations written in interpreted languages. Also, uses * intelligent string handling, so not many strlen() calls, etc. * - comprehensive API for manipulation of values *******/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <time.h> #include "queue.h" #include "xmlrpc.h" #include "expat.h" #include "base64.h" #include "xml_to_xmlrpc.h" #include "xml_to_dandarpc.h" #include "xml_element.h" #include "xmlrpc_private.h" #include "xmlrpc_introspection_private.h" /*-********************* * Begin Time Functions * ***********************/ static int date_from_ISO8601(const char *text, time_t *value) { struct tm tm; int n; int i; time_t t; tm.tm_isdst = -1; if(strlen(text) < 17) { return -1; } n = 1000; tm.tm_year = 0; for(i = 0; i < 4; i++) { tm.tm_year += (text[i]-'0')*n; n /= 10; } n = 10; tm.tm_mon = 0; for(i = 0; i < 2; i++) { tm.tm_mon += (text[i+4]-'0')*n; n /= 10; } tm.tm_mon --; n = 10; tm.tm_mday = 0; for(i = 0; i < 2; i++) { tm.tm_mday += (text[i+6]-'0')*n; n /= 10; } n = 10; tm.tm_hour = 0; for(i = 0; i < 2; i++) { tm.tm_hour += (text[i+9]-'0')*n; n /= 10; } n = 10; tm.tm_min = 0; for(i = 0; i < 2; i++) { tm.tm_min += (text[i+12]-'0')*n; n /= 10; } n = 10; tm.tm_sec = 0; for(i = 0; i < 2; i++) { tm.tm_sec += (text[i+15]-'0')*n; n /= 10; } tm.tm_year -= 1900; *value = mktime(&tm); return 0; } static int date_to_ISO8601(time_t value, char *buf, int length) { struct tm *tm; tm = localtime(&value); return strftime(buf, length, "%Y%m%dT%H:%M:%S", tm); } /*-******************* * End Time Functions * *********************/ /*-*************************** * Begin XMLRPC_REQUEST funcs * *****************************/ /****f* REQUEST/XMLRPC_RequestNew * NAME * XMLRPC_RequestNew * SYNOPSIS * XMLRPC_REQUEST XMLRPC_RequestNew() * FUNCTION * Creates a new XMLRPC_Request data struct * INPUTS * none * SEE ALSO * XMLRPC_RequestFree () * SOURCE */ XMLRPC_REQUEST XMLRPC_RequestNew() { XMLRPC_REQUEST xRequest = calloc(1, sizeof(STRUCT_XMLRPC_REQUEST)); if(xRequest) { simplestring_init(&xRequest->methodName); } return xRequest; } /*******/ /****f* REQUEST/XMLRPC_RequestFree * NAME * XMLRPC_RequestFree * SYNOPSIS * void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) * FUNCTION * Free XMLRPC Request and all sub-values * INPUTS * request -- previously allocated request struct * bFreeIO -- 1 = also free request value data, if any, 0 = ignore. * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_CleanupValue () * SOURCE */ void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) { if(request) { simplestring_free(&request->methodName); if(request->io && bFreeIO) { XMLRPC_CleanupValue(request->io); } if(request->error) { XMLRPC_CleanupValue(request->error); } my_free(request); } } /*******/ /* Set Method Name to call */ /****f* REQUEST/XMLRPC_RequestSetMethodName * NAME * XMLRPC_RequestSetMethodName * SYNOPSIS * const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName) * FUNCTION * Set name of method to call with this request. * INPUTS * request -- previously allocated request struct * methodName -- name of method * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestGetMethodName () * XMLRPC_RequestFree () * SOURCE */ const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName) { if(request) { simplestring_clear(&request->methodName); simplestring_add(&request->methodName, methodName); return request->methodName.str; } return NULL; } /*******/ /****f* REQUEST/XMLRPC_RequestGetMethodName * NAME * XMLRPC_RequestGetMethodName * SYNOPSIS * const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) * FUNCTION * Get name of method called by this request * INPUTS * request -- previously allocated request struct * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestSetMethodName () * XMLRPC_RequestFree () * SOURCE */ const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) { return request ? request->methodName.str : NULL; } /*******/ /****f* REQUEST/XMLRPC_RequestSetRequestType * NAME * XMLRPC_RequestSetRequestType * SYNOPSIS * XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) * FUNCTION * A request struct may be allocated by a caller or by xmlrpc * in response to a request. This allows setting the * request type. * INPUTS * request -- previously allocated request struct * type -- request type [xmlrpc_method_call | xmlrpc_method_response] * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestGetRequestType () * XMLRPC_RequestFree () * XMLRPC_REQUEST_TYPE * SOURCE */ XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) { if(request) { request->request_type = type; return request->request_type; } return xmlrpc_request_none; } /*******/ /****f* REQUEST/XMLRPC_RequestGetRequestType * NAME * XMLRPC_RequestGetRequestType * SYNOPSIS * XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) * FUNCTION * A request struct may be allocated by a caller or by xmlrpc * in response to a request. This allows setting the * request type. * INPUTS * request -- previously allocated request struct * RESULT * type -- request type [xmlrpc_method_call | xmlrpc_method_response] * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestSetRequestType () * XMLRPC_RequestFree () * XMLRPC_REQUEST_TYPE * SOURCE */ XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) { return request ? request->request_type : xmlrpc_request_none; } /*******/ /****f* REQUEST/XMLRPC_RequestSetData * NAME * XMLRPC_RequestSetData * SYNOPSIS * XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) * FUNCTION * Associates a block of xmlrpc data with the request. The * data is *not* copied. A pointer is kept. The caller * should be careful not to doubly free the data value, * which may optionally be free'd by XMLRPC_RequestFree(). * INPUTS * request -- previously allocated request struct * data -- previously allocated data struct * RESULT * XMLRPC_VALUE -- pointer to value stored, or NULL * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestGetData () * XMLRPC_RequestFree () * XMLRPC_REQUEST * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) { if(request && data) { request->io = XMLRPC_CopyValue(data); return request->io; } return NULL; } /*******/ /****f* REQUEST/XMLRPC_RequestGetData * NAME * XMLRPC_RequestGetData * SYNOPSIS * XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) * FUNCTION * Returns data associated with request, if any. * INPUTS * request -- previously allocated request struct * RESULT * XMLRPC_VALUE -- pointer to value stored, or NULL * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestSetData () * XMLRPC_RequestFree () * XMLRPC_REQUEST * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) { return request ? request->io : NULL; } /*******/ /****f* REQUEST/XMLRPC_RequestSetOutputOptions * NAME * XMLRPC_RequestSetOutputOptions * SYNOPSIS * XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output) * FUNCTION * Sets output options used for generating XML. The output struct * is copied, and may be freed by the caller. * INPUTS * request -- previously allocated request struct * output -- output options struct initialized by caller * RESULT * XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to value stored, or NULL * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestGetOutputOptions () * XMLRPC_RequestFree () * XMLRPC_REQUEST * XMLRPC_REQUEST_OUTPUT_OPTIONS * SOURCE */ XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output) { if(request && output) { memcpy(&request->output, output, sizeof(STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS)); return &request->output; } return NULL; } /*******/ /****f* REQUEST/XMLRPC_RequestGetOutputOptions * NAME * XMLRPC_RequestGetOutputOptions * SYNOPSIS * XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) * FUNCTION * Gets a pointer to output options used for generating XML. * INPUTS * request -- previously allocated request struct * RESULT * XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to options stored, or NULL * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestSetOutputOptions () * XMLRPC_RequestFree () * XMLRPC_REQUEST * XMLRPC_REQUEST_OUTPUT_OPTIONS * SOURCE */ XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) { return request ? &request->output : NULL; } /*******/ /*-************************* * End XMLRPC_REQUEST funcs * ***************************/ /*-*************************** * Begin Serializiation funcs * *****************************/ /****f* SERIALIZE/XMLRPC_VALUE_ToXML * NAME * XMLRPC_VALUE_ToXML * SYNOPSIS * char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val) * FUNCTION * encode XMLRPC_VALUE into XML buffer. Note that the generated * buffer will not contain a methodCall. * INPUTS * val -- previously allocated XMLRPC_VALUE * buf_len -- length of returned buffer, if not null * RESULT * char* -- newly allocated buffer containing XML. * It is the caller's responsibility to free it. * SEE ALSO * XMLRPC_REQUEST_ToXML () * XMLRPC_VALUE_FromXML () * XMLRPC_Free () * XMLRPC_VALUE * SOURCE */ char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len) { xml_element *root_elem = XMLRPC_VALUE_to_xml_element(val); char* pRet = NULL; if(root_elem) { pRet = xml_elem_serialize_to_string(root_elem, NULL, buf_len); xml_elem_free(root_elem); } return pRet; } /*******/ /****f* SERIALIZE/XMLRPC_REQUEST_ToXML * NAME * XMLRPC_REQUEST_ToXML * SYNOPSIS * char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request) * FUNCTION * encode XMLRPC_REQUEST into XML buffer * INPUTS * request -- previously allocated XMLRPC_REQUEST * buf_len -- size of returned buf, if not null * RESULT * char* -- newly allocated buffer containing XML. * It is the caller's responsibility to free it. * SEE ALSO * XMLRPC_REQUEST_ToXML () * XMLRPC_REQUEST_FromXML () * XMLRPC_Free () * XMLRPC_VALUE_ToXML () * XMLRPC_REQUEST * SOURCE */ char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int* buf_len) { if(request) { xml_element *root_elem = (request->output.version == xmlrpc_version_simple) ? DANDARPC_REQUEST_to_xml_element(request) : XMLRPC_REQUEST_to_xml_element(request); char* pRet = NULL; if(root_elem) { pRet = xml_elem_serialize_to_string(root_elem, &request->output.xml_elem_opts, buf_len); xml_elem_free(root_elem); } return pRet; } } /*******/ /****f* SERIALIZE/XMLRPC_VALUE_FromXML * NAME * XMLRPC_VALUE_FromXML * SYNOPSIS * XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int le * FUNCTION * Retrieve XMLRPC_VALUE from XML buffer. Note that this will * ignore any methodCall. See XMLRPC_REQUEST_FromXML * INPUTS * in_buf -- character buffer containing XML * len -- length of buffer * RESULT * XMLRPC_VALUE -- newly allocated data, or NULL if error. Should * be free'd by caller. * SEE ALSO * XMLRPC_VALUE_ToXML () * XMLRPC_REQUEST_FromXML () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) { XMLRPC_VALUE xResponse = NULL; XMLRPC_REQUEST req = XMLRPC_REQUEST_FromXML(in_buf, len, in_options); if(req) { xResponse = req->io; XMLRPC_RequestFree(req, 0); } return xResponse; } /*******/ /* map parser errors to standard xml-rpc errors */ static XMLRPC_VALUE map_expat_errors(XML_ELEM_ERROR error) { XMLRPC_VALUE xReturn = NULL; if(error) { XMLRPC_ERROR_CODE code; char buf[1024]; snprintf(buf, sizeof(buf), "error occurred at line %i, column %i, byte index %i", error->line, error->column, error->byte_index); /* expat specific errors */ switch(error->parser_code) { case XML_ERROR_UNKNOWN_ENCODING: code = xmlrpc_error_parse_unknown_encoding; break; case XML_ERROR_INCORRECT_ENCODING: code = xmlrpc_error_parse_bad_encoding; break; default: code = xmlrpc_error_parse_xml_syntax; break; } xReturn = XMLRPC_UtilityCreateFault(code, buf); } return xReturn; } /****f* SERIALIZE/XMLRPC_REQUEST_FromXML * NAME * XMLRPC_REQUEST_FromXML * SYNOPSIS * XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int le * FUNCTION * Retrieve XMLRPC_REQUEST from XML buffer * INPUTS * in_buf -- character buffer containing XML * len -- length of buffer * RESULT * XMLRPC_REQUEST -- newly allocated data, or NULL if error. Should * be free'd by caller. * SEE ALSO * XMLRPC_REQUEST_ToXML () * XMLRPC_VALUE_FromXML () * XMLRPC_REQUEST * SOURCE */ XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) { XMLRPC_REQUEST request = XMLRPC_RequestNew(); STRUCT_XML_ELEM_ERROR error = {0}; if(request) { xml_element *root_elem = xml_elem_parse_buf(in_buf, len, (in_options ? &in_options->xml_elem_opts : NULL), &error); if(root_elem) { if(!strcmp(root_elem->name, "simpleRPC")) { request->output.version = xmlrpc_version_simple; xml_element_to_DANDARPC_REQUEST(request, root_elem); } else { request->output.version = xmlrpc_version_1_0; xml_element_to_XMLRPC_REQUEST(request, root_elem); } xml_elem_free(root_elem); } else { if(error.parser_error) { request->error = map_expat_errors(&error); } } } return request; } /*******/ /*-************************ * End Serialization Funcs * **************************/ /****f* VALUE/XMLRPC_CreateValueEmpty * NAME * XMLRPC_CreateValueEmpty * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueEmpty () * FUNCTION * Create an XML value to be used/modified elsewhere. * INPUTS * RESULT * XMLRPC_VALUE. The new value, or NULL on failure. * SEE ALSO * XMLRPC_CleanupValue () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueEmpty() { XMLRPC_VALUE v = calloc(1, sizeof(STRUCT_XMLRPC_VALUE)); if(v) { v->type = xmlrpc_empty; simplestring_init(&v->id); simplestring_init(&v->str); } return v; } static const char* get_string(const char* buf, int bDup) { if(bDup) { return strdup(buf); } return buf; } /*******/ /****f* VALUE/XMLRPC_SetValueID_Case * NAME * XMLRPC_SetValueID_Case * SYNOPSIS * const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case) * FUNCTION * Assign an ID (key) to an XMLRPC value. * INPUTS * value The xml value who's ID we will set. * id The desired new id. * len length of id string if known, or 0 if unknown. * id_case one of XMLRPC_CASE * RESULT * const char* pointer to the newly allocated id string, or NULL * SEE ALSO * XMLRPC_SetValueID () * XMLRPC_GetValueID () * XMLRPC_VALUE * XMLRPC_CASE * SOURCE */ const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case) { const char* pRetval = NULL; if(value) { if(id) { simplestring_clear(&value->id); (len > 0) ? simplestring_addn(&value->id, id, len) : simplestring_add(&value->id, id); /* upper or lower case string in place if required. could be a seperate func. */ if(id_case == xmlrpc_case_lower || id_case == xmlrpc_case_upper) { int i; for(i = 0; i < value->id.len; i++) { value->id.str[i] = (id_case == xmlrpc_case_lower) ? tolower(value->id.str[i]) : toupper(value->id.str[i]); } } pRetval = value->id.str; #ifdef XMLRPC_DEBUG_REFCOUNT printf("set value id: %s\n", pRetval); #endif } } return pRetval; } /*******/ /****f* VALUE/XMLRPC_SetValueString * NAME * XMLRPC_SetValueString * SYNOPSIS * const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) * FUNCTION * Assign a string value to an XMLRPC_VALUE, and set it to type xmlrpc_string * INPUTS * value The xml value who's ID we will set. * val The desired new string val. * len length of val string if known, or 0 if unknown. * RESULT * const char* pointer to the newly allocated value string, or NULL * SEE ALSO * XMLRPC_GetValueString () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) { char *pRetval = NULL; if(value && val) { simplestring_clear(&value->str); (len > 0) ? simplestring_addn(&value->str, val, len) : simplestring_add(&value->str, val); value->type = xmlrpc_string; pRetval = (char *)value->str.str; } return pRetval; } /*******/ /****f* VALUE/XMLRPC_SetValueInt * NAME * XMLRPC_SetValueInt * SYNOPSIS * void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) * FUNCTION * Assign an int value to an XMLRPC_VALUE, and set it to type xmlrpc_int * INPUTS * value The xml value who's ID we will set. * val The desired new integer value * RESULT * SEE ALSO * XMLRPC_GetValueInt () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) { if(value) { value->type = xmlrpc_int; value->i = val; } } /*******/ /****f* VALUE/XMLRPC_SetValueBoolean * NAME * XMLRPC_SetValueBoolean * SYNOPSIS * void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) * FUNCTION * Assign a boolean value to an XMLRPC_VALUE, and set it to type xmlrpc_boolean * INPUTS * value The xml value who's value we will set. * val The desired new boolean value. [0 | 1] * RESULT * SEE ALSO * XMLRPC_GetValueBoolean () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) { if(value) { value->type = xmlrpc_boolean; value->i = val ? 1 : 0; } } /*******/ /****f* VECTOR/XMLRPC_SetIsVector * NAME * XMLRPC_SetIsVector * SYNOPSIS * int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) * FUNCTION * Set the XMLRPC_VALUE to be a vector (list) type. The vector may be one of * [xmlrpc_array | xmlrpc_struct | xmlrpc_mixed]. An array has only index values. * A struct has key/val pairs. Mixed allows both index and key/val combinations. * INPUTS * value The xml value who's vector type we will set * type New type of vector as enumerated by XMLRPC_VECTOR_TYPE * RESULT * int 1 if successful, 0 otherwise * SEE ALSO * XMLRPC_GetValueType () * XMLRPC_GetVectorType () * XMLRPC_VALUE * XMLRPC_VECTOR_TYPE * XMLRPC_VALUE_TYPE * SOURCE */ int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) { int bSuccess = 0; if(value && value->type != xmlrpc_vector) { value->v = calloc(1, sizeof(STRUCT_XMLRPC_VECTOR)); if(value->v) { value->v->q = (queue*)malloc(sizeof(queue)); if(value->v->q) { Q_Init(value->v->q); value->v->type = type; value->type = xmlrpc_vector; bSuccess = 1; } } } return bSuccess; } /*******/ /****f* VECTOR/XMLRPC_CreateVector * NAME * XMLRPC_CreateVector * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) * FUNCTION * Create a new vector and optionally set an id. * INPUTS * id The id of the vector, or NULL * type New type of vector as enumerated by XMLRPC_VECTOR_TYPE * RESULT * XMLRPC_VALUE The new vector, or NULL on failure. * SEE ALSO * XMLRPC_CreateValueEmpty () * XMLRPC_SetIsVector () * XMLRPC_GetValueType () * XMLRPC_GetVectorType () * XMLRPC_VALUE * XMLRPC_VECTOR_TYPE * XMLRPC_VALUE_TYPE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) { XMLRPC_VALUE val = NULL; val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_VECTOR *pSIV = NULL; if(XMLRPC_SetIsVector(val, type)) { if(id) { const char *pSVI = NULL; pSVI = XMLRPC_SetValueID(val, id, 0); if(NULL == pSVI) { val = NULL; } } } else { val = NULL; } } return val; } /*******/ /* Not yet implemented. * * This should use a hash to determine if a given target id has already * been appended. * * Alternately, it could walk the entire vector, but that could be quite * slow for very large lists. */ static int isDuplicateEntry(XMLRPC_VALUE target, XMLRPC_VALUE source) { return 0; } /****f* VECTOR/XMLRPC_AddValueToVector * NAME * XMLRPC_AddValueToVector * SYNOPSIS * int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) * FUNCTION * Add (append) an existing XMLRPC_VALUE to a vector. * INPUTS * target The target vector * source The source value to append * RESULT * int 1 if successful, else 0 * SEE ALSO * XMLRPC_AddValuesToVector () * XMLRPC_VectorGetValueWithID_Case () * XMLRPC_VALUE * NOTES * The function will fail and return 0 if an attempt is made to add * a value with an ID into a vector of type xmlrpc_vector_array. Such * values can only be added to xmlrpc_vector_struct. * SOURCE */ int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) { if(target && source) { if(target->type == xmlrpc_vector && target->v && target->v->q && target->v->type != xmlrpc_vector_none) { /* guard against putting value of unknown type into vector */ switch(source->type) { case xmlrpc_empty: case xmlrpc_base64: case xmlrpc_boolean: case xmlrpc_datetime: case xmlrpc_double: case xmlrpc_int: case xmlrpc_string: case xmlrpc_vector: /* Guard against putting a key/val pair into an array vector */ if( !(source->id.len && target->v->type == xmlrpc_vector_array) ) { if(isDuplicateEntry(target, source) || Q_PushTail(target->v->q, XMLRPC_CopyValue(source))) { return 1; } } else { fprintf(stderr, "xmlrpc: attempted to add key/val pair to vector of type array\n"); } break; default: fprintf(stderr, "xmlrpc: attempted to add value of unknown type to vector\n"); break; } } } return 0; } /*******/ /****f* VECTOR/XMLRPC_AddValuesToVector * NAME * XMLRPC_AddValuesToVector * SYNOPSIS * XMLRPC_AddValuesToVector ( target, val1, val2, val3, val(n), 0 ) * XMLRPC_AddValuesToVector( XMLRPC_VALUE, ... ) * FUNCTION * Add (append) a series of existing XMLRPC_VALUE to a vector. * INPUTS * target The target vector * ... The source value(s) to append. The last item *must* be 0. * RESULT * int 1 if successful, else 0 * SEE ALSO * XMLRPC_AddValuesToVector () * XMLRPC_VectorGetValueWithID_Case () * XMLRPC_VALUE * NOTES * This function may actually return failure after it has already modified * or added items to target. You can not trust the state of target * if this function returns failure. * SOURCE */ int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) { int iRetval = 0; if(target) { if(target->type == xmlrpc_vector) { XMLRPC_VALUE v = NULL; va_list vl; va_start(vl, target); do { v = va_arg(vl, XMLRPC_VALUE); if(v) { if(!XMLRPC_AddValueToVector(target, v)) { iRetval = 0; break; } } } while(v); va_end(vl); if(NULL == v) { iRetval = 1; } } } return iRetval; } /*******/ /****f* VECTOR/XMLRPC_VectorGetValueWithID_Case * NAME * XMLRPC_VectorGetValueWithID_Case * SYNOPSIS * XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) * FUNCTION * Get value from vector matching id (key) * INPUTS * vector The source vector * id The key to find * id_case Rule for how to match key * RESULT * int 1 if successful, else 0 * SEE ALSO * XMLRPC_SetValueID_Case () * XMLRPC_VALUE * XMLRPC_CASE_COMPARISON * SOURCE */ XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) { if(vector && vector->v && vector->v->q) { q_iter qi = Q_Iter_Head_F(vector->v->q); while(qi) { XMLRPC_VALUE xIter = Q_Iter_Get_F(qi); if(xIter && xIter->id.str) { if(id_case == xmlrpc_case_sensitive) { if(!strcmp(xIter->id.str, id)) { return xIter; } } else if(id_case == xmlrpc_case_insensitive) { if(!strcasecmp(xIter->id.str, id)) { return xIter; } } } qi = Q_Iter_Next_F(qi); } } return NULL; } /*******/ int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value) { if(vector && vector->v && vector->v->q && value) { q_iter qi = Q_Iter_Head_F(vector->v->q); while(qi) { XMLRPC_VALUE xIter = Q_Iter_Get_F(qi); if(xIter == value) { XMLRPC_CleanupValue(xIter); Q_Iter_Del(vector->v->q, qi); return 1; } qi = Q_Iter_Next_F(qi); } } return 0; } /****f* VALUE/XMLRPC_CreateValueString * NAME * XMLRPC_CreateValueString * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) * FUNCTION * Create an XMLRPC_VALUE, and assign a string to it * INPUTS * id The id of the value, or NULL * val The desired new string val. * len length of val string if known, or 0 if unknown. * RESULT * newly allocated XMLRPC_VALUE, or NULL * SEE ALSO * XMLRPC_GetValueString () * XMLRPC_CreateValueEmpty () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) { XMLRPC_VALUE value = NULL; if(val) { value = XMLRPC_CreateValueEmpty(); if(value) { XMLRPC_SetValueString(value, val, len); if(id) { XMLRPC_SetValueID(value, id, 0); } } } return value; } /*******/ /****f* VALUE/XMLRPC_CreateValueInt * NAME * XMLRPC_CreateValueInt * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) * FUNCTION * Create an XMLRPC_VALUE, and assign an int to it * INPUTS * id The id of the value, or NULL * i The desired new int val. * RESULT * newly allocated XMLRPC_VALUE, or NULL * SEE ALSO * XMLRPC_GetValueInt () * XMLRPC_CreateValueEmpty () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueInt(val, i); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_CreateValueBoolean * NAME * XMLRPC_CreateValueBoolean * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) * FUNCTION * Create an XMLRPC_VALUE, and assign an int to it * INPUTS * id The id of the value, or NULL * i The desired new int val. * RESULT * newly allocated XMLRPC_VALUE, or NULL * SEE ALSO * XMLRPC_GetValueBoolean () * XMLRPC_CreateValueEmpty () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueBoolean(val, i); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_CleanupValue * NAME * XMLRPC_CleanupValue * SYNOPSIS * void XMLRPC_CleanupValue(XMLRPC_VALUE value) * FUNCTION * Frees all memory allocated for an XMLRPC_VALUE and any of its children (if a vector) * INPUTS * value The id of the value to be cleaned up. * RESULT * void * NOTES * Normally this function will be called for the topmost vector, thus free-ing * all children. If a child of a vector is free'd first, results are undefined. * Failure to call this function *will* cause memory leaks. * * Also, this function is implemented using reference counting. Thus a value * may be added and freed from multiple parents so long as a reference is added * first using XMLRPC_CopyValue() * SEE ALSO * XMLRPC_RequestFree () * XMLRPC_CreateValueEmpty () * XMLRPC_CopyValue() * XMLRPC_VALUE * SOURCE */ void XMLRPC_CleanupValue(XMLRPC_VALUE value) { if(value) { if(value->iRefCount > 0) { value->iRefCount --; } #ifdef XMLRPC_DEBUG_REFCOUNT if(value->id.str) { printf("decremented refcount of %s, now %i\n", value->id.str, value->iRefCount); } else { printf("decremented refcount of 0x%x, now %i\n", value, value->iRefCount); } #endif if(value->type == xmlrpc_vector) { if(value->v) { if(value->iRefCount == 0) { XMLRPC_VALUE cur = (XMLRPC_VALUE)Q_Head(value->v->q); while( cur ) { XMLRPC_CleanupValue(cur); /* Make sure some idiot didn't include a vector as a child of itself * and thus it would have already free'd these. */ if(value->v && value->v->q) { cur = Q_Next(value->v->q); } else { break; } } Q_Destroy(value->v->q); my_free(value->v->q); my_free(value->v); } } } if(value->iRefCount == 0) { /* guard against freeing invalid types */ switch(value->type) { case xmlrpc_empty: case xmlrpc_base64: case xmlrpc_boolean: case xmlrpc_datetime: case xmlrpc_double: case xmlrpc_int: case xmlrpc_string: case xmlrpc_vector: #ifdef XMLRPC_DEBUG_REFCOUNT if(value->id.str) { printf("free'd %s\n", value->id.str); } else { printf("free'd 0x%x\n", value); } #endif simplestring_free(&value->id); simplestring_free(&value->str); memset(value, 0, sizeof(STRUCT_XMLRPC_VALUE)); my_free(value); break; default: fprintf(stderr, "xmlrpc: attempted to free value of invalid type\n"); break; } } } } /*******/ /****f* VALUE/XMLRPC_SetValueDateTime * NAME * XMLRPC_SetValueDateTime * SYNOPSIS * void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) * FUNCTION * Assign time value to XMLRPC_VALUE * INPUTS * value The target XMLRPC_VALUE * time The desired new unix time value (time_t) * RESULT * void * SEE ALSO * XMLRPC_GetValueDateTime () * XMLRPC_SetValueDateTime_ISO8601 () * XMLRPC_CreateValueDateTime () * XMLRPC_VALUE * SOURCE */ void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) { if(value) { char timeBuf[30]; value->type = xmlrpc_datetime; value->i = time; timeBuf[0] = 0; date_to_ISO8601(time, timeBuf, sizeof(timeBuf)); if(timeBuf[0]) { simplestring_clear(&value->str); simplestring_add(&value->str, timeBuf); } } } /*******/ /****f* VALUE/XMLRPC_CopyValue * NAME * XMLRPC_CopyValue * SYNOPSIS * XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) * FUNCTION * Make a copy of an XMLRPC_VALUE * INPUTS * value The target XMLRPC_VALUE * RESULT * XMLRPC_VALUE -- address of the copy * SEE ALSO * XMLRPC_CleanupValue () * NOTES * This function is implemented via reference counting, so the * returned value is going to be the same as the passed in value. * The value must be freed the same number of times it is copied * or there will be a memory leak. * SOURCE */ XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) { if(value) { value->iRefCount ++; #ifdef XMLRPC_DEBUG_REFCOUNT if(value->id.str) { printf("incremented refcount of %s\n", value->id.str); } #endif } return value; } /*******/ /****f* VALUE/XMLRPC_CreateValueDateTime * NAME * XMLRPC_CreateValueDateTime * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) * FUNCTION * Create new datetime value from time_t * INPUTS * id id of the new value, or NULL * time The desired unix time value (time_t) * RESULT * void * SEE ALSO * XMLRPC_GetValueDateTime () * XMLRPC_SetValueDateTime () * XMLRPC_CreateValueDateTime_ISO8601 () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueDateTime(val, time); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_SetValueDateTime_ISO8601 * NAME * XMLRPC_SetValueDateTime_ISO8601 * SYNOPSIS * void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) * FUNCTION * Set datetime value from IS08601 encoded string * INPUTS * value The target XMLRPC_VALUE * s The desired new time value * RESULT * void * BUGS * This function currently attempts to convert the time string to a valid unix time * value before passing it. Behavior when the string is invalid or out of range * is not well defined, but will probably result in Jan 1, 1970 (0) being passed. * SEE ALSO * XMLRPC_GetValueDateTime_ISO8601 () * XMLRPC_CreateValueDateTime_ISO8601 () * XMLRPC_CreateValueDateTime () * XMLRPC_VALUE * SOURCE */ void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) { if(value) { time_t time_val = 0; if(s) { date_from_ISO8601(s, &time_val); XMLRPC_SetValueDateTime(value, time_val); } } } /*******/ /****f* VALUE/XMLRPC_CreateValueDateTime_ISO8601 * NAME * XMLRPC_CreateValueDateTime_ISO8601 * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) * FUNCTION * Create datetime value from IS08601 encoded string * INPUTS * id The id of the new value, or NULL * s The desired new time value * RESULT * newly allocated XMLRPC_VALUE, or NULL if no value created. * BUGS * See XMLRPC_SetValueDateTime_ISO8601 () * SEE ALSO * XMLRPC_GetValueDateTime_ISO8601 () * XMLRPC_SetValueDateTime_ISO8601 () * XMLRPC_CreateValueDateTime () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueDateTime_ISO8601(val, s); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_SetValueBase64 * NAME * XMLRPC_SetValueBase64 * SYNOPSIS * void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) * FUNCTION * Set base64 value. Base64 is useful for transferring binary data, such as an image. * INPUTS * value The target XMLRPC_VALUE * s The desired new binary value * len The length of s, or NULL. If buffer is not null terminated, len *must* be passed. * RESULT * void * NOTES * Data is set/stored/retrieved as passed in, but is base64 encoded for XML transfer, and * decoded on the other side. This is transparent to the caller. * SEE ALSO * XMLRPC_GetValueBase64 () * XMLRPC_CreateValueBase64 () * XMLRPC_VALUE * SOURCE */ void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) { if(value && s) { simplestring_clear(&value->str); (len > 0) ? simplestring_addn(&value->str, s, len) : simplestring_add(&value->str, s); value->type = xmlrpc_base64; } } /*******/ /****f* VALUE/XMLRPC_CreateValueBase64 * NAME * XMLRPC_CreateValueBase64 * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) * FUNCTION * Create base64 value. Base64 is useful for transferring binary data, such as an image. * INPUTS * id id of the new value, or NULL * s The desired new binary value * len The length of s, or NULL. If buffer is not null terminated, len *must* be passed. * RESULT * newly allocated XMLRPC_VALUE, or NULL if error * NOTES * See XMLRPC_SetValueBase64 () * SEE ALSO * XMLRPC_GetValueBase64 () * XMLRPC_SetValueBase64 () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueBase64(val, s, len); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_SetValueDouble * NAME * XMLRPC_SetValueDouble * SYNOPSIS * void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) * FUNCTION * Set double (floating point) value. * INPUTS * value The target XMLRPC_VALUE * val The desired new double value * RESULT * void * SEE ALSO * XMLRPC_GetValueDouble () * XMLRPC_CreateValueDouble () * XMLRPC_VALUE * SOURCE */ void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) { if(value) { value->type = xmlrpc_double; value->d = val; } } /*******/ /****f* VALUE/XMLRPC_CreateValueDouble * NAME * XMLRPC_CreateValueDouble * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) * FUNCTION * Create double (floating point) value. * INPUTS * id id of the newly created value, or NULL * d The desired new double value * RESULT * void * SEE ALSO * XMLRPC_GetValueDouble () * XMLRPC_CreateValueDouble () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueDouble(val, d); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_GetValueString * NAME * XMLRPC_GetValueString * SYNOPSIS * const char* XMLRPC_GetValueString(XMLRPC_VALUE value) * FUNCTION * retrieve string value * INPUTS * value source XMLRPC_VALUE of type xmlrpc_string * RESULT * void * SEE ALSO * XMLRPC_SetValueString () * XMLRPC_GetValueType () * XMLRPC_VALUE * SOURCE */ const char* XMLRPC_GetValueString(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_string) ? value->str.str : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueStringLen * NAME * XMLRPC_GetValueStringLen * SYNOPSIS * int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) * FUNCTION * determine length of string value * INPUTS * value XMLRPC_VALUE of type xmlrpc_string * RESULT * length of string, or 0 * NOTES * SEE ALSO * XMLRPC_SetValueString () * XMLRPC_GetValueString () * SOURCE */ int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) { return ((value) ? value->str.len : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueInt * NAME * XMLRPC_GetValueInt * SYNOPSIS * int XMLRPC_GetValueInt(XMLRPC_VALUE value) * FUNCTION * retrieve integer value. * INPUTS * value XMLRPC_VALUE of type xmlrpc_int * RESULT * integer value or 0 if value is not valid int * NOTES * use XMLRPC_GetValueType () to be sure if 0 is real return value or not * SEE ALSO * XMLRPC_SetValueInt () * XMLRPC_CreateValueInt () * SOURCE */ int XMLRPC_GetValueInt(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_int) ? value->i : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueBoolean * NAME * XMLRPC_GetValueBoolean * SYNOPSIS * int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) * FUNCTION * retrieve boolean value. * INPUTS * XMLRPC_VALUE of type xmlrpc_boolean * RESULT * boolean value or 0 if value is not valid boolean * NOTES * use XMLRPC_GetValueType() to be sure if 0 is real value or not * SEE ALSO * XMLRPC_SetValueBoolean () * XMLRPC_CreateValueBoolean () * SOURCE */ int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_boolean) ? value->i : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueDouble * NAME * XMLRPC_GetValueDouble * SYNOPSIS * double XMLRPC_GetValueDouble(XMLRPC_VALUE value) * FUNCTION * retrieve double value * INPUTS * XMLRPC_VALUE of type xmlrpc_double * RESULT * double value or 0 if value is not valid double. * NOTES * use XMLRPC_GetValueType() to be sure if 0 is real value or not * SEE ALSO * XMLRPC_SetValueDouble () * XMLRPC_CreateValueDouble () * SOURCE */ double XMLRPC_GetValueDouble(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_double) ? value->d : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueBase64 * NAME * XMLRPC_GetValueBase64 * SYNOPSIS * const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) * FUNCTION * retrieve binary value * INPUTS * XMLRPC_VALUE of type xmlrpc_base64 * RESULT * pointer to binary value or 0 if value is not valid. * SEE ALSO * XMLRPC_SetValueBase64 () * XMLRPC_CreateValueBase64 () * NOTES * Call XMLRPC_GetValueStringLen() to retrieve real length of binary data. strlen() * will not be accurate, as returned data may contain embedded nulls. * SOURCE */ const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_base64) ? value->str.str : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueDateTime * NAME * XMLRPC_GetValueDateTime * SYNOPSIS * time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) * FUNCTION * retrieve time_t value * INPUTS * XMLRPC_VALUE of type xmlrpc_datetime * RESULT * time_t value or 0 if value is not valid datetime. * NOTES * use XMLRPC_GetValueType() to be sure if 0 is real value or not * SEE ALSO * XMLRPC_SetValueDateTime () * XMLRPC_GetValueDateTime_ISO8601 () * XMLRPC_CreateValueDateTime () * SOURCE */ time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) { return (time_t)((value && value->type == xmlrpc_datetime) ? value->i : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueDateTime_IOS8601 * NAME * XMLRPC_GetValueDateTime_IOS8601 * SYNOPSIS * const char* XMLRPC_GetValueDateTime_IOS8601(XMLRPC_VALUE value) * FUNCTION * retrieve ISO8601 formatted time value * INPUTS * XMLRPC_VALUE of type xmlrpc_datetime * RESULT * const char* value or 0 if value is not valid datetime. * SEE ALSO * XMLRPC_SetValueDateTime_IOS8601 () * XMLRPC_GetValueDateTime () * XMLRPC_CreateValueDateTime_IOS8601 () * SOURCE */ const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_datetime) ? value->str.str : 0); } /*******/ /* Get ID (key) of value or NULL */ /****f* VALUE/XMLRPC_GetValueID * NAME * XMLRPC_GetValueID * SYNOPSIS * const char* XMLRPC_GetValueID(XMLRPC_VALUE value) * FUNCTION * retrieve id (key) of value * INPUTS * XMLRPC_VALUE of any type * RESULT * const char* pointer to id of value, or NULL * NOTES * SEE ALSO * XMLRPC_SetValueID() * XMLRPC_CreateValueEmpty() * SOURCE */ const char* XMLRPC_GetValueID(XMLRPC_VALUE value) { return (const char*)((value && value->id.len) ? value->id.str : 0); } /*******/ /****f* VECTOR/XMLRPC_VectorSize * NAME * XMLRPC_VectorSize * SYNOPSIS * int XMLRPC_VectorSize(XMLRPC_VALUE value) * FUNCTION * retrieve size of vector * INPUTS * XMLRPC_VALUE of type xmlrpc_vector * RESULT * count of items in vector * NOTES * This is a cheap operation even on large vectors. Vector size is * maintained by queue during add/remove ops. * SEE ALSO * XMLRPC_AddValueToVector () * SOURCE */ int XMLRPC_VectorSize(XMLRPC_VALUE value) { int size = 0; if(value && value->type == xmlrpc_vector && value->v) { size = Q_Size(value->v->q); } return size; } /*******/ /****f* VECTOR/XMLRPC_VectorRewind * NAME * XMLRPC_VectorRewind * SYNOPSIS * XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) * FUNCTION * reset vector to first item * INPUTS * XMLRPC_VALUE of type xmlrpc_vector * RESULT * first XMLRPC_VALUE in list, or NULL if empty or error. * NOTES * Be careful to rewind any vector passed in to you if you expect to * iterate through the entire list. * SEE ALSO * XMLRPC_VectorNext () * SOURCE */ XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) { XMLRPC_VALUE xReturn = NULL; if(value && value->type == xmlrpc_vector && value->v) { xReturn = (XMLRPC_VALUE)Q_Head(value->v->q); } return xReturn; } /*******/ /****f* VECTOR/XMLRPC_VectorNext * NAME * XMLRPC_VectorNext * SYNOPSIS * XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) * FUNCTION * Iterate vector to next item in list. * INPUTS * XMLRPC_VALUE of type xmlrpc_vector * RESULT * Next XMLRPC_VALUE in vector, or NULL if at end. * NOTES * SEE ALSO * XMLRPC_VectorRewind () * SOURCE */ XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) { XMLRPC_VALUE xReturn = NULL; if(value && value->type == xmlrpc_vector && value->v) { xReturn = (XMLRPC_VALUE)Q_Next(value->v->q); } return xReturn; } /*******/ /****f* VALUE/XMLRPC_GetValueType * NAME * XMLRPC_GetValueType * SYNOPSIS * XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) * FUNCTION * determine data type of the XMLRPC_VALUE * INPUTS * XMLRPC_VALUE target of query * RESULT * data type of value as enumerated by XMLRPC_VALUE_TYPE * NOTES * all values are of type xmlrpc_empty until set. * SEE ALSO * XMLRPC_SetValue* * XMLRPC_CreateValue* * XMLRPC_Append* * SOURCE */ XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) { return value ? value->type : xmlrpc_empty; } /*******/ /* Vector type accessor */ /****f* VALUE/XMLRPC_GetVectorType * NAME * XMLRPC_GetVectorType * SYNOPSIS * XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) * FUNCTION * determine vector type of the XMLRPC_VALUE * INPUTS * XMLRPC_VALUE of type xmlrpc_vector * RESULT * vector type of value as enumerated by XMLRPC_VECTOR_TYPE * NOTES * xmlrpc_none is returned if value is not a vector * SEE ALSO * XMLRPC_SetIsVector () * XMLRPC_GetValueType () * SOURCE */ XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) { return(value && value->v) ? value->v->type : xmlrpc_none; } /*******/ /*-******************* * Begin Server Funcs * *********************/ /****f* VALUE/XMLRPC_ServerCreate * NAME * XMLRPC_ServerCreate * SYNOPSIS * XMLRPC_SERVER XMLRPC_ServerCreate() * FUNCTION * Allocate/Init XMLRPC Server Resources. * INPUTS * none * RESULT * newly allocated XMLRPC_SERVER * NOTES * SEE ALSO * XMLRPC_ServerDestroy () * XMLRPC_GetGlobalServer () * SOURCE */ XMLRPC_SERVER XMLRPC_ServerCreate() { XMLRPC_SERVER server = calloc(1, sizeof(STRUCT_XMLRPC_SERVER)); if(server) { Q_Init(&server->methodlist); Q_Init(&server->docslist); /* register system methods */ xsm_register(server); } return server; } /*******/ /* Return global server. Not locking! Not Thread Safe! */ /****f* VALUE/XMLRPC_GetGlobalServer * NAME * XMLRPC_GetGlobalServer * SYNOPSIS * XMLRPC_SERVER XMLRPC_GetGlobalServer() * FUNCTION * Allocates a global (process-wide) server, or returns pointer if pre-existing. * INPUTS * none * RESULT * pointer to global server, or 0 if error. * NOTES * ***WARNING*** This function is not thread safe. It is included only for the very lazy. * Multi-threaded programs that use this may experience problems. * BUGS * There is currently no way to cleanup the global server gracefully. * SEE ALSO * XMLRPC_ServerCreate () * SOURCE */ XMLRPC_SERVER XMLRPC_GetGlobalServer() { static XMLRPC_SERVER xsServer = 0; if(!xsServer) { xsServer = XMLRPC_ServerCreate(); } return xsServer; } /*******/ /****f* VALUE/XMLRPC_ServerDestroy * NAME * XMLRPC_ServerDestroy * SYNOPSIS * void XMLRPC_ServerDestroy(XMLRPC_SERVER server) * FUNCTION * Free Server Resources * INPUTS * server The server to be free'd * RESULT * void * NOTES * This frees the server struct and any methods that have been added. * SEE ALSO * XMLRPC_ServerCreate () * SOURCE */ void XMLRPC_ServerDestroy(XMLRPC_SERVER server) { if(server) { doc_method* dm = Q_Head(&server->docslist); server_method* sm = Q_Head(&server->methodlist); while( dm ) { my_free(dm); dm = Q_Next(&server->docslist); } while( sm ) { if(sm->name) { my_free(sm->name); } if(sm->desc) { XMLRPC_CleanupValue(sm->desc); } my_free(sm); sm = Q_Next(&server->methodlist); } if(server->xIntrospection) { XMLRPC_CleanupValue(server->xIntrospection); } Q_Destroy(&server->methodlist); Q_Destroy(&server->docslist); my_free(server); } } /*******/ /****f* VALUE/XMLRPC_ServerRegisterMethod * NAME * XMLRPC_ServerRegisterMethod * SYNOPSIS * void XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb) * FUNCTION * Register new XMLRPC method with server * INPUTS * server The XMLRPC_SERVER to register the method with * name public name of the method * cb C function that implements the method * RESULT * int - 1 if success, else 0 * NOTES * A C function must be registered for every "method" that the server recognizes. The * method name is equivalent to <methodCall><name> method name </name></methodCall> in the * XML syntax. * SEE ALSO * XMLRPC_ServerFindMethod () * XMLRPC_ServerCallMethod () * SOURCE */ int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb) { if(server && name && cb) { server_method* sm = malloc(sizeof(server_method)); if(sm) { sm->name = strdup(name); sm->method = cb; sm->desc = NULL; return Q_PushTail(&server->methodlist, sm); } } return 0; } /*******/ inline server_method* find_method(XMLRPC_SERVER server, const char* name) { server_method* sm; q_iter qi = Q_Iter_Head_F(&server->methodlist); while( qi ) { sm = Q_Iter_Get_F(qi); if(sm && !strcmp(sm->name, name)) { return sm; } qi = Q_Iter_Next_F(qi); } return NULL; } const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) { switch(type) { case xmlrpc_none: return "none"; case xmlrpc_empty: return "empty"; case xmlrpc_base64: return "base64"; case xmlrpc_boolean: return "boolean"; case xmlrpc_datetime: return "datetime"; case xmlrpc_double: return "double"; case xmlrpc_int: return "int"; case xmlrpc_string: return "string"; case xmlrpc_vector: switch(vtype) { case xmlrpc_vector_none: return "none"; case xmlrpc_vector_array: return "array"; case xmlrpc_vector_mixed: return "mixed vector (struct)"; case xmlrpc_vector_struct: return "struct"; } } } /****f* VALUE/XMLRPC_ServerFindMethod * NAME * XMLRPC_ServerFindMethod * SYNOPSIS * XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName) * FUNCTION * retrieve C callback associated with a given method name. * INPUTS * server The XMLRPC_SERVER the method is registered with * callName the method to find * RESULT * previously registered XMLRPC_Callback, or NULL * NOTES * Typically, this is used to determine if a requested method exists, without actually calling it. * SEE ALSO * XMLRPC_ServerCallMethod () * XMLRPC_ServerRegisterMethod () * SOURCE */ XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName) { if(server && callName) { q_iter qi = Q_Iter_Head_F(&server->methodlist); while( qi ) { server_method* sm = Q_Iter_Get_F(qi); if(sm && !strcmp(sm->name, callName)) { return sm->method; } qi = Q_Iter_Next_F(qi); } } return NULL; } /*******/ /* Call method specified in request */ /****f* VALUE/XMLRPC_ServerCallMethod * NAME * XMLRPC_ServerCallMethod * SYNOPSIS * XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData) * FUNCTION * * INPUTS * server The XMLRPC_SERVER the method is registered with * request the request to handle * userData any additional data to pass to the C callback, or NULL * RESULT * XMLRPC_VALUE allocated by the callback, or NULL * NOTES * It is typically the caller's responsibility to free the returned value. * * Often the caller will want to serialize the result as XML, via * XMLRPC_VALUE_To_XML () or XMLRPC_REQUEST_To_XML () * SEE ALSO * XMLRPC_ServerFindMethod () * XMLRPC_ServerRegisterMethod () * XMLRPC_CleanupValue () * SOURCE */ XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData) { XMLRPC_VALUE xReturn = NULL; /* check for error set during request parsing / generation */ if(request && request->error) { xReturn = XMLRPC_CopyValue(request->error); } else if(server && request && request->methodName.str) { XMLRPC_Callback cb = XMLRPC_ServerFindMethod(server, request->methodName.str); if(cb) { xReturn = cb(server, request, userData); } else { xReturn = XMLRPC_UtilityCreateFault(xmlrpc_error_unknown_method, request->methodName.str); } } return xReturn; } /*******/ /*-***************** * End server funcs * *******************/ /*-*********************************** * Begin XMLRPC General Options funcs * *************************************/ /* For options used by XMLRPC_VALUE funcs that otherwise do not have * parameters for options. Kind of gross. :( */ typedef struct _xmlrpc_options { XMLRPC_CASE id_case; XMLRPC_CASE_COMPARISON id_case_compare; } STRUCT_XMLRPC_OPTIONS, *XMLRPC_OPTIONS; static XMLRPC_OPTIONS XMLRPC_GetDefaultOptions() { static STRUCT_XMLRPC_OPTIONS options = { xmlrpc_case_exact, xmlrpc_case_sensitive }; return &options; } /****f* VALUE/XMLRPC_GetDefaultIdCase * NAME * XMLRPC_GetDefaultIdCase * SYNOPSIS * XMLRPC_CASE XMLRPC_GetDefaultIdCase() * FUNCTION * Gets default case options used by XMLRPC_VALUE funcs * INPUTS * none * RESULT * XMLRPC_CASE * BUGS * Nasty and gross. Should be server specific, but that requires changing all * the XMLRPC_VALUE api's. * SEE ALSO * XMLRPC_SetDefaultIdCase () * SOURCE */ XMLRPC_CASE XMLRPC_GetDefaultIdCase() { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); return options->id_case; } /*******/ /****f* VALUE/XMLRPC_SetDefaultIdCase * NAME * XMLRPC_SetDefaultIdCase * SYNOPSIS * XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) * FUNCTION * Sets default case options used by XMLRPC_VALUE funcs * INPUTS * id_case case options as enumerated by XMLRPC_CASE * RESULT * XMLRPC_CASE -- newly set option * BUGS * Nasty and gross. Should be server specific, but that requires changing all * the XMLRPC_VALUE api's. * SEE ALSO * XMLRPC_GetDefaultIdCase () * SOURCE */ XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); options->id_case = id_case; return options->id_case; } /*******/ /****f* VALUE/XMLRPC_GetDefaultIdCaseComparison * NAME * XMLRPC_GetDefaultIdCaseComparison * SYNOPSIS * XMLRPC_CASE XMLRPC_GetDefaultIdCaseComparison( ) * FUNCTION * Gets default case comparison options used by XMLRPC_VALUE funcs * INPUTS * none * RESULT * XMLRPC_CASE_COMPARISON default * BUGS * Nasty and gross. Should be server specific, but that requires changing all * the XMLRPC_VALUE api's. * SEE ALSO * XMLRPC_SetDefaultIdCaseComparison () * SOURCE */ XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison() { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); return options->id_case_compare; } /*******/ /****f* VALUE/XMLRPC_SetDefaultIdCaseComparison * NAME * XMLRPC_SetDefaultIdCaseComparison * SYNOPSIS * XMLRPC_CASE XMLRPC_SetDefaultIdCaseComparison( XMLRPC_CASE_COMPARISON id_case_compare ) * FUNCTION * Gets default case comparison options used by XMLRPC_VALUE funcs * INPUTS * id_case_compare case comparison rule to set as default * RESULT * XMLRPC_CASE_COMPARISON newly set default * BUGS * Nasty and gross. Should be server specific, but that requires changing all * the XMLRPC_VALUE api's. * SEE ALSO * XMLRPC_GetDefaultIdCaseComparison () * SOURCE */ XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case_compare) { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); options->id_case_compare = id_case_compare; return options->id_case_compare; } /*******/ /*-********************************* * End XMLRPC General Options funcs * ***********************************/ /*-****************** * Utility API funcs * ********************/ /****f* UTILITY/XMLRPC_UtilityCreateFault * NAME * XMLRPC_UtilityCreateFault * SYNOPSIS * XMLRPC_VALUE XMLRPC_UtilityCreateFault( int fault_code, const char* fault_string ) * FUNCTION * generates a struct containing a string member with id "faultString" and an int member * with id "faultCode". When using the xmlrpc xml serialization, these will be translated * to <fault><value><struct>... format. * INPUTS * fault_code application specific error code. can be 0. * fault_string application specific error string. cannot be null. * RESULT * XMLRPC_VALUE a newly created struct vector representing the error, or null on error. * NOTES * This is a utility function. xmlrpc "faults" are not directly represented in this xmlrpc * API or data structures. It is the author's view, that this API is intended for simple * data types, and a "fault" is a complex data type consisting of multiple simple data * types. This function is provided for convenience only, the same result could be * achieved directly by the application. * * This function now supports some "standardized" fault codes, as specified at. * http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php. * If one of these fault codes is received, the description string will automatically * be prefixed with a standard error string and 2 newlines. * * The actual transformation between this complex type and the xml "<fault>" element takes * place in the xmlrpc to xml serialization layer. This step is not performed when using the * simplerpc serialization, meaning that there will be no "<fault>" element in that * serialization. There will simply be a standard struct with 2 child elements. * imho, the "<fault>" element is unnecessary and/or out of place as part of the standard API. * SOURCE */ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) { XMLRPC_VALUE xOutput = NULL; char* string = NULL; simplestring description; simplestring_init(&description); switch (fault_code) { case xmlrpc_error_parse_xml_syntax: string = xmlrpc_error_parse_xml_syntax_str; break; case xmlrpc_error_parse_unknown_encoding: string = xmlrpc_error_parse_unknown_encoding_str; break; case xmlrpc_error_parse_bad_encoding: string = xmlrpc_error_parse_bad_encoding_str; break; case xmlrpc_error_invalid_xmlrpc: string = xmlrpc_error_invalid_xmlrpc_str; break; case xmlrpc_error_unknown_method: string = xmlrpc_error_unknown_method_str; break; case xmlrpc_error_invalid_params: string = xmlrpc_error_invalid_params_str; break; case xmlrpc_error_internal_server: string = xmlrpc_error_internal_server_str; break; case xmlrpc_error_application: string = xmlrpc_error_application_str; break; case xmlrpc_error_system: string = xmlrpc_error_system_str; break; case xmlrpc_error_transport: string = xmlrpc_error_transport_str; break; } simplestring_add(&description, string); if(string && fault_string) { simplestring_add(&description, "\n\n"); } simplestring_add(&description, fault_string); if(description.len) { xOutput = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); XMLRPC_VectorAppendString(xOutput, "faultString", description.str, description.len); XMLRPC_VectorAppendInt(xOutput, "faultCode", fault_code); } simplestring_free(&description); return xOutput; } /*******/ /****f* UTILITY/XMLRPC_Free * NAME * XMLRPC_Free * SYNOPSIS * void XMLRPC_Free(void* mem) * FUNCTION * frees a block of memory allocated by xmlrpc. * INPUTS * mem memory to free * RESULT * void * NOTES * Useful for OS's where memory must be free'd * in the same library in which it is allocated. * SOURCE */ void XMLRPC_Free(void* mem) { my_free(mem); } /*******/ /****f* UTILITY/XMLRPC_GetVersionString * NAME * XMLRPC_GetVersionString * SYNOPSIS * const char* XMLRPC_GetVersionString() * FUNCTION * returns library version string * INPUTS * * RESULT * const char* * NOTES * SOURCE */ const char* XMLRPC_GetVersionString() { return XMLRPC_VERSION_STR; } /*******/ /*-********************** * End Utility API funcs * ************************/ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc.h +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef XMLRPC_ALREADY_INCLUDED #define XMLRPC_ALREADY_INCLUDED 1 /* includes */ #include "xml_element.h" #include <time.h> /* for time_t */ #ifdef __cplusplus extern "C" { #endif /* this number, representing the date, must be increased each time the API changes */ #define XMLRPC_API_NO 20010721 /* this string should be changed with each packaged release */ #define XMLRPC_VERSION_STR "xmlrpc-epi v. " VERSION /* where to find more info. shouldn't need to change much */ #define XMLRPC_HOME_PAGE_STR "http://xmlprc-epi.sourceforge.net/" /****d* VALUE/XMLRPC_VALUE_TYPE * NAME * XMLRPC_VALUE_TYPE * NOTES * Defines data types for XMLRPC_VALUE * SEE ALSO * XMLRPC_VECTOR_TYPE * XMLRPC_REQUEST_TYPE * SOURCE */ typedef enum _XMLRPC_VALUE_TYPE { xmlrpc_none, /* not a value */ xmlrpc_empty, /* empty value, eg NULL */ xmlrpc_base64, /* base64 value, eg binary data */ xmlrpc_boolean, /* boolean [0 | 1] */ xmlrpc_datetime, /* datetime [ISO8601 | time_t] */ xmlrpc_double, /* double / floating point */ xmlrpc_int, /* integer */ xmlrpc_string, /* string */ xmlrpc_vector /* vector, aka list, array */ } XMLRPC_VALUE_TYPE; /*******/ /****d* VALUE/XMLRPC_VECTOR_TYPE * NAME * XMLRPC_VECTOR_TYPE * NOTES * Defines data types for XMLRPC_VECTOR * SEE ALSO * XMLRPC_VALUE_TYPE * XMLRPC_REQUEST_TYPE * SOURCE */ typedef enum _XMLRPC_VECTOR_TYPE { xmlrpc_vector_none, /* not an array */ xmlrpc_vector_array, /* no values may have key names */ xmlrpc_vector_mixed, /* some values may have key names */ xmlrpc_vector_struct /* all values must have key names */ } XMLRPC_VECTOR_TYPE; /*******/ /****d* VALUE/XMLRPC_REQUEST_TYPE * NAME * XMLRPC_REQUEST_TYPE * NOTES * Defines data types for XMLRPC_REQUEST * SEE ALSO * XMLRPC_VALUE_TYPE * XMLRPC_VECTOR_TYPE * SOURCE */ typedef enum _xmlrpc_request_type { xmlrpc_request_none, /* not a valid request */ xmlrpc_request_call, /* calling/invoking a method */ xmlrpc_request_response, /* responding to a method call */ } XMLRPC_REQUEST_TYPE; /*******/ /****d* VALUE/XMLRPC_ERROR_CODE * NAME * XMLRPC_ERROR_CODE * NOTES * All existing error codes * SEE ALSO * XMLRPC_REQUEST_ERROR * SOURCE */ typedef enum _xmlrpc_error_code { xmlrpc_error_none = 0, /* not an error */ xmlrpc_error_parse_xml_syntax = -32700, xmlrpc_error_parse_unknown_encoding = -32701, xmlrpc_error_parse_bad_encoding = -32702, xmlrpc_error_invalid_xmlrpc = -32600, xmlrpc_error_unknown_method = -32601, xmlrpc_error_invalid_params = -32602, xmlrpc_error_internal_server = -32603, xmlrpc_error_application = -32500, xmlrpc_error_system = -32400, xmlrpc_error_transport = -32300 } XMLRPC_ERROR_CODE; /******/ #define xmlrpc_error_parse_xml_syntax_str "parse error. not well formed." #define xmlrpc_error_parse_unknown_encoding_str "parse error. unknown encoding" #define xmlrpc_error_parse_bad_encoding_str "parse error. invalid character for encoding" #define xmlrpc_error_invalid_xmlrpc_str "server error. xml-rpc not conforming to spec" #define xmlrpc_error_unknown_method_str "server error. method not found." #define xmlrpc_error_invalid_params_str "server error. invalid method parameters" #define xmlrpc_error_internal_server_str "server error. internal xmlrpc library error" #define xmlrpc_error_application_str "application error." #define xmlrpc_error_system_str "system error." #define xmlrpc_error_transport_str "transport error." /****d* VALUE/XMLRPC_VERSION * NAME * XMLRPC_VERSION * NOTES * Defines xml vocabulary used for generated xml * SEE ALSO * XMLRPC_REQUEST_OUTPUT_OPTIONS * XMLRPC_REQUEST_To_XML () * SOURCE */ typedef enum _xmlrpc_version { xmlrpc_version_none, /* not a recognized vocabulary */ xmlrpc_version_1_0, /* xmlrpc 1.0 standard vocab */ xmlrpc_version_simple = 2, /* alt more readable vocab */ xmlrpc_version_danda = 2 /* same as simple. legacy */ } XMLRPC_VERSION; /******/ /****s* VALUE/XMLRPC_REQUEST_OUTPUT_OPTIONS * NAME * XMLRPC_REQUEST_OUTPUT_OPTIONS * NOTES * Defines output options for generated xml * SEE ALSO * XMLRPC_VERSION * XML_ELEM_OUTPUT_OPTIONS * XMLRPC_REQUEST_To_XML () * SOURCE */ typedef struct _xmlrpc_request_output_options { STRUCT_XML_ELEM_OUTPUT_OPTIONS xml_elem_opts; /* xml_element specific output options */ XMLRPC_VERSION version; /* xml vocabulary to use */ } STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS, *XMLRPC_REQUEST_OUTPUT_OPTIONS; /******/ /****s* VALUE/XMLRPC_REQUEST_INPUT_OPTIONS * NAME * XMLRPC_REQUEST_INPUT_OPTIONS * NOTES * Defines options for reading in xml data * SEE ALSO * XMLRPC_VERSION * XML_ELEM_INPUT_OPTIONS * XMLRPC_REQUEST_From_XML () * SOURCE */ typedef struct _xmlrpc_request_input_options { STRUCT_XML_ELEM_INPUT_OPTIONS xml_elem_opts; /* xml_element specific output options */ } STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS, *XMLRPC_REQUEST_INPUT_OPTIONS; /******/ /****s* VALUE/XMLRPC_ERROR * NAME * XMLRPC_ERROR * NOTES * For the reporting and handling of errors * SOURCE */ typedef struct _xmlrpc_error { XMLRPC_ERROR_CODE code; STRUCT_XML_ELEM_ERROR xml_elem_error; /* xml_element errors (parser errors) */ } STRUCT_XMLRPC_ERROR, *XMLRPC_ERROR; /******/ /****d* VALUE/XMLRPC_CASE_COMPARISON * NAME * XMLRPC_CASE_COMPARISON * NOTES * Defines case comparison options for XMLRPC_VALUE/VECTOR API's * SEE ALSO * XMLRPC_CASE * XMLRPC_VALUE * SOURCE */ typedef enum _xmlrpc_case_comparison { xmlrpc_case_insensitive, /* use case-insensitive compare */ xmlrpc_case_sensitive /* use case-sensitive compare */ } XMLRPC_CASE_COMPARISON; /******/ /****d* VALUE/XMLRPC_CASE * NAME * XMLRPC_CASE * NOTES * Defines case behavior when setting IDs in XMLRPC_VALUE API's * SEE ALSO * XMLRPC_CASE_COMPARISON * XMLRPC_VALUE * SOURCE */ typedef enum _xmlrpc_case { xmlrpc_case_exact, /* leave case alone */ xmlrpc_case_lower, /* lower-case id */ xmlrpc_case_upper /* upper-case id */ } XMLRPC_CASE; /******/ /* if you don't like these defaults, you can set them with XMLRPC_SetDefaultIdCase*() */ #define XMLRPC_DEFAULT_ID_CASE XMLRPC_GetDefaultIdCase() #define XMLRPC_DEFAULT_ID_CASE_SENSITIVITY XMLRPC_GetDefaultIdCaseComparison() /* opaque (non-public) types. defined locally in xmlrpc.c */ typedef struct _xmlrpc_request* XMLRPC_REQUEST; typedef struct _xmlrpc_server* XMLRPC_SERVER; typedef struct _xmlrpc_value* XMLRPC_VALUE; /****d* VALUE/XMLRPC_Callback * NAME * XMLRPC_Callback * NOTES * Function prototype for user defined method handlers (callbacks). * SEE ALSO * XMLRPC_ServerRegisterMethod () * XMLRPC_ServerCallMethod () * XMLRPC_REQUEST * XMLRPC_VALUE * SOURCE */ typedef XMLRPC_VALUE (*XMLRPC_Callback)(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); /******/ /* ID Case Defaults */ XMLRPC_CASE XMLRPC_GetDefaultIdCase(); XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case); XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison(); XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case); /* Vector manipulation */ int XMLRPC_VectorSize(XMLRPC_VALUE value); XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value); XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value); int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type); int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source); int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...); int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value); XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case); /* Create values */ XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int truth); XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len); XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time); XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s); XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double f); XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i); XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* s, int len); XMLRPC_VALUE XMLRPC_CreateValueEmpty(); XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type); /* Cleanup values */ void XMLRPC_CleanupValue(XMLRPC_VALUE value); XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value); /* Set Values */ void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time); void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s); void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val); void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val); void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val); const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* s, int len); void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len); const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case); #define XMLRPC_SetValueID(value, id, len) XMLRPC_SetValueID_Case(value, id, len, XMLRPC_DEFAULT_ID_CASE) /* Get Values */ const char* XMLRPC_GetValueString(XMLRPC_VALUE value); int XMLRPC_GetValueStringLen(XMLRPC_VALUE value); int XMLRPC_GetValueInt(XMLRPC_VALUE value); int XMLRPC_GetValueBoolean(XMLRPC_VALUE value); double XMLRPC_GetValueDouble(XMLRPC_VALUE value); const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value); time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value); const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value); const char* XMLRPC_GetValueID(XMLRPC_VALUE value); /* Type introspection */ XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE v); XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE v); /* Parsing and Creating XML */ XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options); XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options); char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int *buf_len); char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len); /* Request manipulation funcs */ const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName); const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request); XMLRPC_REQUEST XMLRPC_RequestNew(); void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO); XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output); XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request); XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data); XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request); XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type); XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request); /* Server Creation/Destruction; Method Registration and Invocation */ XMLRPC_SERVER XMLRPC_ServerCreate(); XMLRPC_SERVER XMLRPC_GetGlobalServer(); /* better to use XMLRPC_ServerCreate if you can */ void XMLRPC_ServerDestroy(XMLRPC_SERVER server); int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb); XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName); XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData); #include "xmlrpc_introspection.h" /* Public Utility funcs */ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string); void XMLRPC_Free(void* mem); const char* XMLRPC_GetVersionString(); /****d* VALUE/XMLRPC_MACROS * NAME * Some Helpful Macros * NOTES * Some macros for making life easier. Should be self-explanatory. * SEE ALSO * XMLRPC_AddValueToVector () * XMLRPC_VectorGetValueWithID_Case () * XMLRPC_VALUE * SOURCE */ /* Append values to vector */ #define XMLRPC_VectorAppendString(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueString(id, s, len)) #define XMLRPC_VectorAppendBase64(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBase64(id, s, len)) #define XMLRPC_VectorAppendDateTime(vector, id, time) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime(id, time)) #define XMLRPC_VectorAppendDateTime_ISO8601(vector, id, s) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime_ISO8601(id, s)) #define XMLRPC_VectorAppendDouble(vector, id, f) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDouble(id, f)) #define XMLRPC_VectorAppendInt(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueInt(id, i)) #define XMLRPC_VectorAppendBoolean(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBoolean(id, i)) /* Get named values from vector */ #define XMLRPC_VectorGetValueWithID(vector, id) XMLRPC_VectorGetValueWithID_Case(vector, id, XMLRPC_DEFAULT_ID_CASE_SENSITIVITY) #define XMLRPC_VectorGetStringWithID(vector, id) XMLRPC_GetValueString(XMLRPC_VectorGetValueWithID(vector, id)) #define XMLRPC_VectorGetBase64WithID(vector, id) XMLRPC_GetValueBase64(XMLRPC_VectorGetValueWithID(vector, id)) #define XMLRPC_VectorGetDateTimeWithID(vector, id) XMLRPC_GetValueDateTime(XMLRPC_VectorGetValueWithID(vector, id)) #define XMLRPC_VectorGetDoubleWithID(vector, id) XMLRPC_GetValueDouble(XMLRPC_VectorGetValueWithID(vector, id)) #define XMLRPC_VectorGetIntWithID(vector, id) XMLRPC_GetValueInt(XMLRPC_VectorGetValueWithID(vector, id)) #define XMLRPC_VectorGetBooleanWithID(vector, id) XMLRPC_GetValueBoolean(XMLRPC_VectorGetValueWithID(vector, id)) /******/ #ifdef __cplusplus } #endif #endif /* not XMLRPC_ALREADY_INCLUDED */ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc.m4 +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc.m4 CPPFLAGS="$CPPFLAGS -DVERSION=\\\"0.42\\\"" AC_DEFUN(XMLRPC_CHECKS,[ AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_LN_S]) AC_REQUIRE([AC_PROG_RANLIB]) AC_DEFINE(UNDEF_THREADS_HACK,,[ ]) XMLRPC_HEADER_CHECKS XMLRPC_TYPE_CHECKS XMLRPC_FUNCTION_CHECKS PHP_FAST_OUTPUT($ext_builddir/libxmlrpc/Makefile) ]) Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /****h* ABOUT/xmlrpc_introspection * AUTHOR * Dan Libby, aka danda (dan@libby.com) * HISTORY * 4/10/2001 -- danda -- initial introspection support * TODO * NOTES *******/ #include "queue.h" #include "xmlrpc.h" #include "xmlrpc_private.h" #include "xmlrpc_introspection_private.h" #include <string.h> #include <stdlib.h> #include <stdarg.h> /* forward declarations for static (non public, non api) funcs */ static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); /*-********************************** * Introspection Callbacks (methods) * ************************************/ /* iterates through a list of structs and finds the one with key "name" matching * needle. slow, would benefit from a struct key hash. */ inline XMLRPC_VALUE find_named_value(XMLRPC_VALUE list, const char* needle) { XMLRPC_VALUE xIter = XMLRPC_VectorRewind(list); while(xIter) { const char* name = XMLRPC_VectorGetStringWithID(xIter, xi_token_name); if(name && !strcmp(name, needle)) { return xIter; } xIter = XMLRPC_VectorNext(list); } return NULL; } /* iterates through docs callbacks and calls any that have not yet been called */ static void check_docs_loaded(XMLRPC_SERVER server, void* userData) { if(server) { q_iter qi = Q_Iter_Head_F(&server->docslist); while( qi ) { doc_method* dm = Q_Iter_Get_F(qi); if(dm && !dm->b_called) { dm->method(server, userData); dm->b_called = 1; } qi = Q_Iter_Next_F(qi); } } } /* utility function for xi_system_describe_methods_cb */ inline void describe_method(XMLRPC_SERVER server, XMLRPC_VALUE vector, const char* method) { if(method) { server_method* sm = find_method(server, method); if(sm) { XMLRPC_AddValueToVector(vector, sm->desc); } } } /* system.describeMethods() callback */ static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { XMLRPC_VALUE xParams = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)); XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); XMLRPC_VALUE xMethodList = XMLRPC_CreateVector("methodList", xmlrpc_vector_array); XMLRPC_VALUE xTypeList = NULL; int bAll = 1; /* lazy loading of introspection data */ check_docs_loaded(server, userData); xTypeList = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList"); XMLRPC_AddValueToVector(xResponse, xTypeList); XMLRPC_AddValueToVector(xResponse, xMethodList); /* check if we have any param */ if(xParams) { /* check if string or vector (1 or n) */ XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(xParams); if(type == xmlrpc_string) { /* just one. spit it out. */ describe_method(server, xMethodList, XMLRPC_GetValueString(xParams)); bAll = 0; } else if(type == xmlrpc_vector) { /* multiple. spit all out */ XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xParams); while(xIter) { describe_method(server, xMethodList, XMLRPC_GetValueString(xIter)); xIter = XMLRPC_VectorNext(xParams); } bAll = 0; } } /* otherwise, default to sending all methods */ if(bAll) { q_iter qi = Q_Iter_Head_F(&server->methodlist); while( qi ) { server_method* sm = Q_Iter_Get_F(qi); if(sm) { XMLRPC_AddValueToVector(xMethodList, sm->desc); } qi = Q_Iter_Next_F(qi); } } return xResponse; } /* this complies with system.listMethods as defined at http://xmlrpc.usefulinc.com/doc/reserved.html */ static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); q_iter qi = Q_Iter_Head_F(&server->methodlist); while( qi ) { server_method* sm = Q_Iter_Get_F(qi); if(sm) { XMLRPC_VectorAppendString(xResponse, 0, sm->name, 0); } qi = Q_Iter_Next_F(qi); } return xResponse; } /* this complies with system.methodSignature as defined at * http://xmlrpc.usefulinc.com/doc/sysmethodsig.html */ static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input))); XMLRPC_VALUE xResponse = NULL; /* lazy loading of introspection data */ check_docs_loaded(server, userData); if(method) { server_method* sm = find_method(server, method); if(sm && sm->desc) { XMLRPC_VALUE xTypesArray = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); XMLRPC_VALUE xIter, xParams, xSig, xSigIter; const char* type; /* array of possible signatures. */ xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); /* find first signature */ xSig = XMLRPC_VectorGetValueWithID(sm->desc, xi_token_signatures); xSigIter = XMLRPC_VectorRewind( xSig ); /* iterate through sigs */ while(xSigIter) { /* first type is the return value */ type = XMLRPC_VectorGetStringWithID(XMLRPC_VectorRewind( XMLRPC_VectorGetValueWithID(xSigIter, xi_token_returns)), xi_token_type); XMLRPC_AddValueToVector(xTypesArray, XMLRPC_CreateValueString(NULL, type ? type : type_to_str(xmlrpc_none, 0), 0)); /* the rest are parameters */ xParams = XMLRPC_VectorGetValueWithID(xSigIter, xi_token_params); xIter = XMLRPC_VectorRewind(xParams); /* iter through params, adding to types array */ while(xIter) { XMLRPC_AddValueToVector(xTypesArray, XMLRPC_CreateValueString(NULL, XMLRPC_VectorGetStringWithID(xIter, xi_token_type), 0)); xIter = XMLRPC_VectorNext(xParams); } /* add types for this signature */ XMLRPC_AddValueToVector(xResponse, xTypesArray); xSigIter = XMLRPC_VectorNext( xSig ); } } } return xResponse; } /* this complies with system.methodHelp as defined at * http://xmlrpc.usefulinc.com/doc/sysmethhelp.html */ static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input))); XMLRPC_VALUE xResponse = NULL; /* lazy loading of introspection data */ check_docs_loaded(server, userData); if(method) { server_method* sm = find_method(server, method); if(sm && sm->desc) { const char* help = XMLRPC_VectorGetStringWithID(sm->desc, xi_token_purpose); /* returns a documentation string, or empty string */ xResponse = XMLRPC_CreateValueString(NULL, help ? help : xi_token_empty, 0); } } return xResponse; } /*-************************************** * End Introspection Callbacks (methods) * ****************************************/ /*-************************ * Introspection Utilities * **************************/ /* performs registration of introspection methods */ void xi_register_system_methods(XMLRPC_SERVER server) { XMLRPC_ServerRegisterMethod(server, xi_token_system_list_methods, xi_system_list_methods_cb); XMLRPC_ServerRegisterMethod(server, xi_token_system_method_help, xi_system_method_help_cb); XMLRPC_ServerRegisterMethod(server, xi_token_system_method_signature, xi_system_method_signature_cb); XMLRPC_ServerRegisterMethod(server, xi_token_system_describe_methods, xi_system_describe_methods_cb); } /* describe a value (param, return, type) */ static XMLRPC_VALUE describeValue_worker(const char* type, const char* id, const char* desc, int optional, const char* default_val, XMLRPC_VALUE sub_params) { XMLRPC_VALUE xParam = NULL; if(id || desc) { xParam = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); XMLRPC_VectorAppendString(xParam, xi_token_name, id, 0); XMLRPC_VectorAppendString(xParam, xi_token_type, type, 0); XMLRPC_VectorAppendString(xParam, xi_token_description, desc, 0); if(optional != 2) { XMLRPC_VectorAppendInt(xParam, xi_token_optional, optional); } if(optional == 1 && default_val) { XMLRPC_VectorAppendString(xParam, xi_token_default, default_val, 0); } XMLRPC_AddValueToVector(xParam, sub_params); } return xParam; } /* convert an xml tree conforming to spec <url tbd> to XMLRPC_VALUE * suitable for use with XMLRPC_ServerAddIntrospectionData */ XMLRPC_VALUE xml_element_to_method_description(xml_element* el, XMLRPC_ERROR err) { XMLRPC_VALUE xReturn = NULL; if(el->name) { const char* name = NULL; const char* type = NULL; const char* basetype = NULL; const char* desc = NULL; const char* def = NULL; int optional = 0; xml_element_attr* attr_iter = Q_Head(&el->attrs); /* grab element attributes up front to save redundant while loops */ while(attr_iter) { if(!strcmp(attr_iter->key, "name")) { name = attr_iter->val; } else if(!strcmp(attr_iter->key, "type")) { type = attr_iter->val; } else if(!strcmp(attr_iter->key, "basetype")) { basetype = attr_iter->val; } else if(!strcmp(attr_iter->key, "desc")) { desc = attr_iter->val; } else if(!strcmp(attr_iter->key, "optional")) { if(attr_iter->val && !strcmp(attr_iter->val, "yes")) { optional = 1; } } else if(!strcmp(attr_iter->key, "default")) { def = attr_iter->val; } attr_iter = Q_Next(&el->attrs); } /* value and typeDescription behave about the same */ if(!strcmp(el->name, "value") || !strcmp(el->name, "typeDescription")) { XMLRPC_VALUE xSubList = NULL; const char* ptype = !strcmp(el->name, "value") ? type : basetype; if(ptype) { if(Q_Size(&el->children) && !strcmp(ptype, "array") || !strcmp(ptype, "struct") || !strcmp(ptype, "mixed")) { xSubList = XMLRPC_CreateVector("member", xmlrpc_vector_array); if(xSubList) { xml_element* elem_iter = Q_Head(&el->children); while(elem_iter) { XMLRPC_AddValueToVector(xSubList, xml_element_to_method_description(elem_iter, err)); elem_iter = Q_Next(&el->children); } } } xReturn = describeValue_worker(ptype, name, (desc ? desc : (xSubList ? NULL : el->text.str)), optional, def, xSubList); } } /* these three kids are about equivalent */ else if(!strcmp(el->name, "params") || !strcmp(el->name, "returns") || !strcmp(el->name, "signature")) { if(Q_Size(&el->children)) { xml_element* elem_iter = Q_Head(&el->children); xReturn = XMLRPC_CreateVector(!strcmp(el->name, "signature") ? NULL : el->name, xmlrpc_vector_struct); while(elem_iter) { XMLRPC_AddValueToVector(xReturn, xml_element_to_method_description(elem_iter, err)); elem_iter = Q_Next(&el->children); } } } else if(!strcmp(el->name, "methodDescription")) { xml_element* elem_iter = Q_Head(&el->children); xReturn = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); XMLRPC_VectorAppendString(xReturn, xi_token_name, name, 0); while(elem_iter) { XMLRPC_AddValueToVector(xReturn, xml_element_to_method_description(elem_iter, err)); elem_iter = Q_Next(&el->children); } } /* items are slightly special */ else if(!strcmp(el->name, "item")) { xReturn = XMLRPC_CreateValueString(name, el->text.str, el->text.len); } /* sure. we'll let any ol element with children through */ else if(Q_Size(&el->children)) { xml_element* elem_iter = Q_Head(&el->children); xReturn = XMLRPC_CreateVector(el->name, xmlrpc_vector_mixed); while(elem_iter) { XMLRPC_AddValueToVector(xReturn, xml_element_to_method_description(elem_iter, err)); elem_iter = Q_Next(&el->children); } } /* or anything at all really, so long as its got some text. * no reason being all snotty about a spec, right? */ else if(el->name && el->text.len) { xReturn = XMLRPC_CreateValueString(el->name, el->text.str, el->text.len); } } return xReturn; } /*-**************************** * End Introspection Utilities * ******************************/ /*-****************** * Introspection API * ********************/ /****f* VALUE/XMLRPC_IntrospectionCreateDescription * NAME * XMLRPC_IntrospectionCreateDescription * SYNOPSIS * XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) * FUNCTION * converts raw xml describing types and methods into an * XMLRPC_VALUE suitable for use with XMLRPC_ServerAddIntrospectionData() * INPUTS * xml - xml data conforming to introspection spec at <url tbd> * err - optional pointer to error struct. filled in if error occurs and not NULL. * RESULT * XMLRPC_VALUE - newly created value, or NULL if fatal error. * BUGS * Currently does little or no validation of xml. * Only parse errors are currently reported in err, not structural errors. * SEE ALSO * XMLRPC_ServerAddIntrospectionData () * SOURCE */ XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) { XMLRPC_VALUE xReturn = NULL; xml_element* root = xml_elem_parse_buf(xml, 0, 0, err ? &err->xml_elem_error : NULL); if(root) { xReturn = xml_element_to_method_description(root, err); xml_elem_free(root); } return xReturn; } /*******/ /****f* SERVER/XMLRPC_ServerAddIntrospectionData * NAME * XMLRPC_ServerAddIntrospectionData * SYNOPSIS * int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) * FUNCTION * updates server with additional introspection data * INPUTS * server - target server * desc - introspection data, should be a struct generated by * XMLRPC_IntrospectionCreateDescription () * RESULT * int - 1 if success, else 0 * NOTES * - function will fail if neither typeList nor methodList key is present in struct. * - if method or type already exists, it will be replaced. * - desc is never freed by the server. caller is responsible for cleanup. * BUGS * - horribly slow lookups. prime candidate for hash improvements. * - uglier and more complex than I like to see for API functions. * SEE ALSO * XMLRPC_ServerAddIntrospectionData () * XMLRPC_ServerRegisterIntrospectionCallback () * XMLRPC_CleanupValue () * SOURCE */ int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) { int bSuccess = 0; if(server && desc) { XMLRPC_VALUE xNewTypes = XMLRPC_VectorGetValueWithID(desc, "typeList"); XMLRPC_VALUE xNewMethods = XMLRPC_VectorGetValueWithID(desc, "methodList"); XMLRPC_VALUE xServerTypes = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList"); if(xNewMethods) { XMLRPC_VALUE xMethod = XMLRPC_VectorRewind(xNewMethods); while(xMethod) { const char* name = XMLRPC_VectorGetStringWithID(xMethod, xi_token_name); server_method* sm = find_method(server, name); if(sm) { if(sm->desc) { XMLRPC_CleanupValue(sm->desc); } sm->desc = XMLRPC_CopyValue(xMethod); bSuccess = 1; } xMethod = XMLRPC_VectorNext(xNewMethods); } } if(xNewTypes) { if(!xServerTypes) { if(!server->xIntrospection) { server->xIntrospection = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); } XMLRPC_AddValueToVector(server->xIntrospection, xNewTypes); bSuccess = 1; } else { XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xNewTypes); while(xIter) { /* get rid of old values */ XMLRPC_VALUE xPrev = find_named_value(xServerTypes, XMLRPC_VectorGetStringWithID(xIter, xi_token_name)); if(xPrev) { XMLRPC_VectorRemoveValue(xServerTypes, xPrev); } XMLRPC_AddValueToVector(xServerTypes, xIter); bSuccess = 1; xIter = XMLRPC_VectorNext(xNewTypes); } } } } return bSuccess; } /*******/ /****f* SERVER/XMLRPC_ServerRegisterIntrospectionCallback * NAME * XMLRPC_ServerRegisterIntrospectionCallback * SYNOPSIS * int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) * FUNCTION * registers a callback for lazy generation of introspection data * INPUTS * server - target server * cb - callback that will generate introspection data * RESULT * int - 1 if success, else 0 * NOTES * parsing xml and generating introspection data is fairly expensive, thus a * server may wish to wait until this data is actually requested before generating * it. Any number of callbacks may be registered at any time. A given callback * will only ever be called once, the first time an introspection request is * processed after the time of callback registration. * SEE ALSO * XMLRPC_ServerAddIntrospectionData () * XMLRPC_IntrospectionCreateDescription () * SOURCE */ int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) { int bSuccess = 0; if(server && cb) { doc_method* dm = calloc(1, sizeof(doc_method)); if(dm) { dm->method = cb; dm->b_called = 0; if(Q_PushTail(&server->docslist, dm)) { bSuccess = 1; } else { my_free(dm); } } } return 0; } /*******/ /*-********************** * End Introspection API * ************************/ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.h +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* IMPORTANT! * * only public (official API) things should be in this file. Anything else * should go in <group>_private.h, or in the appropriate .c file. */ #ifndef __XI_INTROSPECTION_H /* * Avoid include redundancy. */ #define __XI_INTROSPECTION_H /*---------------------------------------------------------------------------- * xmlrpc_introspection.h * * Purpose: * define public introspection API * Comments: */ /*---------------------------------------------------------------------------- * Constants */ #define xi_token_params "params" #define xi_token_returns "returns" #define xi_token_related "related" #define xi_token_sub "sub" /*---------------------------------------------------------------------------- * Includes */ /*---------------------------------------------------------------------------- * Structures */ /****d* VALUE/XMLRPC_IntrospectionCallback * NAME * XMLRPC_IntrospectionCallback * NOTES * Function prototype for lazy documentation generation (not generated until requested). * SOURCE */ typedef void (*XMLRPC_IntrospectionCallback)(XMLRPC_SERVER server, void* userData); /******/ /*---------------------------------------------------------------------------- * Globals */ /*---------------------------------------------------------------------------- * Functions */ XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR error); int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc); int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb); /*---------------------------------------------------------------------------- * Macros */ #endif /* __XI_INTROSPECTION_H */ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Dan Libby, Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* IMPORTANT! * * only non-public things should be in this file. It is fine for any .c file * in xmlrpc/src to include it, but users of the public API should never * include it, and thus *.h files that are part of the public API should * never include it, or they would break if this file is not present. */ #ifndef __XI_INTROSPECTION_PRIVATE_H /* * Avoid include redundancy. */ #define __XI_INTROSPECTION_PRIVATE_H /*---------------------------------------------------------------------------- * xmlrpc_introspection_private.h * * Purpose: * define non-public introspection routines * Comments: */ /*---------------------------------------------------------------------------- * Constants */ #define xi_token_default "default" #define xi_token_description "description" #define xi_token_name "name" #define xi_token_optional "optional" #define xi_token_params "params" #define xi_token_purpose "purpose" #define xi_token_returns "returns" #define xi_token_signatures "signatures" #define xi_token_type "type" #define xi_token_version "version" #define xi_token_empty "" #define xi_token_system_describe_methods "system.describeMethods" #define xi_token_system_list_methods "system.listMethods" #define xi_token_system_method_help "system.methodHelp" #define xi_token_system_method_signature "system.methodSignature" /*---------------------------------------------------------------------------- * Includes */ /*---------------------------------------------------------------------------- * Structures */ typedef struct _doc_method { XMLRPC_IntrospectionCallback method; int b_called; } doc_method; /*---------------------------------------------------------------------------- * Globals */ /*---------------------------------------------------------------------------- * Functions */ void xi_register_system_methods(XMLRPC_SERVER server); /*---------------------------------------------------------------------------- * Macros */ #endif /* __XI_INTROSPECTION_PRIVATE_H */ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc_private.h +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc_private.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* only non-public things should be in this file. It is fine for any .c file * in xmlrpc/src to include it, but users of the public API should never * include it, and thus *.h files that are part of the public API should * never include it, or they would break if this file is not present. */ #ifndef XMLRPC_PRIVATE_ALREADY_INCLUDED /* * Avoid include redundancy. */ #define XMLRPC_PRIVATE_ALREADY_INCLUDED #ifdef __cplusplus extern "C" { #endif /*---------------------------------------------------------------------------- * xmlrpc_private.h * * Purpose: * define non-public intra-library routines & data * Comments: */ /*---------------------------------------------------------------------------- * Constants */ /*---------------------------------------------------------------------------- * Includes */ /*---------------------------------------------------------------------------- * Structures */ /* Some of these are typedef'd in xmlrpc.h for public use */ typedef struct _xmlrpc_vector* XMLRPC_VECTOR; /****s* VALUE/XMLRPC_VALUE * NAME * XMLRPC_VALUE * NOTES * A value of variable data type. The most important object in this API. :) * * This struct is opaque to callers and should be accessed only via accessor functions. * SEE ALSO * XMLRPC_REQUEST * XMLRPC_CreateValueEmpty () * XMLRPC_CleanupValue () * SOURCE */ typedef struct _xmlrpc_value { XMLRPC_VALUE_TYPE type; /* data type of this value */ XMLRPC_VECTOR v; /* vector type specific info */ simplestring str; /* string value buffer */ simplestring id; /* id of this value. possibly empty. */ int i; /* integer value. */ double d; /* double value */ int iRefCount; /* So we know when we can delete the value . */ } STRUCT_XMLRPC_VALUE; /******/ /****s* VALUE/XMLRPC_REQUEST * NAME * XMLRPC_REQUEST * NOTES * Internal representation of an XML request. * * This struct is opaque to callers and should be accessed only via accessor functions. * * SEE ALSO * XMLRPC_VALUE * XMLRPC_RequestNew () * XMLRPC_RequestFree () * SOURCE */ typedef struct _xmlrpc_request { XMLRPC_VALUE io; /* data associated with this request */ simplestring methodName; /* name of method being called */ XMLRPC_REQUEST_TYPE request_type; /* type of request */ STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS output; /* xml output options */ XMLRPC_VALUE error; /* error codes */ } STRUCT_XMLRPC_REQUEST; /******/ /* Vector type. Used by XMLRPC_VALUE. Never visible to users of the API. */ typedef struct _xmlrpc_vector { XMLRPC_VECTOR_TYPE type; /* vector type */ const char* id; /* ??? unused? */ queue *q; /* list of child values */ } STRUCT_XMLRPC_VECTOR; /******/ /****s* VALUE/XMLRPC_SERVER * NAME * XMLRPC_SERVER * NOTES * internal representation of an xmlrpc server * * This struct is opaque to callers and should be accessed only via accessor functions. * * SEE ALSO * XMLRPC_ServerCreate () * XMLRPC_ServerDestroy () * SOURCE */ typedef struct _xmlrpc_server { queue methodlist; /* list of callback methods */ queue docslist; /* list of introspection callbacks */ XMLRPC_VALUE xIntrospection; } STRUCT_XMLRPC_SERVER; /******/ typedef struct _server_method { char* name; XMLRPC_VALUE desc; XMLRPC_Callback method; } server_method; /*---------------------------------------------------------------------------- * Globals */ /*---------------------------------------------------------------------------- * Functions */ server_method* find_method(XMLRPC_SERVER server, const char* name); const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype); /*---------------------------------------------------------------------------- * Macros */ #define my_free(thing) if(thing) {free(thing); thing = 0;} #ifdef __cplusplus } #endif #endif /* XMLRPC_PRIVATE_ALREADY_INCLUDED */
Index: php4/ext/xmlrpc/Makefile.in +++ php4/ext/xmlrpc/Makefile.in # $Id: Makefile.in,v 1.1 2001/09/06 04:13:29 danda Exp $ LTLIBRARY_NAME = libxmlrpc.la LTLIBRARY_SOURCES = xmlrpc-epi-php.c LTLIBRARY_SHARED_NAME = xmlrpc.la EXTRA_INCLUDES = -I$(XMLRPC_INC_DIR) LTLIBRARY_LIBADD = $(XMLRPC_LIBADD) LTLIBRARY_SHARED_LIBADD = $(XMLRPC_SHARED_LIBADD) SUBDIRS = $(XMLRPC_SUBDIRS) include $(top_srcdir)/build/dynlib.mk Index: php4/ext/xmlrpc/config.m4 +++ php4/ext/xmlrpc/config.m4 dnl $Id: config.m4,v 1.1 2001/09/06 04:13:29 danda Exp $ sinclude(ext/xmlrpc/libxmlrpc/acinclude.m4) sinclude(ext/xmlrpc/libxmlrpc/xmlrpc.m4) sinclude(libxmlrpc/acinclude.m4) sinclude(libxmlrpc/xmlrpc.m4) AC_DEFUN(XMLRPC_LIB_CHK,[ str="$XMLRPC_DIR/$1/libxmlrpc.*" for j in `echo $str`; do if test -r $j; then XMLRPC_LIB_DIR=$XMLRPC_DIR/$1 break 2 fi done ]) PHP_ARG_WITH(xmlrpc, for XMLRPC-EPI support, [ --with-xmlrpc[=DIR] Include XMLRPC-EPI support. DIR is the XMLRPC-EPI base directory. If unspecified, the bundled XMLRPC-EPI library will be used.], yes) PHP_ARG_WITH(expat-dir, libexpat dir for XMLRPC-EPI [ --with-expat-dir=DIR XMLRPC-EPI: libexpat dir for XMLRPC-EPI]) if test "$PHP_XMLRPC" != "no"; then AC_DEFINE(HAVE_XMLRPC, 1, [Whether you have XMLRPC-EPI]) PHP_EXTENSION(xmlrpc,$ext_shared) dnl check for iconv found_iconv=no AC_CHECK_LIB(c, iconv_open, found_iconv=yes) if test "$found_iconv" = "no"; then for i in /usr /usr/local $ICONV_DIR; do if test -f $i/lib/libconv.a -o -f $i/lib/libiconv.s?; then PHP_ADD_LIBRARY_WITH_PATH(iconv, $i/lib, XMLRPC_SHARED_LIBADD) found_iconv=yes fi done fi if test "$found_iconv" = "no"; then AC_MSG_ERROR(iconv not found, in order to build XMLRPC-EPI you need the iconv library) fi dnl check for expat testval=no for i in $PHP_EXPAT_DIR; do if test -f $i/lib/libexpat.a -o -f $i/lib/libexpat.s?; then AC_DEFINE(HAVE_LIBEXPAT2,1,[ ]) PHP_ADD_LIBRARY_WITH_PATH(expat, $i/lib, XMLRPC_SHARED_LIBADD) PHP_ADD_INCLUDE($i/include) testval=yes fi done if test "$testval" = "no"; then PHP_ADD_LIBRARY(xmlparse) PHP_ADD_LIBRARY(xmltok) fi fi if test "$PHP_XMLRPC" = "yes"; then XMLRPC_CHECKS XMLRPC_LIBADD=libxmlrpc/libxmlrpc.la XMLRPC_SHARED_LIBADD=libxmlrpc/libxmlrpc.la XMLRPC_SUBDIRS=libxmlrpc PHP_SUBST(XMLRPC_LIBADD) PHP_SUBST(XMLRPC_SUBDIRS) LIB_BUILD($ext_builddir/libxmlrpc,$ext_shared,yes) PHP_ADD_INCLUDE($ext_srcdir/libxmlrpc) XMLRPC_MODULE_TYPE=builtin elif test "$PHP_XMLRPC" != "no"; then for i in $PHP_XMLRPC; do if test -r $i/include/xmlrpc/xmlrpc.h; then XMLRPC_DIR=$i XMLRPC_INC_DIR=$i/include/xmlrpc elif test -r $i/include/xmlrpc.h; then XMLRPC_DIR=$i XMLRPC_INC_DIR=$i/include fi done if test -z "$XMLRPC_DIR"; then AC_MSG_ERROR(Cannot find header files under $PHP_XMLRPC) fi XMLRPC_MODULE_TYPE=external for i in lib lib/xmlrpc; do XMLRPC_LIB_CHK($i) done if test -z "$XMLRPC_LIB_DIR"; then AC_MSG_ERROR(Cannot find xmlrpc library under $XMLRPC_DIR) fi if test "$PHP_ZLIB_DIR" != "no"; then PHP_ADD_LIBRARY(z,, XMLRPC_SHARED_LIBADD) XMLRPC_LIBS="-L$PHP_ZLIB_DIR -z" fi PHP_ADD_LIBRARY_WITH_PATH(xmlrpc, $XMLRPC_LIB_DIR, XMLRPC_SHARED_LIBADD) XMLRPC_LIBS="-L$XMLRPC_LIB_DIR -lxmlrpc $XMLRPC_LIBS" PHP_ADD_INCLUDE($XMLRPC_INC_DIR) XMLRPC_INCLUDE="-I$XMLRPC_INC_DIR" else XMLRPC_MODULE_TYPE=none fi PHP_SUBST(XMLRPC_SHARED_LIBADD) PHP_SUBST_OLD(XMLRPC_MODULE_TYPE) PHP_SUBST_OLD(XMLRPC_LIBS) PHP_SUBST_OLD(XMLRPC_INCLUDE) Index: php4/ext/xmlrpc/libs.mk +++ php4/ext/xmlrpc/libs.mk include $(top_builddir)/config_vars.mk LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X) LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo) $(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES) $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD) targets = $(LTLIBRARY_NAME) Index: php4/ext/xmlrpc/php_config.h.in +++ php4/ext/xmlrpc/php_config.h.in /* php_config.h.in. Generated automatically from configure.in by autoheader. */ /* Define if your C compiler doesn't accept -c and -o together. */ #undef NO_MINUS_C_MINUS_O /* Whether you have XMLRPC */ #undef HAVE_XMLRPC /* Whether to build xmlrpc as dynamic module */ #undef COMPILE_DL_XMLRPC Index: php4/ext/xmlrpc/php_xmlrpc.h +++ php4/ext/xmlrpc/php_xmlrpc.h /* This file is part of, or distributed with, libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* auto-generated portions of this file are also subject to the php license */ /* +----------------------------------------------------------------------+ | PHP version 4.0 | +----------------------------------------------------------------------+ | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: | | | +----------------------------------------------------------------------+ */ #ifndef _PHP_XMLRPC_H #define _PHP_XMLRPC_H /* You should tweak config.m4 so this symbol (or some else suitable) gets defined. */ #if 1 /* HAVE_XMLRPC */ extern zend_module_entry xmlrpc_module_entry; #define phpext_xmlrpc_ptr &xmlrpc_module_entry #ifdef PHP_WIN32 #define PHP_XMLRPC_API __declspec(dllexport) #else #define PHP_XMLRPC_API #endif PHP_MINIT_FUNCTION(xmlrpc); PHP_MSHUTDOWN_FUNCTION(xmlrpc); PHP_RINIT_FUNCTION(xmlrpc); PHP_RSHUTDOWN_FUNCTION(xmlrpc); PHP_MINFO_FUNCTION(xmlrpc); PHP_FUNCTION(xmlrpc_encode); PHP_FUNCTION(xmlrpc_decode); PHP_FUNCTION(xmlrpc_decode_request); PHP_FUNCTION(xmlrpc_encode_request); PHP_FUNCTION(xmlrpc_get_type); PHP_FUNCTION(xmlrpc_set_type); PHP_FUNCTION(xmlrpc_server_create); PHP_FUNCTION(xmlrpc_server_destroy); PHP_FUNCTION(xmlrpc_server_register_method); PHP_FUNCTION(xmlrpc_server_call_method); PHP_FUNCTION(xmlrpc_parse_method_descriptions); PHP_FUNCTION(xmlrpc_server_add_introspection_data); PHP_FUNCTION(xmlrpc_server_register_introspection_callback); /* Fill in this structure and use entries in it for thread safety instead of using true globals. */ typedef struct { /* You can use the next one as type if your module registers any resources. Oh, you can of course rename it to something more suitable, add list entry types or remove it if it not needed. It's just an example. */ int le_xmlrpc_server; } php_xmlrpc_globals; /* In every function that needs to use variables in php_xmlrpc_globals, do call XMLRPCLS_FETCH(); after declaring other variables used by that function, and always refer to them as XMLRPCG(variable). You are encouraged to rename these macros something shorter, see examples in any other php module directory. */ #ifdef ZTS #define XMLRPCG(v) (xmlrpc_globals->v) #define XMLRPCLS_FETCH() php_xmlrpc_globals *xmlrpc_globals = ts_resource(gd_xmlrpc_id) #else #define XMLRPCG(v) (xmlrpc_globals.v) #define XMLRPCLS_FETCH() #endif #else #define phpext_xmlrpc_ptr NULL #endif #endif /* _PHP_XMLRPC_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ Index: php4/ext/xmlrpc/xmlrpc-epi-php.c +++ php4/ext/xmlrpc/xmlrpc-epi-php.c /* This file is part of, or distributed with, libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* auto-generated portions of this file are also subject to the php license */ /* +----------------------------------------------------------------------+ | PHP version 4.0 | +----------------------------------------------------------------------+ | Copyright (c) 1997, 1998, 1999, 2000 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: | | | +----------------------------------------------------------------------+ */ #include "php.h" #include "php_ini.h" #include "php_xmlrpc.h" #include "xmlrpc.h" /* You should tweak config.m4 so this symbol (or some else suitable) gets defined. */ #ifdef ZTS int xmlrpc_globals_id; #else php_xmlrpc_globals xmlrpc_globals; #endif /* Every user visible function must have an entry in xmlrpc_functions[]. */ function_entry xmlrpc_functions[] = { PHP_FE(xmlrpc_encode, NULL) PHP_FE(xmlrpc_decode, NULL) PHP_FE(xmlrpc_decode_request, NULL) PHP_FE(xmlrpc_encode_request, NULL) PHP_FE(xmlrpc_get_type, NULL) PHP_FE(xmlrpc_set_type, NULL) PHP_FE(xmlrpc_server_create, NULL) PHP_FE(xmlrpc_server_destroy, NULL) PHP_FE(xmlrpc_server_register_method, NULL) PHP_FE(xmlrpc_server_call_method, NULL) PHP_FE(xmlrpc_parse_method_descriptions, NULL) PHP_FE(xmlrpc_server_add_introspection_data, NULL) PHP_FE(xmlrpc_server_register_introspection_callback, NULL) {NULL, NULL, NULL} /* Must be the last line in xmlrpc_functions[] */ }; zend_module_entry xmlrpc_module_entry = { "xmlrpc", xmlrpc_functions, PHP_MINIT(xmlrpc), PHP_MSHUTDOWN(xmlrpc), PHP_RINIT(xmlrpc), /* Replace with NULL if there's nothing to do at request start */ PHP_RSHUTDOWN(xmlrpc), /* Replace with NULL if there's nothing to do at request end */ PHP_MINFO(xmlrpc), STANDARD_MODULE_PROPERTIES }; #ifdef COMPILE_DL_XMLRPC ZEND_GET_MODULE(xmlrpc) #endif /* Remove comments and fill if you need to have entries in php.ini PHP_INI_BEGIN() PHP_INI_END() */ /******************************* * local structures and defines * *******************************/ // per server data typedef struct _xmlrpc_server_data { pval* method_map; pval* introspection_map; XMLRPC_SERVER server_ptr; } xmlrpc_server_data; // how to format output typedef struct _php_output_options { int b_php_out; STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out; } php_output_options; // data passed to C callback typedef struct _xmlrpc_callback_data { pval* xmlrpc_method; pval* php_function; pval* caller_params; pval* return_data; xmlrpc_server_data* server; char php_executed; } xmlrpc_callback_data; #define PHP_EXT_VERSION "0.41" // output options #define OUTPUT_TYPE_KEY "output_type" #define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1) #define OUTPUT_TYPE_VALUE_PHP "php" #define OUTPUT_TYPE_VALUE_XML "xml" #define VERBOSITY_KEY "verbosity" #define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1) #define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space" #define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only" #define VERBOSITY_VALUE_PRETTY "pretty" #define ESCAPING_KEY "escaping" #define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1) #define ESCAPING_VALUE_CDATA "cdata" #define ESCAPING_VALUE_NON_ASCII "non-ascii" #define ESCAPING_VALUE_NON_PRINT "non-print" #define ESCAPING_VALUE_MARKUP "markup" #define VERSION_KEY "version" #define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1) #define VERSION_VALUE_SIMPLE "simple" #define VERSION_VALUE_XMLRPC "xmlrpc" #define ENCODING_KEY "encoding" #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1) #define ENCODING_DEFAULT "iso-8859-1" // value types #define OBJECT_TYPE_ATTR "xmlrpc_type" #define OBJECT_VALUE_ATTR "scalar" /*********************** * forward declarations * ***********************/ XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue); static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data); /********************* * startup / shutdown * *********************/ static void destroy_server_data(xmlrpc_server_data *server) { if(server) { XMLRPC_ServerDestroy(server->server_ptr); zval_dtor(server->method_map); FREE_ZVAL(server->method_map); zval_dtor(server->introspection_map); FREE_ZVAL(server->introspection_map); efree(server); } } /* called when server is being destructed. either when xmlrpc_server_destroy * is called, or when request ends. */ static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc) { if(rsrc && rsrc->ptr) { destroy_server_data((xmlrpc_server_data*)rsrc->ptr); } } /* module init */ PHP_MINIT_FUNCTION(xmlrpc) { /* Remove comments if you have entries in php.ini REGISTER_INI_ENTRIES(); */ XMLRPCG(le_xmlrpc_server) = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number); return SUCCESS; } /* module shutdown */ PHP_MSHUTDOWN_FUNCTION(xmlrpc) { /* Remove comments if you have entries in php.ini UNREGISTER_INI_ENTRIES(); */ return SUCCESS; } /* Remove if there's nothing to do at request start */ PHP_RINIT_FUNCTION(xmlrpc) { return SUCCESS; } /* Remove if there's nothing to do at request end */ PHP_RSHUTDOWN_FUNCTION(xmlrpc) { return SUCCESS; } /* display info in phpinfo() */ PHP_MINFO_FUNCTION(xmlrpc) { php_info_print_table_start(); php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString()); php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION); php_info_print_table_row(2, "author", "Dan Libby"); php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net"); php_info_print_table_row(2, "open sourced by", "Epinions.com"); php_info_print_table_end(); /* DISPLAY_INI_ENTRIES(); */ } /******************* * random utilities * *******************/ /* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc). * Could easily be further generalized to work with objects. */ static int add_long(pval* list, char* id, int num) { if(id) return add_assoc_long(list, id, num); else return add_next_index_long(list, num); } static int add_double(pval* list, char* id, double num) { if(id) return add_assoc_double(list, id, num); else return add_next_index_double(list, num); } static int add_string(pval* list, char* id, char* string, int duplicate) { if(id) return add_assoc_string(list, id, string, duplicate); else return add_next_index_string(list, string, duplicate); } static int add_stringl(pval* list, char* id, char* string, uint length, int duplicate) { if(id) return add_assoc_stringl(list, id, string, length, duplicate); else return add_next_index_stringl(list, string, length, duplicate); } static int add_pval(pval* list, const char* id, pval** val) { if(list && val) { if(id) return zend_hash_update(list->value.ht, (char*)id, strlen(id)+1, (void *) val, sizeof(pval **), NULL); else return zend_hash_next_index_insert(list->value.ht, (void *) val, sizeof(pval **), NULL); } } #if ZEND_MODULE_API_NO >= 20001222 #define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0) #else #define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index) #endif /************************* * input / output options * *************************/ /* parse an array (user input) into output options suitable for use by xmlrpc engine * and determine whether to return data as xml or php vars */ static void set_output_options(php_output_options* options, pval* output_opts) { if(options) { /* defaults */ options->b_php_out = 0; options->xmlrpc_out.version = xmlrpc_version_1_0; options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT; options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty; options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping; if(output_opts && output_opts->type == IS_ARRAY) { pval** val; /* verbosity of generated xml */ if(zend_hash_find(output_opts->value.ht, OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**)&val) == SUCCESS) { if((*val)->type == IS_STRING) { if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_PHP)) { options->b_php_out = 1; } else if(!strcmp((*val)->value.str.val, OUTPUT_TYPE_VALUE_XML)) { options->b_php_out = 0; } } } /* verbosity of generated xml */ if(zend_hash_find(output_opts->value.ht, VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, (void**)&val) == SUCCESS) { if((*val)->type == IS_STRING) { if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NO_WHITE_SPACE)) { options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space; } else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_NEWLINES_ONLY)) { options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only; } else if(!strcmp((*val)->value.str.val, VERBOSITY_VALUE_PRETTY)) { options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty; } } } /* version of xml to output */ if(zend_hash_find(output_opts->value.ht, VERSION_KEY, VERSION_KEY_LEN + 1, (void**)&val) == SUCCESS) { if((*val)->type == IS_STRING) { if(!strcmp((*val)->value.str.val, VERSION_VALUE_XMLRPC)) { options->xmlrpc_out.version = xmlrpc_version_1_0; } else if(!strcmp((*val)->value.str.val, VERSION_VALUE_SIMPLE)) { options->xmlrpc_out.version = xmlrpc_version_simple; } } } /* encoding code set */ if(zend_hash_find(output_opts->value.ht, ENCODING_KEY, ENCODING_KEY_LEN + 1, (void**)&val) == SUCCESS) { if((*val)->type == IS_STRING) { options->xmlrpc_out.xml_elem_opts.encoding = estrdup((*val)->value.str.val); } } /* escaping options */ if(zend_hash_find(output_opts->value.ht, ESCAPING_KEY, ESCAPING_KEY_LEN + 1, (void**)&val) == SUCCESS) { /* multiple values allowed. check if array */ if((*val)->type == IS_ARRAY) { pval** iter_val; zend_hash_internal_pointer_reset((*val)->value.ht); options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping; while(1) { if(zend_hash_get_current_data((*val)->value.ht, (void**)&iter_val) == SUCCESS) { if((*iter_val)->type == IS_STRING && (*iter_val)->value.str.val) { if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_CDATA)) { options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping; } else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) { options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping; } else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) { options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping; } else if(!strcmp((*iter_val)->value.str.val, ESCAPING_VALUE_MARKUP)) { options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping; } } } else { break; } zend_hash_move_forward((*val)->value.ht); } } /* else, check for single value */ else if((*val)->type == IS_STRING) { if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_CDATA)) { options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping; } else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_ASCII)) { options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping; } else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_NON_PRINT)) { options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping; } else if(!strcmp((*val)->value.str.val, ESCAPING_VALUE_MARKUP)) { options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping; } } } } } } /****************** * encode / decode * ******************/ /* php arrays have no distinction between array and struct types. * they even allow mixed. Thus, we determine the type by iterating * through the entire array and figuring out each element. * room for some optimation here if we stop after a specific # of elements. */ static XMLRPC_VECTOR_TYPE determine_vector_type(HashTable *ht) { int bArray = 0, bStruct = 0, bMixed = 0; unsigned long num_index; char* my_key; zend_hash_internal_pointer_reset(ht); while(1) { int res = my_zend_hash_get_current_key(ht, &my_key, &num_index); if(res == HASH_KEY_IS_LONG) { if(bStruct) { bMixed = 1; break; } bArray = 1; } else if(res == HASH_KEY_NON_EXISTANT) { break; } else if(res == HASH_KEY_IS_STRING) { if(bArray) { bMixed = 1; break; } bStruct = 1; } zend_hash_move_forward(ht); } return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array); } /* recursively convert php values into xmlrpc values */ static XMLRPC_VALUE PHP_to_XMLRPC_worker(const char* key, pval* in_val, int depth) { XMLRPC_VALUE xReturn = NULL; if(in_val) { pval* val = NULL; XMLRPC_VALUE_TYPE type = get_pval_xmlrpc_type(in_val, &val); if(val) { switch(type) { case xmlrpc_base64: if(val->type == IS_NULL) { xReturn = XMLRPC_CreateValueBase64(key, "", 1); } else { xReturn = XMLRPC_CreateValueBase64(key, val->value.str.val, val->value.str.len); } break; case xmlrpc_datetime: convert_to_string(val); xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, val->value.str.val); break; case xmlrpc_boolean: convert_to_boolean(val); xReturn = XMLRPC_CreateValueBoolean(key, val->value.lval); break; case xmlrpc_int: convert_to_long(val); xReturn = XMLRPC_CreateValueInt(key, val->value.lval); break; case xmlrpc_double: convert_to_double(val); xReturn = XMLRPC_CreateValueDouble(key, val->value.dval); break; case xmlrpc_string: convert_to_string(val); xReturn = XMLRPC_CreateValueString(key, val->value.str.val, val->value.str.len); break; case xmlrpc_vector: { unsigned long num_index; pval** pIter; char* my_key; convert_to_array(val); xReturn = XMLRPC_CreateVector(key, determine_vector_type(val->value.ht)); zend_hash_internal_pointer_reset(val->value.ht); while(1) { int res = my_zend_hash_get_current_key(val->value.ht, &my_key, &num_index); if(res == HASH_KEY_IS_LONG) { if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) { XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(0, *pIter, depth++)); } } else if(res == HASH_KEY_NON_EXISTANT) { break; } else if(res == HASH_KEY_IS_STRING) { if(zend_hash_get_current_data(val->value.ht, (void**)&pIter) == SUCCESS) { XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(my_key, *pIter, depth++)); } } zend_hash_move_forward(val->value.ht); } } break; default: break; } } } return xReturn; } static XMLRPC_VALUE PHP_to_XMLRPC(pval* root_val) { return PHP_to_XMLRPC_worker(NULL, root_val, 0); } /* recursively convert xmlrpc values into php values */ static pval* XMLRPC_to_PHP(XMLRPC_VALUE el) { pval* elem = NULL; char* pBuf; const char* pStr; if(el) { XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el); MAKE_STD_ZVAL(elem); /* init. very important. spent a frustrating day finding this out. */ switch(type) { case xmlrpc_empty: elem->type = IS_NULL; break; case xmlrpc_string: pStr = XMLRPC_GetValueString(el); if(pStr) { elem->value.str.len = XMLRPC_GetValueStringLen(el); elem->value.str.val = estrndup(pStr, elem->value.str.len); elem->type = IS_STRING; } break; case xmlrpc_int: elem->value.lval = XMLRPC_GetValueInt(el); elem->type = IS_LONG; break; case xmlrpc_boolean: elem->value.lval = XMLRPC_GetValueBoolean(el); elem->type = IS_BOOL; break; case xmlrpc_double: elem->value.dval = XMLRPC_GetValueDouble(el); elem->type = IS_DOUBLE; break; case xmlrpc_datetime: elem->value.str.len = XMLRPC_GetValueStringLen(el); elem->value.str.val = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), elem->value.str.len); elem->type = IS_STRING; break; case xmlrpc_base64: pStr = XMLRPC_GetValueBase64(el); if(pStr) { elem->value.str.len = XMLRPC_GetValueStringLen(el); elem->value.str.val = estrndup(pStr, elem->value.str.len); elem->type = IS_STRING; } break; case xmlrpc_vector: if(array_init(elem) == SUCCESS) { XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el); while( xIter ) { pval* val = XMLRPC_to_PHP(xIter); if(val) { add_pval(elem, XMLRPC_GetValueID(xIter), &val); } xIter = XMLRPC_VectorNext(el); } } break; default: break; } set_pval_xmlrpc_type(elem, type); } return elem; } /* {{{ proto string xmlrpc_encode_request(string method, mixed params) generate xml for a method request */ PHP_FUNCTION(xmlrpc_encode_request) { XMLRPC_REQUEST xRequest = NULL; pval* method, *vals, *out_opts; char* outBuf; php_output_options out; if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &method, &vals, &out_opts) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } set_output_options(&out, (ARG_COUNT(ht) == 3) ? out_opts : 0); if(return_value_used) { xRequest = XMLRPC_RequestNew(); if(xRequest) { XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out); if(method->type == IS_NULL) { XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response); } else { XMLRPC_RequestSetMethodName(xRequest, method->value.str.val); XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call); } if(vals->type != IS_NULL) { XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(vals)); } outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0); if(outBuf) { RETVAL_STRING(outBuf, 1); free(outBuf); } XMLRPC_RequestFree(xRequest, 1); } } } /* {{{ proto string xmlrpc_encode(mixed value) generate xml for a PHP value */ PHP_FUNCTION(xmlrpc_encode) { XMLRPC_VALUE xOut = NULL; pval* arg1, *out_opts; php_output_options out; char* outBuf; if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } if( return_value_used ) { /* convert native php type to xmlrpc type */ xOut = PHP_to_XMLRPC(arg1); /* generate raw xml from xmlrpc data */ outBuf = XMLRPC_VALUE_ToXML(xOut, 0); if(xOut) { if(outBuf) { RETVAL_STRING(outBuf, 1); free(outBuf); } /* cleanup */ XMLRPC_CleanupValue(xOut); } } } /* }}} */ pval* decode_request_worker(pval* xml_in, pval* encoding_in, pval* method_name_out) { pval* retval = NULL; XMLRPC_REQUEST response; STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {0}; opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in->value.str.val) : ENCODING_DEFAULT; /* generate XMLRPC_REQUEST from raw xml */ response = XMLRPC_REQUEST_FromXML(xml_in->value.str.val, xml_in->value.str.len, &opts); if(response) { /* convert xmlrpc data to native php types */ retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response)); if(XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) { if(method_name_out) { convert_to_string(method_name_out); method_name_out->type = IS_STRING; method_name_out->value.str.val = estrdup(XMLRPC_RequestGetMethodName(response)); method_name_out->value.str.len = strlen(method_name_out->value.str.val); } } /* dust, sweep, and mop */ XMLRPC_RequestFree(response, 1); } return retval; } /* {{{ proto array xmlrpc_decode_request(string xml, string& method, [string encoding]) decode xml into native php types */ PHP_FUNCTION(xmlrpc_decode_request) { pval* xml, *method, *encoding = NULL; if( !(ARG_COUNT(ht) == 2 || ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &xml, &method, &encoding) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } #if ZEND_MODULE_API_NO < 20010901 if (!ParameterPassedByReference(ht,2)) { zend_error(E_WARNING,"second argument to xmlrpc_decode_request() passed by value, expecting reference"); } #endif convert_to_string(xml); convert_to_string(method); if(ARG_COUNT(ht) == 3) { convert_to_string(encoding); } if(return_value_used) { pval* retval = decode_request_worker(xml, encoding, method); if(retval) { *return_value = *retval; zval_copy_ctor(return_value); } } } /* }}} */ /* {{{ proto array xmlrpc_decode(string xml, [string encoding]) decode xml into native php types */ PHP_FUNCTION(xmlrpc_decode) { pval* arg1, *arg2 = NULL; if( !(ARG_COUNT(ht) == 1 || ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg1, &arg2) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } convert_to_string(arg1); if(ARG_COUNT(ht) == 2) { convert_to_string(arg2); } if(return_value_used) { pval* retval = decode_request_worker(arg1, arg2, NULL); if(retval) { *return_value = *retval; zval_copy_ctor(return_value); } } } /* }}} */ /************************* * server related methods * *************************/ /* {{{ proto handle xmlrpc_server_create() create an xmlrpc server */ PHP_FUNCTION(xmlrpc_server_create) { if(ARG_COUNT(ht) != 0) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } if(return_value_used) { pval *method_map, *introspection_map; MAKE_STD_ZVAL(method_map); MAKE_STD_ZVAL(introspection_map); if(array_init(method_map) == SUCCESS && array_init(introspection_map) == SUCCESS) { /* allocate server data. free'd in destroy_server_data() */ xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data)); if(server) { server->method_map = method_map; server->introspection_map = introspection_map; server->server_ptr = XMLRPC_ServerCreate(); XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback); /* store for later use */ ZEND_REGISTER_RESOURCE(return_value,server, XMLRPCG(le_xmlrpc_server)); } } } } /* {{{ proto void xmlrpc_server_destroy(handle server) destroy server resources */ PHP_FUNCTION(xmlrpc_server_destroy) { pval* arg1; int bSuccess = FAILURE; if(ARG_COUNT(ht) != 1 || getParameters(ht, 1, &arg1) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } if(arg1->type == IS_RESOURCE) { int type; xmlrpc_server_data *server = zend_list_find(arg1->value.lval, &type); if(server && type == XMLRPCG(le_xmlrpc_server)) { bSuccess = zend_list_delete(arg1->value.lval); /* called by hashtable destructor * destroy_server_data(server); */ } } RETVAL_LONG(bSuccess == SUCCESS); } /* called by xmlrpc C engine as method handler for all registered methods. * it then calls the corresponding PHP function to handle the method. */ static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data) { pval *retval_ptr; xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data; pval* xmlrpc_params; pval* callback_params[3]; /* convert xmlrpc to native php types */ xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest)); /* setup data hoojum */ callback_params[0] = pData->xmlrpc_method; callback_params[1] = xmlrpc_params; callback_params[2] = pData->caller_params; /* Use same C function for all methods */ /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */ call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params); pData->php_executed = 1; } /* called by the C server when it first receives an introspection request. We pass this on to * our PHP listeners, if any */ static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) { pval *retval_ptr, **php_function; pval* callback_params[1]; xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data; MAKE_STD_ZVAL(retval_ptr); retval_ptr->type = IS_NULL; /* setup data hoojum */ callback_params[0] = pData->caller_params; /* loop through and call all registered callbacks */ zend_hash_internal_pointer_reset(pData->server->introspection_map->value.ht); while(1) { if(zend_hash_get_current_data(pData->server->introspection_map->value.ht, (void**)&php_function) == SUCCESS) { /* php func prototype: function string user_func($user_params) */ if(call_user_function(CG(function_table), NULL, *php_function, retval_ptr, 1, callback_params) == SUCCESS) { XMLRPC_VALUE xData; STRUCT_XMLRPC_ERROR err = {0}; /* return value should be a string */ convert_to_string(retval_ptr); xData = XMLRPC_IntrospectionCreateDescription(retval_ptr->value.str.val, &err); if(xData) { if(!XMLRPC_ServerAddIntrospectionData(server, xData)) { zend_error(E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", (*php_function)->value.str.val); } XMLRPC_CleanupValue(xData); } else { /* could not create description */ if(err.xml_elem_error.parser_code) { zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to add introspection data returned from %s()", err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, (*php_function)->value.str.val); } else { zend_error(E_WARNING, "Unable to add introspection data returned from %s()", (*php_function)->value.str.val); } } } else { /* user func failed */ zend_error(E_WARNING, "Error calling user introspection callback: %s()", (*php_function)->value.str.val); } } else { break; } zend_hash_move_forward(pData->server->introspection_map->value.ht); } /* so we don't call the same callbacks ever again */ zend_hash_clean(pData->server->introspection_map->value.ht); } /* {{{ proto boolean xmlrpc_server_register_method(handle server, string method_name, string function) register a php function to handle method matching method_name */ PHP_FUNCTION(xmlrpc_server_register_method) { pval* method_key, *method_name, *handle, *method_name_save; int type; xmlrpc_server_data* server; /* get some params. should be 3 */ if( !(ARG_COUNT(ht) == 3) || getParameters(ht, ARG_COUNT(ht), &handle, &method_key, &method_name) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } server = zend_list_find(handle->value.lval, &type); if(type == XMLRPCG(le_xmlrpc_server)) { /* register with C engine. every method just calls our standard callback, * and it then dispatches to php as necessary */ if(XMLRPC_ServerRegisterMethod(server->server_ptr, method_key->value.str.val, php_xmlrpc_callback)) { /* save for later use */ MAKE_STD_ZVAL(method_name_save); *method_name_save = *method_name; zval_copy_ctor(method_name_save); /* register our php method */ add_pval(server->method_map, method_key->value.str.val, &method_name_save); RETURN_BOOL(1); } } RETURN_BOOL(0); } /* {{{ proto boolean xmlrpc_server_register_introspection_callback(handle server, string function) register a php function to generate documentation */ PHP_FUNCTION(xmlrpc_server_register_introspection_callback) { pval* method_key, *method_name, *handle, *method_name_save; int type; xmlrpc_server_data* server; /* get some params. should be 2 */ if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &method_name) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } server = zend_list_find(handle->value.lval, &type); if(type == XMLRPCG(le_xmlrpc_server)) { { /* save for later use */ MAKE_STD_ZVAL(method_name_save); *method_name_save = *method_name; zval_copy_ctor(method_name_save); /* register our php method */ add_pval(server->introspection_map, NULL, &method_name_save); RETURN_BOOL(1); } } RETURN_BOOL(0); } /* this function is itchin for a re-write */ /* {{{ proto mixed xmlrpc_server_call_method(handle server, string xml, mixed user_data, [array output_options]) parse xml request and call method */ PHP_FUNCTION(xmlrpc_server_call_method) { xmlrpc_callback_data data = {0}; XMLRPC_REQUEST xRequest; STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts; xmlrpc_server_data* server; pval *rawxml, *caller_params, *handle, *output_opts; int type; php_output_options out; /* get params. 3 or 4 params ok */ if(ARG_COUNT(ht) == 4) { if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params, &output_opts) != SUCCESS) { WRONG_PARAM_COUNT; } /* user output options */ set_output_options(&out, output_opts); } else if(ARG_COUNT(ht) == 3) { if(getParameters(ht, ARG_COUNT(ht), &handle, &rawxml, &caller_params) != SUCCESS) { WRONG_PARAM_COUNT; } /* user output options */ set_output_options(&out, NULL); } else { WRONG_PARAM_COUNT; } server = zend_list_find(handle->value.lval, &type); if(type == XMLRPCG(le_xmlrpc_server)) { /* HACK: use output encoding for now */ input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding); /* generate an XMLRPC_REQUEST from the raw xml input */ xRequest = XMLRPC_REQUEST_FromXML(rawxml->value.str.val, rawxml->value.str.len, &input_opts); if(xRequest) { /* check if we have a method name -- indicating success and all manner of good things */ if(XMLRPC_RequestGetMethodName(xRequest)) { pval** php_function, *returned = NULL; XMLRPC_VALUE xAnswer = NULL; MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */ MAKE_STD_ZVAL(data.return_data); data.return_data->type = IS_NULL; /* in case value is never init'd, we don't dtor to think it is a string or something */ data.xmlrpc_method->type = IS_NULL; /* setup some data to pass to the callback function */ data.xmlrpc_method->value.str.val = estrdup(XMLRPC_RequestGetMethodName(xRequest)); data.xmlrpc_method->value.str.len = strlen(data.xmlrpc_method->value.str.val); data.xmlrpc_method->type = IS_STRING; data.caller_params = caller_params; data.php_executed = 0; data.server = server; /* check if the called method has been previous registered */ if(zend_hash_find(server->method_map->value.ht, data.xmlrpc_method->value.str.val, data.xmlrpc_method->value.str.len + 1, (void**)&php_function) == SUCCESS) { data.php_function = *php_function; } /* We could just call the php method directly ourselves at this point, but we do this * with a C callback in case the xmlrpc library ever implements some cool usage stats, * or somesuch. */ xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data); if(xAnswer) { if(out.b_php_out) { zval_dtor(data.return_data); FREE_ZVAL(data.return_data); data.return_data = XMLRPC_to_PHP(xAnswer); } } else if(data.php_executed) { if(!out.b_php_out) { xAnswer = PHP_to_XMLRPC(data.return_data); } } /* should we return data as xml? */ if(!out.b_php_out) { XMLRPC_REQUEST xResponse = XMLRPC_RequestNew(); if(xResponse) { char* outBuf = 0; int buf_len = 0; /* set some required request hoojum */ XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out); XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response); XMLRPC_RequestSetData(xResponse, xAnswer); /* generate xml */ outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len); if(outBuf) { RETVAL_STRINGL(outBuf, buf_len, 1); free(outBuf); } /* cleanup after ourselves. what a sty! */ XMLRPC_RequestFree(xResponse, 0); } } /* or as native php types? */ else { *return_value = *data.return_data; zval_copy_ctor(return_value); } /* cleanup after ourselves. what a sty! */ zval_dtor(data.xmlrpc_method); FREE_ZVAL(data.xmlrpc_method); zval_dtor(data.return_data); FREE_ZVAL(data.return_data); if(xAnswer) { XMLRPC_CleanupValue(xAnswer); } } XMLRPC_RequestFree(xRequest, 1); } } } /* {{{ proto int xmlrpc_server_add_introspection_data(handle server, array desc) add introspection documentation */ PHP_FUNCTION(xmlrpc_server_add_introspection_data) { pval *method, *handle, *desc; int type; xmlrpc_server_data* server; /* get some params. should be 2 */ if ( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &handle, &desc) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } server = zend_list_find(handle->value.lval, &type); if (type == XMLRPCG(le_xmlrpc_server)) { XMLRPC_VALUE xDesc = PHP_to_XMLRPC(desc); if (xDesc) { int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc); XMLRPC_CleanupValue(xDesc); RETURN_LONG(retval); } } RETURN_LONG(0); } /* {{{ proto array xmlrpc_parse_method_descriptions(string xml) decode xml into a list of method descriptions */ PHP_FUNCTION(xmlrpc_parse_method_descriptions) { pval* arg1, *arg2, *retval; if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg1) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } convert_to_string(arg1); if(return_value_used) { STRUCT_XMLRPC_ERROR err = {0}; XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1->value.str.val, &err); if(xVal) { retval = XMLRPC_to_PHP(xVal); if(retval) { *return_value = *retval; zval_copy_ctor(return_value); } /* dust, sweep, and mop */ XMLRPC_CleanupValue(xVal); } else { /* could not create description */ if(err.xml_elem_error.parser_code) { zend_error(E_WARNING, "xml parse error: [line %i, column %i, message: %s] Unable to create introspection data", err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error); } else { zend_error(E_WARNING, "Invalid xml structure. Unable to create introspection data"); } zend_error(E_WARNING, "xml parse error. no method description created"); } } } /************ * type data * ************/ #define XMLRPC_TYPE_COUNT 9 #define XMLRPC_VECTOR_TYPE_COUNT 4 #define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT) /* return a string matching a given xmlrpc type */ static const char** get_type_str_mapping() { static const char* str_mapping[TYPE_STR_MAP_SIZE]; static int first = 1; if(first) { /* warning. do not add/delete without changing size define */ str_mapping[xmlrpc_none] = "none"; str_mapping[xmlrpc_empty] = "empty"; str_mapping[xmlrpc_base64] = "base64"; str_mapping[xmlrpc_boolean] = "boolean"; str_mapping[xmlrpc_datetime] = "datetime"; str_mapping[xmlrpc_double] = "double"; str_mapping[xmlrpc_int] = "int"; str_mapping[xmlrpc_string] = "string"; str_mapping[xmlrpc_vector] = "vector"; str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none"; str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array"; str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed"; str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct"; first = 0; } return (const char**)str_mapping; } /* map an xmlrpc type to a string */ const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) { const char** str_mapping = get_type_str_mapping(); if(vtype == xmlrpc_vector_none) { return str_mapping[type]; } else { return str_mapping[XMLRPC_TYPE_COUNT + vtype]; } } /* map a string to an xmlrpc type */ XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) { const char** str_mapping = get_type_str_mapping(); int i; if(str) { for(i = 0; i < XMLRPC_TYPE_COUNT; i++) { if(!strcmp(str_mapping[i], str)) { return (XMLRPC_VALUE_TYPE)i; } } } return xmlrpc_none; } /* map a string to an xmlrpc vector type */ XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) { const char** str_mapping = get_type_str_mapping(); int i; if(str) { for(i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) { if(!strcmp(str_mapping[i], str)) { return (XMLRPC_VECTOR_TYPE)(i - XMLRPC_TYPE_COUNT); } } } return xmlrpc_none; } /* set a given value to a particular type. * note: this only works on strings, and only for date and base64, * which do not have native php types. black magic lies herein. */ int set_pval_xmlrpc_type(pval* value, XMLRPC_VALUE_TYPE type) { int bSuccess = FAILURE; /* we only really care about strings because they can represent * base64 and datetime. all other types have corresponding php types */ if(value->type == IS_STRING) { if(type == xmlrpc_base64 || type == xmlrpc_datetime) { const char* typestr = xmlrpc_type_as_str(type, xmlrpc_vector_none); pval* type; MAKE_STD_ZVAL(type); type->type = IS_STRING; type->value.str.val = estrdup(typestr); type->value.str.len = strlen(typestr); convert_to_object(value); bSuccess = zend_hash_update(value->value.obj.properties, OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL); } } return bSuccess; } /* return xmlrpc type of a php value */ XMLRPC_VALUE_TYPE get_pval_xmlrpc_type(pval* value, pval** newvalue) { XMLRPC_VALUE_TYPE type = xmlrpc_none; if(value) { switch(value->type) { case IS_NULL: type = xmlrpc_base64; break; #ifndef BOOL_AS_LONG /* Right thing to do, but it breaks some legacy code. */ case IS_BOOL: type = xmlrpc_boolean; break; #else case IS_BOOL: #endif case IS_LONG: case IS_RESOURCE: type = xmlrpc_int; break; case IS_DOUBLE: type = xmlrpc_double; break; case IS_CONSTANT: type = xmlrpc_string; break; case IS_STRING: type = xmlrpc_string; break; case IS_ARRAY: case IS_CONSTANT_ARRAY: type = xmlrpc_vector; break; case IS_OBJECT: { pval** attr; type = xmlrpc_vector; if(zend_hash_find(value->value.obj.properties, OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**)&attr) == SUCCESS) { if((*attr)->type == IS_STRING) { type = xmlrpc_str_as_type((*attr)->value.str.val); } } break; } } /* if requested, return an unmolested (magic removed) copy of the value */ if(newvalue) { pval** val; if( (type == xmlrpc_base64 && value->type != IS_NULL) || type == xmlrpc_datetime) { if(zend_hash_find(value->value.obj.properties, OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), (void**)&val) == SUCCESS) { *newvalue = *val; } } else { *newvalue = value; } } } return type; } /* {{{ proto bool xmlrpc_set_type(string value, string type) set xmlrpc type, base64 or datetime, for a php string value */ PHP_FUNCTION(xmlrpc_set_type) { pval* arg, *type; XMLRPC_VALUE_TYPE vtype; if( !(ARG_COUNT(ht) == 2) || getParameters(ht, ARG_COUNT(ht), &arg, &type) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } #if ZEND_MODULE_API_NO < 20010901 if (!ParameterPassedByReference(ht,1)) { zend_error(E_WARNING,"first argument to xmlrpc_set_type() passed by value, expecting reference"); } #endif convert_to_string(type); vtype = xmlrpc_str_as_type(type->value.str.val); if(vtype != xmlrpc_none) { if(set_pval_xmlrpc_type(arg, vtype) == SUCCESS) { RETURN_TRUE; } } else { zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type->value.str.val); } RETURN_FALSE; } /* {{{ proto string xmlrpc_get_type(mixed value) get xmlrpc type for a php value. especially useful for base64 and datetime strings */ PHP_FUNCTION(xmlrpc_get_type) { pval* arg; XMLRPC_VALUE_TYPE type; XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none; if( !(ARG_COUNT(ht) == 1) || getParameters(ht, ARG_COUNT(ht), &arg) == FAILURE) { WRONG_PARAM_COUNT; /* prints/logs a warning and returns */ } type = get_pval_xmlrpc_type(arg, 0); if(type == xmlrpc_vector) { vtype = determine_vector_type(arg->value.ht); } RETURN_STRING((char*)xmlrpc_type_as_str(type, vtype), 1); } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */ Index: php4/ext/xmlrpc/libxmlrpc/Makefile.in +++ php4/ext/xmlrpc/libxmlrpc/Makefile.in LTLIBRARY_NAME = libxmlrpc.la LTLIBRARY_SOURCES = base64.c simplestring.c xml_to_dandarpc.c \ xmlrpc_introspection.c encodings.c system_methods.c \ xml_to_xmlrpc.c queue.c xml_element.c xmlrpc.c DEFS = -DVERSION="0.41" include $(top_srcdir)/build/dynlib.mk Index: php4/ext/xmlrpc/libxmlrpc/acinclude.m4 +++ php4/ext/xmlrpc/libxmlrpc/acinclude.m4 # Local macros for automake & autoconf AC_DEFUN(XMLRPC_FUNCTION_CHECKS,[ # Standard XMLRPC list AC_CHECK_FUNCS( \ strtoul strtoull snprintf \ strstr strpbrk strerror\ memcpy memmove) ]) AC_DEFUN(XMLRPC_HEADER_CHECKS,[ AC_HEADER_STDC AC_CHECK_HEADERS(xmlparse.h xmltok.h stdlib.h strings.h string.h) ]) AC_DEFUN(XMLRPC_TYPE_CHECKS,[ AC_REQUIRE([AC_C_CONST]) AC_REQUIRE([AC_C_INLINE]) AC_CHECK_SIZEOF(char, 1) AC_CHECK_SIZEOF(int, 4) AC_CHECK_SIZEOF(long, 4) AC_CHECK_SIZEOF(long long, 8) AC_TYPE_SIZE_T AC_HEADER_TIME AC_TYPE_UID_T XMLRPC_CHECK_ULONG XMLRPC_CHECK_UCHAR XMLRPC_CHECK_UINT XMLRPC_CHECK_INT_8_16_32 XMLRPC_TYPE_QSORT ]) Index: php4/ext/xmlrpc/libxmlrpc/base64.c +++ php4/ext/xmlrpc/libxmlrpc/base64.c static const char rcsid[] = "#(@) $Id: base64.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; /* Encode or decode file as MIME base64 (RFC 1341) by John Walker http://www.fourmilab.ch/ This program is in the public domain. */ #include <stdio.h> /* ENCODE -- Encode binary file into base64. */ #include <stdlib.h> #include "base64.h" static unsigned char dtable[512]; void buffer_new(struct buffer_st *b) { b->length = 512; b->data = malloc(sizeof(char)*(b->length)); b->data[0] = 0; b->ptr = b->data; b->offset = 0; } void buffer_add(struct buffer_st *b, char c) { *(b->ptr++) = c; b->offset++; if (b->offset == b->length) { b->length += 512; b->data = realloc(b->data, b->length); b->ptr = b->data + b->offset; } } void buffer_delete(struct buffer_st *b) { free(b->data); b->length = 0; b->offset = 0; b->ptr = NULL; b->data = NULL; } void base64_encode(struct buffer_st *b, const char *source, int length) { int i, hiteof = 0; int offset = 0; int olen; char *dest; olen = 0; buffer_new(b); /* Fill dtable with character encodings. */ for (i = 0; i < 26; i++) { dtable[i] = 'A' + i; dtable[26 + i] = 'a' + i; } for (i = 0; i < 10; i++) { dtable[52 + i] = '0' + i; } dtable[62] = '+'; dtable[63] = '/'; while (!hiteof) { unsigned char igroup[3], ogroup[4]; int c, n; igroup[0] = igroup[1] = igroup[2] = 0; for (n = 0; n < 3; n++) { c = *(source++); offset++; if (offset > length) { hiteof = 1; break; } igroup[n] = (unsigned char) c; } if (n > 0) { ogroup[0] = dtable[igroup[0] >> 2]; ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)]; ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)]; ogroup[3] = dtable[igroup[2] & 0x3F]; /* Replace characters in output stream with "=" pad characters if fewer than three characters were read from the end of the input stream. */ if (n < 3) { ogroup[3] = '='; if (n < 2) { ogroup[2] = '='; } } for (i = 0; i < 4; i++) { buffer_add(b, ogroup[i]); if (!(b->offset % 72)) { // buffer_add(b, '\r'); buffer_add(b, '\n'); } } } } // buffer_add(b, '\r'); buffer_add(b, '\n'); } void base64_decode(struct buffer_st *bfr, const char *source, int length) { int i; int offset = 0; int endoffile; int count; buffer_new(bfr); for (i = 0; i < 255; i++) { dtable[i] = 0x80; } for (i = 'A'; i <= 'Z'; i++) { dtable[i] = 0 + (i - 'A'); } for (i = 'a'; i <= 'z'; i++) { dtable[i] = 26 + (i - 'a'); } for (i = '0'; i <= '9'; i++) { dtable[i] = 52 + (i - '0'); } dtable['+'] = 62; dtable['/'] = 63; dtable['='] = 0; endoffile = 0; /*CONSTANTCONDITION*/ while (1) { unsigned char a[4], b[4], o[3]; for (i = 0; i < 4; i++) { int c; while (1) { c = *(source++); offset++; if (offset > length) endoffile = 1; if (isspace(c) || c == '\n' || c == '\r') continue; break; } if (endoffile) { /* if (i > 0) { fprintf(stderr, "Input file incomplete.\n"); exit(1); } */ return; } if (dtable[c] & 0x80) { /* fprintf(stderr, "Offset %i length %i\n", offset, length); fprintf(stderr, "character '%c:%x:%c' in input file.\n", c, c, dtable[c]); exit(1); */ i--; continue; } a[i] = (unsigned char) c; b[i] = (unsigned char) dtable[c]; } o[0] = (b[0] << 2) | (b[1] >> 4); o[1] = (b[1] << 4) | (b[2] >> 2); o[2] = (b[2] << 6) | b[3]; i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3); count = 0; while (count < i) { buffer_add(bfr, o[count++]); } if (i < 3) { return; } } } Index: php4/ext/xmlrpc/libxmlrpc/base64.h +++ php4/ext/xmlrpc/libxmlrpc/base64.h /* Encode or decode file as MIME base64 (RFC 1341) by John Walker http://www.fourmilab.ch/ This program is in the public domain. */ struct buffer_st { char *data; int length; char *ptr; int offset; }; void buffer_new(struct buffer_st *b); void buffer_add(struct buffer_st *b, char c); void buffer_delete(struct buffer_st *b); void base64_encode(struct buffer_st *b, const char *source, int length); void base64_decode(struct buffer_st *b, const char *source, int length); /* #define DEBUG_MALLOC */ #ifdef DEBUG_MALLOC void *_malloc_real(size_t s, char *file, int line); void _free_real(void *p, char *file, int line); #define malloc(s) _malloc_real(s,__FILE__,__LINE__) #define free(p) _free_real(p, __FILE__,__LINE__) #endif Index: php4/ext/xmlrpc/libxmlrpc/encodings.c +++ php4/ext/xmlrpc/libxmlrpc/encodings.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ static const char rcsid[] = "#(@) $Id: encodings.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; #include <errno.h> #include <iconv.h> #include "encodings.h" static char* convert(const char* src, int src_len, int *new_len, const char* from_enc, const char* to_enc) { char* outbuf = 0; if(src && src_len && from_enc && to_enc) { int outlenleft = src_len; int outlen = src_len; int inlenleft = src_len; iconv_t ic = iconv_open(to_enc, from_enc); char* src_ptr = (char*)src; char* out_ptr = 0; if(ic != (iconv_t)-1) { size_t st; outbuf = (char*)malloc(outlen + 1); if(outbuf) { out_ptr = (char*)outbuf; while(inlenleft) { st = iconv(ic, &src_ptr, &inlenleft, &out_ptr, &outlenleft); if(st == -1) { if(errno == E2BIG) { int diff = out_ptr - outbuf; outlen += inlenleft; outlenleft += inlenleft; outbuf = (char*)realloc(outbuf, outlen + 1); if(!outbuf) { break; } out_ptr = outbuf + diff; } else { free(outbuf); outbuf = 0; break; } } } } iconv_close(ic); } outlen -= outlenleft; if(new_len) { *new_len = outbuf ? outlen : 0; } if(outbuf) { outbuf[outlen] = 0; } } return outbuf; } /* returns a new string that must be freed */ char* utf8_encode(const char *s, int len, int *newlen, const char* encoding) { return convert(s, len, newlen, encoding, "UTF-8"); } /* returns a new string, possibly decoded */ char* utf8_decode(const char *s, int len, int *newlen, const char* encoding) { return convert(s, len, newlen, "UTF-8", encoding); } Index: php4/ext/xmlrpc/libxmlrpc/encodings.h +++ php4/ext/xmlrpc/libxmlrpc/encodings.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef __ENCODINGS__H #define __ENCODINGS__H /* these defines are for legacy purposes. */ #define encoding_utf_8 "UTF-8" typedef const char* ENCODING_ID; #define utf8_get_encoding_id_string(desired_enc) ((const char*)desired_enc) #define utf8_get_encoding_id_from_string(id_string) ((ENCODING_ID)id_string) char* utf8_encode(const char *s, int len, int *newlen, ENCODING_ID encoding); char* utf8_decode(const char *s, int len, int *newlen, ENCODING_ID encoding); #endif /* __ENCODINGS__H */ Index: php4/ext/xmlrpc/libxmlrpc/libs.mk +++ php4/ext/xmlrpc/libxmlrpc/libs.mk include $(top_builddir)/config_vars.mk LTLIBRARY_OBJECTS = $(LTLIBRARY_SOURCES:.c=.lo) $(LTLIBRARY_OBJECTS_X) LTLIBRARY_SHARED_OBJECTS = $(LTLIBRARY_OBJECTS:.lo=.slo) $(LTLIBRARY_NAME): $(LTLIBRARY_OBJECTS) $(LTLIBRARY_DEPENDENCIES) $(LINK) $(LTLIBRARY_LDFLAGS) $(LTLIBRARY_OBJECTS) $(LTLIBRARY_LIBADD) targets = $(LTLIBRARY_NAME) Index: php4/ext/xmlrpc/libxmlrpc/queue.c +++ php4/ext/xmlrpc/libxmlrpc/queue.c static const char rcsid[] = "#(@) $Id: queue.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; /* * Date last modified: Jan 2001 * Modifications by Dan Libby (dan@libby.com), including: * - various fixes, null checks, etc * - addition of Q_Iter funcs, macros */ /*-************************************************************** * * File : q.c * * Author: Peter Yard [1993.01.02] -- 02 Jan 1993 * * Disclaimer: This code is released to the public domain. * * Description: * Generic double ended queue (Deque pronounced DEK) for handling * any data types, with sorting. * * By use of various functions in this module the caller * can create stacks, queues, lists, doubly linked lists, * sorted lists, indexed lists. All lists are dynamic. * * It is the responsibility of the caller to malloc and free * memory for insertion into the queue. A pointer to the object * is used so that not only can any data be used but various kinds * of data can be pushed on the same queue if one so wished e.g. * various length string literals mixed with pointers to structures * or integers etc. * * Enhancements: * A future improvement would be the option of multiple "cursors" * so that multiple locations could occur in the one queue to allow * placemarkers and additional flexibility. Perhaps even use queue * itself to have a list of cursors. * * Usage: * * /x init queue x/ * queue q; * Q_Init(&q); * * To create a stack : * * Q_PushHead(&q, &mydata1); /x push x/ * Q_PushHead(&q, &mydata2); * ..... * data_ptr = Q_PopHead(&q); /x pop x/ * ..... * data_ptr = Q_Head(&q); /x top of stack x/ * * To create a FIFO: * * Q_PushHead(&q, &mydata1); * ..... * data_ptr = Q_PopTail(&q); * * To create a double list: * * data_ptr = Q_Head(&q); * .... * data_ptr = Q_Next(&q); * data_ptr = Q_Tail(&q); * if (Q_IsEmpty(&q)) .... * ..... * data_ptr = Q_Previous(&q); * * To create a sorted list: * * Q_PushHead(&q, &mydata1); /x push x/ * Q_PushHead(&q, &mydata2); * ..... * if (!Q_Sort(&q, MyFunction)) * .. error .. * * /x fill in key field of mydata1. * * NB: Q_Find does linear search * x/ * * if (Q_Find(&q, &mydata1, MyFunction)) * { * /x found it, queue cursor now at correct record x/ * /x can retrieve with x/ * data_ptr = Q_Get(&q); * * /x alter data , write back with x/ * Q_Put(&q, data_ptr); * } * * /x Search with binary search x/ * if (Q_Seek(&q, &mydata, MyFunction)) * /x etc x/ * * ****************************************************************/ #include <stdlib.h> #include "queue.h" static void QuickSort(void *list[], int low, int high, int (*Comp)(const void *, const void *)); static int Q_BSearch(queue *q, void *key, int (*Comp)(const void *, const void *)); /* The index: a pointer to pointers */ static void **index; static datanode **posn_index; /*** * ** function : Q_Init * ** purpose : Initialise queue object and pointers. * ** parameters : 'queue' pointer. * ** returns : True_ if init successful else False_ * ** comments : ***/ int Q_Init(queue *q) { if(q) { q->head = q->tail = NULL; q->cursor = q->head; q->size = 0; q->sorted = False_; } return True_; } /*** * ** function : Q_AtHead * ** purpose : tests if cursor is at head of queue * ** parameters : 'queue' pointer. * ** returns : boolean - True_ is at head else False_ * ** comments : * ***/ int Q_AtHead(queue *q) { return(q && q->cursor == q->head); } /*** * ** function : Q_AtTail * ** purpose : boolean test if cursor at tail of queue * ** parameters : 'queue' pointer to test. * ** returns : True_ or False_ * ** comments : * ***/ int Q_AtTail(queue *q) { return(q && q->cursor == q->tail); } /*** * ** function : Q_IsEmpty * ** purpose : test if queue has nothing in it. * ** parameters : 'queue' pointer * ** returns : True_ if IsEmpty queue, else False_ * ** comments : * ***/ inline int Q_IsEmpty(queue *q) { return(!q || q->size == 0); } /*** * ** function : Q_Size * ** purpose : return the number of elements in the queue * ** parameters : queue pointer * ** returns : number of elements * ** comments : * ***/ int Q_Size(queue *q) { return q ? q->size : 0; } /*** * ** function : Q_Head * ** purpose : position queue cursor to first element (head) of queue. * ** parameters : 'queue' pointer * ** returns : pointer to data at head. If queue is IsEmpty returns NULL * ** comments : * ***/ void *Q_Head(queue *q) { if(Q_IsEmpty(q)) return NULL; q->cursor = q->head; return q->cursor->data; } /*** * ** function : Q_Tail * ** purpose : locate cursor at tail of queue. * ** parameters : 'queue' pointer * ** returns : pointer to data at tail , if queue IsEmpty returns NULL * ** comments : * ***/ void *Q_Tail(queue *q) { if(Q_IsEmpty(q)) return NULL; q->cursor = q->tail; return q->cursor->data; } /*** * ** function : Q_PushHead * ** purpose : put a data pointer at the head of the queue * ** parameters : 'queue' pointer, void pointer to the data. * ** returns : True_ if success else False_ if unable to push data. * ** comments : * ***/ int Q_PushHead(queue *q, void *d) { if(q && d) { node *n; datanode *p; p = malloc(sizeof(datanode)); if(p == NULL) return False_; n = q->head; q->head = (node*)p; q->head->prev = NULL; if(q->size == 0) { q->head->next = NULL; q->tail = q->head; } else { q->head->next = (datanode*)n; n->prev = q->head; } q->head->data = d; q->size++; q->cursor = q->head; q->sorted = False_; return True_; } return False_; } /*** * ** function : Q_PushTail * ** purpose : put a data element pointer at the tail of the queue * ** parameters : queue pointer, pointer to the data * ** returns : True_ if data pushed, False_ if data not inserted. * ** comments : * ***/ int Q_PushTail(queue *q, void *d) { if(q && d) { node *p; datanode *n; n = malloc(sizeof(datanode)); if(n == NULL) return False_; p = q->tail; q->tail = (node *)n; if(q->size == 0) { q->tail->prev = NULL; q->head = q->tail; } else { q->tail->prev = (datanode *)p; p->next = q->tail; } q->tail->next = NULL; q->tail->data = d; q->cursor = q->tail; q->size++; q->sorted = False_; return True_; } } /*** * ** function : Q_PopHead * ** purpose : remove and return the top element at the head of the * queue. * ** parameters : queue pointer * ** returns : pointer to data element or NULL if queue is IsEmpty. * ** comments : * ***/ void *Q_PopHead(queue *q) { datanode *n; void *d; if(Q_IsEmpty(q)) return NULL; d = q->head->data; n = q->head->next; free(q->head); q->size--; if(q->size == 0) q->head = q->tail = q->cursor = NULL; else { q->head = (node *)n; q->head->prev = NULL; q->cursor = q->head; } q->sorted = False_; return d; } /*** * ** function : Q_PopTail * ** purpose : remove element from tail of queue and return data. * ** parameters : queue pointer * ** returns : pointer to data element that was at tail. NULL if queue * IsEmpty. * ** comments : * ***/ void *Q_PopTail(queue *q) { datanode *p; void *d; if(Q_IsEmpty(q)) return NULL; d = q->tail->data; p = q->tail->prev; free(q->tail); q->size--; if(q->size == 0) q->head = q->tail = q->cursor = NULL; else { q->tail = (node *)p; q->tail->next = NULL; q->cursor = q->tail; } q->sorted = False_; return d; } /*** * ** function : Q_Next * ** purpose : Move to the next element in the queue without popping * ** parameters : queue pointer. * ** returns : pointer to data element of new element or NULL if end * of the queue. * ** comments : This uses the cursor for the current position. Q_Next * only moves in the direction from the head of the queue * to the tail. ***/ void *Q_Next(queue *q) { if(!q) return NULL; if(q->cursor->next == NULL) return NULL; q->cursor = (node *)q->cursor->next; return q->cursor->data ; } /*** * ** function : Q_Previous * ** purpose : Opposite of Q_Next. Move to next element closer to the * head of the queue. * ** parameters : pointer to queue * ** returns : pointer to data of new element else NULL if queue IsEmpty * ** comments : Makes cursor move towards the head of the queue. * ***/ void *Q_Previous(queue *q) { if(!q) return NULL; if(q->cursor->prev == NULL) return NULL; q->cursor = (node *)q->cursor->prev; return q->cursor->data; } void *Q_Iter_Del(queue *q, q_iter iter) { void *d; datanode *n, *p; if(!q) return NULL; if(iter == NULL) return NULL; if(iter == (q_iter)q->head) return Q_PopHead(q); if(iter == (q_iter)q->tail) return Q_PopTail(q); n = ((node*)iter)->next; p = ((node*)iter)->prev; d = ((node*)iter)->data; free(iter); if(p) { p->next = n; } if (q->cursor == (node*)iter) { if (p) { q->cursor = p; } else { q->cursor = n; } } if (n != NULL) { n->prev = p; } q->size--; q->sorted = False_; return d; } /*** * ** function : Q_DelCur * ** purpose : Delete the current queue element as pointed to by * the cursor. * ** parameters : queue pointer * ** returns : pointer to data element. * ** comments : WARNING! It is the responsibility of the caller to * free any memory. Queue cannot distinguish between * pointers to literals and malloced memory. * ***/ void *Q_DelCur(queue* q) { if(q) { return Q_Iter_Del(q, (q_iter)q->cursor); } return 0; } /*** * ** function : Q_Destroy * ** purpose : Free all queue resources * ** parameters : queue pointer * ** returns : null. * ** comments : WARNING! It is the responsibility of the caller to * free any memory. Queue cannot distinguish between * pointers to literals and malloced memory. * ***/ void Q_Destroy(queue *q) { while(!Q_IsEmpty(q)) { Q_PopHead(q); } } /*** * ** function : Q_Get * ** purpose : get the pointer to the data at the cursor location * ** parameters : queue pointer * ** returns : data element pointer * ** comments : * ***/ void *Q_Get(queue *q) { if(!q) return NULL; if(q->cursor == NULL) return NULL; return q->cursor->data; } /*** * ** function : Q_Put * ** purpose : replace pointer to data with new pointer to data. * ** parameters : queue pointer, data pointer * ** returns : boolean- True_ if successful, False_ if cursor at NULL * ** comments : * ***/ int Q_Put(queue *q, void *data) { if(q && data) { if(q->cursor == NULL) return False_; q->cursor->data = data; return True_; } return False_; } /*** * ** function : Q_Find * ** purpose : Linear search of queue for match with key in *data * ** parameters : queue pointer q, data pointer with data containing key * comparison function here called Comp. * ** returns : True_ if found , False_ if not in queue. * ** comments : Useful for small queues that are constantly changing * and would otherwise need constant sorting with the * Q_Seek function. * For description of Comp see Q_Sort. * Queue cursor left on position found item else at end. * ***/ int Q_Find(queue *q, void *data, int (*Comp)(const void *, const void *)) { void *d; if (q == NULL) { return False_; } d = Q_Head(q); do { if(Comp(d, data) == 0) return True_; d = Q_Next(q); } while(!Q_AtTail(q)); if(Comp(d, data) == 0) return True_; return False_; } /*======== Sorted Queue and Index functions ========= */ static void QuickSort(void *list[], int low, int high, int (*Comp)(const void *, const void *)) { int flag = 1, i, j; void *key, *temp; if(low < high) { i = low; j = high + 1; key = list[ low ]; while(flag) { i++; while(Comp(list[i], key) < 0) i++; j--; while(Comp(list[j], key) > 0) j--; if(i < j) { temp = list[i]; list[i] = list[j]; list[j] = temp; } else flag = 0; } temp = list[low]; list[low] = list[j]; list[j] = temp; QuickSort(list, low, j-1, Comp); QuickSort(list, j+1, high, Comp); } } /*** * ** function : Q_Sort * ** purpose : sort the queue and allow index style access. * ** parameters : queue pointer, comparison function compatible with * with 'qsort'. * ** returns : True_ if sort succeeded. False_ if error occurred. * ** comments : Comp function supplied by caller must return * -1 if data1 < data2 * 0 if data1 == data2 * +1 if data1 > data2 * * for Comp(data1, data2) * * If queue is already sorted it frees the memory of the * old index and starts again. * ***/ int Q_Sort(queue *q, int (*Comp)(const void *, const void *)) { int i; void *d; datanode *dn; /* if already sorted free memory for tag array */ if(q->sorted) { free(index); free(posn_index); q->sorted = False_; } /* Now allocate memory of array, array of pointers */ index = malloc(q->size * sizeof(q->cursor->data)); if(index == NULL) return False_; posn_index = malloc(q->size * sizeof(q->cursor)); if(posn_index == NULL) { free(index); return False_; } /* Walk queue putting pointers into array */ d = Q_Head(q); for(i=0; i < q->size; i++) { index[i] = d; posn_index[i] = q->cursor; d = Q_Next(q); } /* Now sort the index */ QuickSort(index, 0, q->size - 1, Comp); /* Rearrange the actual queue into correct order */ dn = q->head; i = 0; while(dn != NULL) { dn->data = index[i++]; dn = dn->next; } /* Re-position to original element */ if(d != NULL) Q_Find(q, d, Comp); else Q_Head(q); q->sorted = True_; return True_; } /*** * ** function : Q_BSearch * ** purpose : binary search of queue index for node containing key * ** parameters : queue pointer 'q', data pointer of key 'key', * Comp comparison function. * ** returns : integer index into array of node pointers, * or -1 if not found. * ** comments : see Q_Sort for description of 'Comp' function. * ***/ static int Q_BSearch( queue *q, void *key, int (*Comp)(const void *, const void*)) { int low, mid, hi, val; low = 0; hi = q->size - 1; while(low <= hi) { mid = (low + hi) / 2; val = Comp(key, index[ mid ]); if(val < 0) hi = mid - 1; else if(val > 0) low = mid + 1; else /* Success */ return mid; } /* Not Found */ return -1; } /*** * ** function : Q_Seek * ** purpose : use index to locate data according to key in 'data' * ** parameters : queue pointer 'q', data pointer 'data', Comp comparison * function. * ** returns : pointer to data or NULL if could not find it or could * not sort queue. * ** comments : see Q_Sort for description of 'Comp' function. * ***/ void *Q_Seek(queue *q, void *data, int (*Comp)(const void *, const void *)) { int idx; if (q == NULL) { return NULL; } if(!q->sorted) { if(!Q_Sort(q, Comp)) return NULL; } idx = Q_BSearch(q, data, Comp); if(idx < 0) return NULL; q->cursor = posn_index[idx]; return index[idx]; } /*** * ** function : Q_Insert * ** purpose : Insert an element into an indexed queue * ** parameters : queue pointer 'q', data pointer 'data', Comp comparison * function. * ** returns : pointer to data or NULL if could not find it or could * not sort queue. * ** comments : see Q_Sort for description of 'Comp' function. * WARNING! This code can be very slow since each new * element means a new Q_Sort. Should only be used for * the insertion of the odd element ,not the piecemeal * building of an entire queue. ***/ int Q_Insert(queue *q, void *data, int (*Comp)(const void *, const void *)) { if (q == NULL) { return False_; } Q_PushHead(q, data); if(!Q_Sort(q, Comp)) return False_; return True_; } /* read only funcs for iterating through queue. above funcs modify queue */ q_iter Q_Iter_Head(queue *q) { return q ? (q_iter)q->head : NULL; } q_iter Q_Iter_Tail(queue *q) { return q ? (q_iter)q->tail : NULL; } q_iter Q_Iter_Next(q_iter qi) { return qi ? (q_iter)((node*)qi)->next : NULL; } q_iter Q_Iter_Prev(q_iter qi) { return qi ? (q_iter)((node*)qi)->prev : NULL; } void * Q_Iter_Get(q_iter qi) { return qi ? ((node*)qi)->data : NULL; } int Q_Iter_Put(q_iter qi, void* data) { if(qi) { ((node*)qi)->data = data; return True_; } return False_; } Index: php4/ext/xmlrpc/libxmlrpc/queue.h +++ php4/ext/xmlrpc/libxmlrpc/queue.h /* * Date last modified: Jan 2001 * Modifications by Dan Libby (dan@libby.com), including: * - various fixes, null checks, etc * - addition of Q_Iter funcs, macros */ /* * File : q.h * * Peter Yard 02 Jan 1993. * * Disclaimer: This code is released to the public domain. */ #ifndef Q__H #define Q__H #ifndef False_ #define False_ 0 #endif #ifndef True_ #define True_ 1 #endif typedef struct nodeptr datanode; typedef struct nodeptr { void *data ; datanode *prev, *next ; } node ; /* For external use with Q_Iter* funcs */ typedef struct nodeptr* q_iter; typedef struct { node *head, *tail, *cursor; int size, sorted, item_deleted; } queue; typedef struct { void *dataptr; node *loc ; } index_elt ; int Q_Init(queue *q); void Q_Destroy(queue *q); int Q_IsEmpty(queue *q); int Q_Size(queue *q); int Q_AtHead(queue *q); int Q_AtTail(queue *q); int Q_PushHead(queue *q, void *d); int Q_PushTail(queue *q, void *d); void *Q_Head(queue *q); void *Q_Tail(queue *q); void *Q_PopHead(queue *q); void *Q_PopTail(queue *q); void *Q_Next(queue *q); void *Q_Previous(queue *q); void *Q_DelCur(queue *q); void *Q_Get(queue *q); int Q_Put(queue *q, void *data); int Q_Sort(queue *q, int (*Comp)(const void *, const void *)); int Q_Find(queue *q, void *data, int (*Comp)(const void *, const void *)); void *Q_Seek(queue *q, void *data, int (*Comp)(const void *, const void *)); int Q_Insert(queue *q, void *data, int (*Comp)(const void *, const void *)); /* read only funcs for iterating through queue. above funcs modify queue */ q_iter Q_Iter_Head(queue *q); q_iter Q_Iter_Tail(queue *q); q_iter Q_Iter_Next(q_iter qi); q_iter Q_Iter_Prev(q_iter qi); void* Q_Iter_Get(q_iter qi); int Q_Iter_Put(q_iter qi, void* data); // not read only! here for completeness. void* Q_Iter_Del(queue *q, q_iter iter); // not read only! here for completeness. /* Fast (macro'd) versions of above */ #define Q_Iter_Head_F(q) (q ? (q_iter)((queue*)q)->head : NULL) #define Q_Iter_Tail_F(q) (q ? (q_iter)((queue*)q)->tail : NULL) #define Q_Iter_Next_F(qi) (qi ? (q_iter)((node*)qi)->next : NULL) #define Q_Iter_Prev_F(qi) (qi ? (q_iter)((node*)qi)->prev : NULL) #define Q_Iter_Get_F(qi) (qi ? ((node*)qi)->data : NULL) #endif /* Q__H */ Index: php4/ext/xmlrpc/libxmlrpc/simplestring.c +++ php4/ext/xmlrpc/libxmlrpc/simplestring.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ static const char rcsid[] = "#(@) $Id: simplestring.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; #define SIMPLESTRING_INCR 32 /****h* ABOUT/simplestring * NAME * simplestring * AUTHOR * Dan Libby, aka danda (dan@libby.com) * CREATION DATE * 06/2000 * HISTORY * 10/15/2000 -- danda -- adding robodoc documentation * PORTABILITY * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just * about anything with minor mods. * NOTES * This code was written primarily for xmlrpc, but has found some other uses. * * simplestring is, as the name implies, a simple API for dealing with C strings. * Why would I write yet another string API? Because I couldn't find any that were * a) free / GPL, b) simple/lightweight, c) fast, not doing unneccesary strlens all * over the place. So. It is simple, and it seems to work, and it is pretty fast. * * Oh, and it is also binary safe, ie it can handle strings with embedded NULLs, * so long as the real length is passed in. * * And the masses rejoiced. * * BUGS * there must be some. ******/ #include "simplestring.h" #define my_free(thing) if(thing) {free(thing); thing = 0;} /*----------------------** * Begin String Functions * *-----------------------*/ /****f* FUNC/simplestring_init * NAME * simplestring_init * SYNOPSIS * void simplestring_init(simplestring* string) * FUNCTION * initialize string * INPUTS * string - pointer to a simplestring struct that will be initialized * RESULT * void * NOTES * SEE ALSO * simplestring_free () * simplestring_clear () * SOURCE */ void simplestring_init(simplestring* string) { memset(string, 0, sizeof(simplestring)); } /******/ static void simplestring_init_str(simplestring* string) { string->str = (char*)malloc(SIMPLESTRING_INCR); if(string->str) { string->str[0] = 0; string->len = 0; string->size = SIMPLESTRING_INCR; } else { string->size = 0; } } /****f* FUNC/simplestring_clear * NAME * simplestring_clear * SYNOPSIS * void simplestring_clear(simplestring* string) * FUNCTION * clears contents of a string * INPUTS * string - the string value to clear * RESULT * void * NOTES * This function is very fast as it does not de-allocate any memory. * SEE ALSO * * SOURCE */ void simplestring_clear(simplestring* string) { if(string->str) { string->str[0] = 0; } string->len = 0; } /******/ /****f* FUNC/simplestring_free * NAME * simplestring_free * SYNOPSIS * void simplestring_free(simplestring* string) * FUNCTION * frees contents of a string, if any. Does *not* free the simplestring struct itself. * INPUTS * string - value containing string to be free'd * RESULT * void * NOTES * caller is responsible for allocating and freeing simplestring* struct itself. * SEE ALSO * simplestring_init () * SOURCE */ void simplestring_free(simplestring* string) { if(string && string->str) { my_free(string->str); string->len = 0; } } /******/ /****f* FUNC/simplestring_addn * NAME * simplestring_addn * SYNOPSIS * void simplestring_addn(simplestring* string, const char* add, int add_len) * FUNCTION * copies n characters from source to target string * INPUTS * target - target string * source - source string * add_len - number of characters to copy * RESULT * void * NOTES * SEE ALSO * simplestring_add () * SOURCE */ void simplestring_addn(simplestring* target, const char* source, int add_len) { if(target && source) { if(!target->str) { simplestring_init_str(target); } if(target->len + add_len + 1 > target->size) { /* newsize is current length + new length */ int newsize = target->len + add_len + 1; int incr = target->size * 2; /* align to SIMPLESTRING_INCR increments */ newsize = newsize - (newsize % incr) + incr; target->str = (char*)realloc(target->str, newsize); target->size = target->str ? newsize : 0; } if(target->str) { if(add_len) { memcpy(target->str + target->len, source, add_len); } target->len += add_len; target->str[target->len] = 0; /* null terminate */ } } } /******/ /****f* FUNC/simplestring_add * NAME * simplestring_add * SYNOPSIS * void simplestring_add(simplestring* string, const char* add) * FUNCTION * appends a string of unknown length from source to target * INPUTS * target - the target string to append to * source - the source string of unknown length * RESULT * void * NOTES * SEE ALSO * simplestring_addn () * SOURCE */ void simplestring_add(simplestring* target, const char* source) { if(target && source) { simplestring_addn(target, source, strlen(source)); } } /******/ /*---------------------- * End String Functions * *--------------------**/ Index: php4/ext/xmlrpc/libxmlrpc/simplestring.h +++ php4/ext/xmlrpc/libxmlrpc/simplestring.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef __SIMPLESTRING_H__ #define __SIMPLESTRING_H__ /*-******************************** * begin simplestring header stuff * **********************************/ #ifdef __cplusplus extern "C" { #endif /****s* struct/simplestring * NAME * simplestring * NOTES * represents a string efficiently for fast appending, etc. * SOURCE */ typedef struct _simplestring { char* str; /* string buf */ int len; /* length of string/buf */ int size; /* size of allocated buffer */ } simplestring; /******/ #ifndef NULL #define NULL 0 #endif void simplestring_init(simplestring* string); void simplestring_clear(simplestring* string); void simplestring_free(simplestring* string); void simplestring_addn(simplestring* string, const char* add, int add_len); #ifdef __cplusplus } #endif /*-****************************** * end simplestring header stuff * ********************************/ #endif /* __SIMPLESTRING_H__ */ Index: php4/ext/xmlrpc/libxmlrpc/system_methods.c +++ php4/ext/xmlrpc/libxmlrpc/system_methods.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /****h* ABOUT/system_methods * AUTHOR * Dan Libby, aka danda (dan@libby.com) * HISTORY * 4/28/2001 -- danda -- adding system.multicall and separating out system methods. * TODO * NOTES *******/ #include "queue.h" #include "xmlrpc.h" #include "xmlrpc_private.h" #include "xmlrpc_introspection_private.h" #include "system_methods_private.h" #include <string.h> #include <stdlib.h> #include <stdarg.h> static const char* xsm_introspection_xml = "<?xml version='1.0' ?>" "<introspection version='1.0'>" "<typeList>" "<typeDescription name='system.value' basetype='struct' desc='description of a value'>" "<value type='string' name='name' optional='yes'>value identifier</value>" "<value type='string' name='type'>value's xmlrpc or user-defined type</value>" "<value type='string' name='description'>value's textual description</value> " "<value type='boolean' name='optional'>true if value is optional, else it is required</value> " "<value type='any' name='member' optional='yes'>a child of this element. n/a for scalar types</value> " "</typeDescription>" "<typeDescription name='system.valueList' basetype='array' desc='list of value descriptions'>" "<value type='system.value'/>" "</typeDescription>" "<typeDescription name='system.stringList' basetype='array' desc='list of strings'>" "<value type='string'/>" "</typeDescription>" "</typeList>" "<methodList>" "<!-- system.describeMethods -->" "<methodDescription name='system.describeMethods'>" "<author>Dan Libby</author>" "<purpose>fully describes the methods and types implemented by this XML-RPC server.</purpose>" "<version>1.1</version>" "<signatures>" "<signature>" "<params>" "<value type='array' name='methodList' optional='yes' desc='a list of methods to be described. if omitted, all are described.'>" "<value type='string'>a valid method name</value>" "</value>" "</params>" "<returns>" "<value type='struct' desc='contains methods list and types list'>" "<value type='array' name='methodList' desc='a list of methods'>" "<value type='struct' desc='representation of a single method'>" "<value type='string' name='name'>method name</value>" "<value type='string' name='version' optional='yes'>method version</value>" "<value type='string' name='author' optional='yes'>method author</value>" "<value type='string' name='purpose' optional='yes'>method purpose</value>" "<value type='array' name='signatures' desc='list of method signatures'>" "<value type='struct' desc='representation of a single signature'>" "<value type='system.valueList' name='params' optional='yes'>parameter list</value>" "<value type='system.valueList' name='returns' optional='yes'>return value list</value>" "</value>" "</value>" "<value type='system.stringList' name='bugs' optional='yes'>list of known bugs</value>" "<value type='system.stringList' name='errors' optional='yes'>list of possible errors and error codes</value>" "<value type='system.stringList' name='examples' optional='yes'>list of examples</value>" "<value type='system.stringList' name='history' optional='yes'>list of modifications</value>" "<value type='system.stringList' name='notes' optional='yes'>list of notes</value>" "<value type='system.stringList' name='see' optional='yes'>see also. list of related methods</value>" "<value type='system.stringList' name='todo' optional='yes'>list of unimplemented features</value>" "</value>" "</value>" "<value type='array' name='typeList' desc='a list of type descriptions. Typically used for referencing complex types'>" "<value type='system.value'>a type description</value>" "</value>" "</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.listMethods' />" "<item name='system.methodSignature' />" "<item name='system.methodHelp' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "<!-- system.listMethods -->" "<methodDescription name='system.listMethods'>" "<author>Dan Libby</author>" "<purpose>enumerates the methods implemented by this XML-RPC server.</purpose>" "<version>1.0</version>" "<signatures>" "<signature>" "<returns>" "<value type='array' desc='an array of strings'>" "<value type='string'>name of a method implemented by the server.</value>" "</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.describeMethods' />" "<item name='system.methodSignature' />" "<item name='system.methodHelp' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "<!-- system.methodHelp -->" "<methodDescription name='system.methodHelp'>" "<author>Dan Libby</author>" "<purpose>provides documentation string for a single method</purpose>" "<version>1.0</version>" "<signatures>" "<signature>" "<params>" "<value type='string' name='methodName'>name of the method for which documentation is desired</value>" "</params>" "<returns>" "<value type='string'>help text if defined for the method passed, otherwise an empty string</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.listMethods' />" "<item name='system.methodSignature' />" "<item name='system.methodHelp' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "<!-- system.methodSignature -->" "<methodDescription name='system.methodSignature'>" "<author>Dan Libby</author>" "<purpose>provides 1 or more signatures for a single method</purpose>" "<version>1.0</version>" "<signatures>" "<signature>" "<params>" "<value type='string' name='methodName'>name of the method for which documentation is desired</value>" "</params>" "<returns>" "<value type='array' desc='a list of arrays, each representing a signature'>" "<value type='array' desc='a list of strings. the first element represents the method return value. subsequent elements represent parameters.'>" "<value type='string'>a string indicating the xmlrpc type of a value. one of: string, int, double, base64, datetime, array, struct</value>" "</value>" "</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.listMethods' />" "<item name='system.methodHelp' />" "<item name='system.describeMethods' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "<!-- system.multiCall -->" "<methodDescription name='system.multiCall'>" "<author>Dan Libby</author>" "<purpose>executes multiple methods in sequence and returns the results</purpose>" "<version>1.0</version>" "<signatures>" "<signature>" "<params>" "<value type='array' name='methodList' desc='an array of method call structs'>" "<value type='struct' desc='a struct representing a single method call'>" "<value type='string' name='methodName' desc='name of the method to be executed'/>" "<value type='array' name='params' desc='an array representing the params to a method. sub-elements should match method signature'/>" "</value>" "</value>" "</params>" "<returns>" "<value type='array' desc='an array of method responses'>" "<value type='array' desc='an array containing a single value, which is the method's response'/>" "</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.listMethods' />" "<item name='system.methodHelp' />" "<item name='system.describeMethods' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "<!-- system.getCapabilities -->" "<methodDescription name='system.getCapabilities'>" "<author>Dan Libby</author>" "<purpose>returns a list of capabilities supported by this server</purpose>" "<version>1.0</version>" "<notes><item>spec url: http://groups.yahoo.com/group/xml-rpc/message/2897</item></notes>" "<signatures>" "<signature>" "<returns>" "<value type='struct' desc='list of capabilities, each with a unique key defined by the capability's spec'>" "<value type='struct' desc='definition of a single capability'>" "<value type='string' name='specURL'>www address of the specification defining this capability</value>" "<value type='int' name='specVersion'>version of the spec that this server's implementation conforms to</value>" "</value>" "</value>" "</returns>" "</signature>" "</signatures>" "<see>" "<item name='system.listMethods' />" "<item name='system.methodHelp' />" "<item name='system.describeMethods' />" "</see>" "<example/>" "<error/>" "<note/>" "<bug/>" "<todo/>" "</methodDescription>" "</methodList>" "</introspection>"; /* forward declarations for static (non public, non api) funcs */ static XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); static XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); /*-******************* * System Methods API * *********************/ static void xsm_lazy_doc_methods_cb(XMLRPC_SERVER server, void* userData) { XMLRPC_VALUE xDesc = XMLRPC_IntrospectionCreateDescription(xsm_introspection_xml, NULL); XMLRPC_ServerAddIntrospectionData(server, xDesc); XMLRPC_CleanupValue(xDesc); } void xsm_register(XMLRPC_SERVER server) { xi_register_system_methods(server); XMLRPC_ServerRegisterMethod(server, xsm_token_system_multicall, xsm_system_multicall_cb); XMLRPC_ServerRegisterMethod(server, xsm_token_system_get_capabilities, xsm_system_get_capabilities_cb); /* callback for documentation generation should it be requested */ XMLRPC_ServerRegisterIntrospectionCallback(server, xsm_lazy_doc_methods_cb); } XMLRPC_VALUE xsm_system_multicall_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { XMLRPC_VALUE xArray = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)); XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_array); if (xArray) { XMLRPC_VALUE xMethodIter = XMLRPC_VectorRewind(xArray); while (xMethodIter) { XMLRPC_REQUEST request = XMLRPC_RequestNew(); if(request) { const char* methodName = XMLRPC_VectorGetStringWithID(xMethodIter, "methodName"); XMLRPC_VALUE params = XMLRPC_VectorGetValueWithID(xMethodIter, "params"); if(methodName && params) { XMLRPC_VALUE xRandomArray = XMLRPC_CreateVector(0, xmlrpc_vector_array); XMLRPC_RequestSetMethodName(request, methodName); XMLRPC_RequestSetData(request, params); XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); XMLRPC_AddValueToVector(xRandomArray, XMLRPC_ServerCallMethod(server, request, userData)); XMLRPC_AddValueToVector(xReturn, xRandomArray); } XMLRPC_RequestFree(request, 1); } xMethodIter = XMLRPC_VectorNext(xArray); } } return xReturn; } XMLRPC_VALUE xsm_system_get_capabilities_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { XMLRPC_VALUE xReturn = XMLRPC_CreateVector(0, xmlrpc_vector_struct); XMLRPC_VALUE xFaults = XMLRPC_CreateVector("faults_interop", xmlrpc_vector_struct); XMLRPC_VALUE xIntro = XMLRPC_CreateVector("introspection", xmlrpc_vector_struct); /* support for fault spec */ XMLRPC_VectorAppendString(xFaults, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php", 0); XMLRPC_VectorAppendInt(xFaults, "specVersion", 20010516); /* support for introspection spec */ XMLRPC_VectorAppendString(xIntro, "specURL", "http://xmlrpc-epi.sourceforge.net/specs/rfc.introspection.php", 0); XMLRPC_VectorAppendInt(xIntro, "specVersion", 20010516); XMLRPC_AddValuesToVector(xReturn, xFaults, xIntro, NULL); return xReturn; } /*-*********************** * End System Methods API * *************************/ Index: php4/ext/xmlrpc/libxmlrpc/system_methods_private.h +++ php4/ext/xmlrpc/libxmlrpc/system_methods_private.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Dan Libby, Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* IMPORTANT! * * only non-public things should be in this file. It is fine for any .c file * in xmlrpc/src to include it, but users of the public API should never * include it, and thus *.h files that are part of the public API should * never include it, or they would break if this file is not present. */ #ifndef __SYSTEM_METHODS_PRIVATE_H /* * Avoid include redundancy. */ #define __SYSTEM_METHODS_PRIVATE_H /*---------------------------------------------------------------------------- * system_methods_private.h * * Purpose: * define non-public system.* methods * Comments: * xsm = xmlrpc system methods */ /*---------------------------------------------------------------------------- * Constants */ #define xsm_token_system_multicall "system.multiCall" #define xsm_token_system_get_capabilities "system.getCapabilities" /*---------------------------------------------------------------------------- * Includes */ /*---------------------------------------------------------------------------- * Structures */ /*---------------------------------------------------------------------------- * Globals */ /*---------------------------------------------------------------------------- * Functions */ void xsm_register(XMLRPC_SERVER server); int xsm_is_system_method(XMLRPC_Callback cb); /*---------------------------------------------------------------------------- * Macros */ #endif /* __SYSTEM_METHODS_PRIVATE_H */ Index: php4/ext/xmlrpc/libxmlrpc/xml_element.c +++ php4/ext/xmlrpc/libxmlrpc/xml_element.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ static const char rcsid[] = "#(@) $Id: xml_element.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; /****h* ABOUT/xml_element * NAME * xml_element * AUTHOR * Dan Libby, aka danda (dan@libby.com) * CREATION DATE * 06/2000 * HISTORY * 10/15/2000 -- danda -- adding robodoc documentation * TODO * Nicer external API. Get rid of macros. Make opaque types, etc. * PORTABILITY * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just * about anything with minor mods. * NOTES * This code incorporates ideas from expat-ensor from http://xml.ensor.org. * * It was coded primarily to act as a go-between for expat and xmlrpc. To this * end, it stores xml elements, their sub-elements, and their attributes in an * in-memory tree. When expat is done parsing, the tree can be walked, thus * retrieving the values. The code can also be used to build a tree via API then * write out the tree to a buffer, thus "serializing" the xml. * * It turns out this is useful for other purposes, such as parsing config files. * YMMV. * * Some Features: * - output option for xml escaping data. Choices include no escaping, entity escaping, * or CDATA sections. * - output option for character encoding. Defaults to (none) utf-8. * - output option for verbosity/readability. ultra-compact, newlines, pretty/level indented. * * BUGS * there must be some. ******/ #include <stdlib.h> #include <string.h> #include "xml_element.h" #include "queue.h" #include "expat.h" #include "encodings.h" #define my_free(thing) if(thing) {free(thing); thing = 0;} #define XML_DECL_START "<?xml" #define XML_DECL_START_LEN sizeof(XML_DECL_START) - 1 #define XML_DECL_VERSION "version='1.0'" #define XML_DECL_VERSION_LEN sizeof(XML_DECL_VERSION) - 1 #define XML_DECL_ENCODING_ATTR "encoding" #define XML_DECL_ENCODING_ATTR_LEN sizeof(XML_DECL_ENCODING_ATTR) - 1 #define XML_DECL_ENCODING_DEFAULT "utf-8" #define XML_DECL_ENCODING_DEFAULT_LEN sizeof(XML_DECL_ENCODING_DEFAULT) - 1 #define XML_DECL_END "?>" #define XML_DECL_END_LEN sizeof(XML_DECL_END) - 1 #define START_TOKEN_BEGIN "<" #define START_TOKEN_BEGIN_LEN sizeof(START_TOKEN_BEGIN) - 1 #define START_TOKEN_END ">" #define START_TOKEN_END_LEN sizeof(START_TOKEN_END) - 1 #define EMPTY_START_TOKEN_END "/>" #define EMPTY_START_TOKEN_END_LEN sizeof(EMPTY_START_TOKEN_END) - 1 #define END_TOKEN_BEGIN "</" #define END_TOKEN_BEGIN_LEN sizeof(END_TOKEN_BEGIN) - 1 #define END_TOKEN_END ">" #define END_TOKEN_END_LEN sizeof(END_TOKEN_END) - 1 #define ATTR_DELIMITER "\"" #define ATTR_DELIMITER_LEN sizeof(ATTR_DELIMITER) - 1 #define CDATA_BEGIN "<![CDATA[" #define CDATA_BEGIN_LEN sizeof(CDATA_BEGIN) - 1 #define CDATA_END "]]>" #define CDATA_END_LEN sizeof(CDATA_END) - 1 #define EQUALS "=" #define EQUALS_LEN sizeof(EQUALS) - 1 #define WHITESPACE " " #define WHITESPACE_LEN sizeof(WHITESPACE) - 1 #define NEWLINE "\n" #define NEWLINE_LEN sizeof(NEWLINE) - 1 #define MAX_VAL_BUF 144 #define SCALAR_STR "SCALAR" #define SCALAR_STR_LEN sizeof(SCALAR_STR) - 1 #define VECTOR_STR "VECTOR" #define VECTOR_STR_LEN sizeof(VECTOR_STR) - 1 #define RESPONSE_STR "RESPONSE" #define RESPONSE_STR_LEN sizeof(RESPONSE_STR) - 1 /*----------------------------- - Begin xml_element Functions - -----------------------------*/ /****f* xml_element/xml_elem_free_non_recurse * NAME * xml_elem_free_non_recurse * SYNOPSIS * void xml_elem_free_non_recurse(xml_element* root) * FUNCTION * free a single xml element. child elements will not be freed. * INPUTS * root - the element to free * RESULT * void * NOTES * SEE ALSO * xml_elem_free () * xml_elem_new () * SOURCE */ void xml_elem_free_non_recurse(xml_element* root) { if(root) { xml_element_attr* attrs = Q_Head(&root->attrs); while(attrs) { my_free(attrs->key); my_free(attrs->val); my_free(attrs); attrs = Q_Next(&root->attrs); } Q_Destroy(&root->children); Q_Destroy(&root->attrs); my_free((char*)root->name); simplestring_free(&root->text); my_free(root); } } /******/ /****f* xml_element/xml_elem_free * NAME * xml_elem_free * SYNOPSIS * void xml_elem_free(xml_element* root) * FUNCTION * free an xml element and all of its child elements * INPUTS * root - the root of an xml tree you would like to free * RESULT * void * NOTES * SEE ALSO * xml_elem_free_non_recurse () * xml_elem_new () * SOURCE */ void xml_elem_free(xml_element* root) { if(root) { xml_element* kids = Q_Head(&root->children); while(kids) { xml_elem_free(kids); kids = Q_Next(&root->children); } xml_elem_free_non_recurse(root); } } /******/ /****f* xml_element/xml_elem_new * NAME * xml_elem_new * SYNOPSIS * xml_element* xml_elem_new() * FUNCTION * allocates and initializes a new xml_element * INPUTS * none * RESULT * xml_element* or NULL. NULL indicates an out-of-memory condition. * NOTES * SEE ALSO * xml_elem_free () * xml_elem_free_non_recurse () * SOURCE */ xml_element* xml_elem_new() { xml_element* elem = calloc(1, sizeof(xml_element)); if(elem) { Q_Init(&elem->children); Q_Init(&elem->attrs); simplestring_init(&elem->text); /* init empty string in case we don't find any char data */ simplestring_addn(&elem->text, "", 0); } return elem; } /******/ static int xml_elem_writefunc(int (*fptr)(void *data, const char *text, int size), const char *text, void *data, int len) { return fptr && text ? fptr(data, text, len ? len : strlen(text)) : 0; } static int create_xml_escape(char *pString, unsigned char c) { int counter = 0; pString[counter++] = '&'; pString[counter++] = '#'; if(c >= 100) { pString[counter++] = c / 100 + '0'; c = c % 100; } if(c >= 10) { pString[counter++] = c / 10 + '0'; c = c % 10; } pString[counter++] = c + '0'; pString[counter++] = ';'; return counter; } #define non_ascii(c) (c > 127) #define non_print(c) (!isprint(c)) #define markup(c) (c == '&' || c == '\"' || c == '>' || c == '<') #define entity_length(c) ( (c >= 100) ? 3 : ((c >= 10) ? 2 : 1) ) + 3; /* "&#" + c + ";" */ /* * xml_elem_entity_escape * * Purpose: * escape reserved xml chars and non utf-8 chars as xml entities * Comments: * The return value may be a new string, or null if no * conversion was performed. In the latter case, *newlen will * be 0. * Flags (to escape) * xml_elem_no_escaping = 0x000, * xml_elem_entity_escaping = 0x002, // escape xml special chars as entities * xml_elem_non_ascii_escaping = 0x008, // escape chars above 127 * xml_elem_cdata_escaping = 0x010, // wrap in cdata */ static char* xml_elem_entity_escape(const char* buf, int old_len, int *newlen, XML_ELEM_ESCAPING flags) { char *pRetval = 0; int iNewBufLen=0; #define should_escape(c, flag) ( ((flag & xml_elem_markup_escaping) && markup(c)) || \ ((flag & xml_elem_non_ascii_escaping) && non_ascii(c)) || \ ((flag & xml_elem_non_print_escaping) && non_print(c)) ) if(buf && *buf) { const unsigned char *bufcopy; char *NewBuffer; int ToBeXmlEscaped=0; int iLength; bufcopy = buf; iLength= old_len ? old_len : strlen(buf); while(*bufcopy) { if( should_escape(*bufcopy, flags) ) { /* the length will increase by length of xml escape - the character length */ iLength += entity_length(*bufcopy); ToBeXmlEscaped=1; } bufcopy++; } if(ToBeXmlEscaped) { NewBuffer= malloc(iLength+1); if(NewBuffer) { bufcopy=buf; while(*bufcopy) { if(should_escape(*bufcopy, flags)) { iNewBufLen += create_xml_escape(NewBuffer+iNewBufLen,*bufcopy); } else { NewBuffer[iNewBufLen++]=*bufcopy; } bufcopy++; } NewBuffer[iNewBufLen] = 0; pRetval = NewBuffer; } } } if(newlen) { *newlen = iNewBufLen; } return pRetval; } static void xml_element_serialize(xml_element *el, int (*fptr)(void *data, const char *text, int size), void *data, XML_ELEM_OUTPUT_OPTIONS options, int depth) { int i; static STRUCT_XML_ELEM_OUTPUT_OPTIONS default_opts = {xml_elem_pretty, xml_elem_markup_escaping | xml_elem_non_print_escaping, XML_DECL_ENCODING_DEFAULT}; static char whitespace[] = " " " " " "; depth++; if(!el) { fprintf(stderr, "Nothing to write\n"); return; } if(!options) { options = &default_opts; } /* print xml declaration if at root level */ if(depth == 1) { xml_elem_writefunc(fptr, XML_DECL_START, data, XML_DECL_START_LEN); xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); xml_elem_writefunc(fptr, XML_DECL_VERSION, data, XML_DECL_VERSION_LEN); if(options->encoding && *options->encoding) { xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); xml_elem_writefunc(fptr, XML_DECL_ENCODING_ATTR, data, XML_DECL_ENCODING_ATTR_LEN); xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN); xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); xml_elem_writefunc(fptr, options->encoding, data, 0); xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); } xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); xml_elem_writefunc(fptr, XML_DECL_END, data, XML_DECL_END_LEN); if(options->verbosity != xml_elem_no_white_space) { xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); } } if(options->verbosity == xml_elem_pretty && depth > 2) { xml_elem_writefunc(fptr, whitespace, data, depth - 2); } /* begin element */ xml_elem_writefunc(fptr,START_TOKEN_BEGIN, data, START_TOKEN_BEGIN_LEN); if(el->name) { xml_elem_writefunc(fptr, el->name, data, 0); /* write attrs, if any */ if(Q_Size(&el->attrs)) { xml_element_attr* iter = Q_Head(&el->attrs); while( iter ) { xml_elem_writefunc(fptr, WHITESPACE, data, WHITESPACE_LEN); xml_elem_writefunc(fptr, iter->key, data, 0); xml_elem_writefunc(fptr, EQUALS, data, EQUALS_LEN); xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); xml_elem_writefunc(fptr, iter->val, data, 0); xml_elem_writefunc(fptr, ATTR_DELIMITER, data, ATTR_DELIMITER_LEN); iter = Q_Next(&el->attrs); } } } else { xml_elem_writefunc(fptr, "None", data, 0); } /* if no text and no children, use abbreviated form, eg: <foo/> */ if(!el->text.len && !Q_Size(&el->children)) { xml_elem_writefunc(fptr, EMPTY_START_TOKEN_END, data, EMPTY_START_TOKEN_END_LEN); } /* otherwise, print element contents */ else { xml_elem_writefunc(fptr, START_TOKEN_END, data, START_TOKEN_END_LEN); /* print text, if any */ if(el->text.len) { char* escaped_str = el->text.str; int buflen = el->text.len; if(options->escaping && options->escaping != xml_elem_cdata_escaping) { escaped_str = xml_elem_entity_escape(el->text.str, buflen, &buflen, options->escaping ); if(!escaped_str) { escaped_str = el->text.str; } } if(options->escaping & xml_elem_cdata_escaping) { xml_elem_writefunc(fptr, CDATA_BEGIN, data, CDATA_BEGIN_LEN); } xml_elem_writefunc(fptr, escaped_str, data, buflen); if(escaped_str != el->text.str) { my_free(escaped_str); } if(options->escaping & xml_elem_cdata_escaping) { xml_elem_writefunc(fptr, CDATA_END, data, CDATA_END_LEN); } } /* no text, so print child elems */ else { xml_element *kids = Q_Head(&el->children); i = 0; while( kids ) { if(i++ == 0) { if(options->verbosity != xml_elem_no_white_space) { xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); } } xml_element_serialize(kids, fptr, data, options, depth); kids = Q_Next(&el->children); } if(i) { if(options->verbosity == xml_elem_pretty && depth > 2) { xml_elem_writefunc(fptr, whitespace, data, depth - 2); } } } xml_elem_writefunc(fptr, END_TOKEN_BEGIN, data, END_TOKEN_BEGIN_LEN); xml_elem_writefunc(fptr,el->name ? el->name : "None", data, 0); xml_elem_writefunc(fptr, END_TOKEN_END, data, END_TOKEN_END_LEN); } if(options->verbosity != xml_elem_no_white_space) { xml_elem_writefunc(fptr, NEWLINE, data, NEWLINE_LEN); } } /* print buf to file */ static file_out_fptr(void *f, const char *text, int size) { fputs(text, (FILE *)f); } /* print buf to simplestring */ static simplestring_out_fptr(void *f, const char *text, int size) { simplestring* buf = (simplestring*)f; if(buf) { simplestring_addn(buf, text, size); } } /****f* xml_element/xml_elem_serialize_to_string * NAME * xml_elem_serialize_to_string * SYNOPSIS * void xml_element_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len) * FUNCTION * writes element tree as XML into a newly allocated buffer * INPUTS * el - root element of tree * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS * buf_len - length of returned buffer, if not null. * RESULT * char* or NULL. Must be free'd by caller. * NOTES * SEE ALSO * xml_elem_serialize_to_stream () * xml_elem_parse_buf () * SOURCE */ char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len) { simplestring buf; simplestring_init(&buf); xml_element_serialize(el, simplestring_out_fptr, (void *)&buf, options, 0); if(buf_len) { *buf_len = buf.len; } return buf.str; } /******/ /****f* xml_element/xml_elem_serialize_to_stream * NAME * xml_elem_serialize_to_stream * SYNOPSIS * void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options) * FUNCTION * writes element tree as XML into a stream (typically an opened file) * INPUTS * el - root element of tree * output - stream handle * options - options determining how output is written. see XML_ELEM_OUTPUT_OPTIONS * RESULT * void * NOTES * SEE ALSO * xml_elem_serialize_to_string () * xml_elem_parse_buf () * SOURCE */ void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options) { xml_element_serialize(el, file_out_fptr, (void *)output, options, 0); } /******/ /*--------------------------* * End xml_element Functions * *--------------------------*/ /*---------------------- * Begin Expat Handlers * *---------------------*/ typedef struct _xml_elem_data { xml_element* root; xml_element* current; XML_ELEM_INPUT_OPTIONS input_options; int needs_enc_conversion; } xml_elem_data; /* expat start of element handler */ static void startElement(void *userData, const char *name, const char **attrs) { int i; xml_element *c; xml_elem_data* mydata = (xml_elem_data*)userData; const char** p = attrs; if(mydata) { c = mydata->current; mydata->current = xml_elem_new(); mydata->current->name = (char*)strdup(name); mydata->current->parent = c; /* init attrs */ while(p && *p) { xml_element_attr* attr = malloc(sizeof(xml_element_attr)); if(attr) { attr->key = strdup(*p); attr->val = strdup(*(p+1)); Q_PushTail(&mydata->current->attrs, attr); p += 2; } } } } /* expat end of element handler */ static void endElement(void *userData, const char *name) { xml_elem_data* mydata = (xml_elem_data*)userData; if(mydata && mydata->current && mydata->current->parent) { Q_PushTail(&mydata->current->parent->children, mydata->current); mydata->current = mydata->current->parent; } } /* expat char data handler */ static void charHandler(void *userData, const char *s, int len) { xml_elem_data* mydata = (xml_elem_data*)userData; if(mydata && mydata->current) { /* Check if we need to decode utf-8 parser output to another encoding */ if(mydata->needs_enc_conversion && mydata->input_options->encoding) { char* add_text = utf8_decode(s, len, &len, mydata->input_options->encoding); if(add_text) { simplestring_addn(&mydata->current->text, add_text, len); free(add_text); return; } } simplestring_addn(&mydata->current->text, s, len); } } /******/ /*-------------------* * End Expat Handlers * *-------------------*/ /*-------------------* * xml_elem_parse_buf * *-------------------*/ /****f* xml_element/xml_elem_parse_buf * NAME * xml_elem_parse_buf * SYNOPSIS * xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error) * FUNCTION * parse a buffer containing XML into an xml_element in-memory tree * INPUTS * in_buf - buffer containing XML document * len - length of buffer * options - input options. optional * error - error result data. optional. check if result is null. * RESULT * void * NOTES * The returned data must be free'd by caller * SEE ALSO * xml_elem_serialize_to_string () * xml_elem_free () * SOURCE */ xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error) { xml_element* xReturn = NULL; char buf[100] = ""; static STRUCT_XML_ELEM_INPUT_OPTIONS default_opts = {encoding_utf_8}; if(!options) { options = &default_opts; } if(in_buf) { XML_Parser parser; xml_elem_data mydata = {0}; parser = XML_ParserCreate(NULL); mydata.root = xml_elem_new(); mydata.current = mydata.root; mydata.input_options = options; mydata.needs_enc_conversion = options->encoding && strcmp(options->encoding, encoding_utf_8); XML_SetElementHandler(parser, startElement, endElement); XML_SetCharacterDataHandler(parser, charHandler); /* pass the xml_elem_data struct along */ XML_SetUserData(parser, (void*)&mydata); if(!len) { len = strlen(in_buf); } /* parse the XML */ if(XML_Parse(parser, in_buf, len, 1) == 0) { enum XML_Error err_code = XML_GetErrorCode(parser); int line_num = XML_GetCurrentLineNumber(parser); int col_num = XML_GetCurrentColumnNumber(parser); long byte_idx = XML_GetCurrentByteIndex(parser); int byte_total = XML_GetCurrentByteCount(parser); const char * error_str = XML_ErrorString(err_code); if(byte_idx >= 0) { snprintf(buf, sizeof(buf), "\n\tdata beginning %i before byte index: %s\n", byte_idx > 10 ? 10 : byte_idx, in_buf + (byte_idx > 10 ? byte_idx - 10 : byte_idx)); } fprintf(stderr, "expat reports error code %i\n" "\tdescription: %s\n" "\tline: %i\n" "\tcolumn: %i\n" "\tbyte index: %i\n" "\ttotal bytes: %i\n%s ", err_code, error_str, line_num, col_num, byte_idx, byte_total, buf); /* error condition */ if(error) { error->parser_code = (long)err_code; error->line = line_num; error->column = col_num; error->byte_index = byte_idx; error->parser_error = error_str; } } else { xReturn = (xml_element*)Q_Head(&mydata.root->children); } XML_ParserFree(parser); xml_elem_free_non_recurse(mydata.root); } return xReturn; } /******/ Index: php4/ext/xmlrpc/libxmlrpc/xml_element.h +++ php4/ext/xmlrpc/libxmlrpc/xml_element.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef __XML_ELEMENT_H__ #define __XML_ELEMENT_H__ /* includes */ #include <stdio.h> #include "queue.h" #include "simplestring.h" #include "encodings.h" #ifdef __cplusplus extern "C" { #endif /****d* enum/XML_ELEM_VERBOSITY * NAME * XML_ELEM_VERBOSITY * NOTES * verbosity/readability options for generated xml * SEE ALSO * XML_ELEM_OUTPUT_OPTIONS * SOURCE */ typedef enum _xml_elem_verbosity { xml_elem_no_white_space, /* compact xml with no white space */ xml_elem_newlines_only, /* add newlines for enhanced readability */ xml_elem_pretty /* add newlines and indent accordint to depth */ } XML_ELEM_VERBOSITY; /******/ /****d* enum/XML_ELEM_ESCAPING * NAME * XML_ELEM_ESCAPING * NOTES * xml escaping options for generated xml * SEE ALSO * XML_ELEM_OUTPUT_OPTIONS * SOURCE */ typedef enum _xml_elem_escaping { xml_elem_no_escaping = 0x000, xml_elem_markup_escaping = 0x002, /* entity escape xml special chars */ xml_elem_non_ascii_escaping = 0x008, /* entity escape chars above 127 */ xml_elem_non_print_escaping = 0x010, /* entity escape non print (illegal) chars */ xml_elem_cdata_escaping = 0x020, /* wrap in cdata section */ } XML_ELEM_ESCAPING; /******/ /****s* struct/XML_ELEM_OUTPUT_OPTIONS * NAME * XML_ELEM_OUTPUT_OPTIONS * NOTES * defines various output options * SOURCE */ typedef struct _xml_output_options { XML_ELEM_VERBOSITY verbosity; /* length/verbosity of xml */ XML_ELEM_ESCAPING escaping; /* how to escape special chars */ const char* encoding; /* <?xml encoding="<encoding>" ?> */ } STRUCT_XML_ELEM_OUTPUT_OPTIONS, *XML_ELEM_OUTPUT_OPTIONS; /******/ /****s* struct/XML_ELEM_INPUT_OPTIONS * NAME * XML_ELEM_INPUT_OPTIONS * NOTES * defines various input options * SOURCE */ typedef struct _xml_input_options { ENCODING_ID encoding; /* which encoding to use. */ } STRUCT_XML_ELEM_INPUT_OPTIONS, *XML_ELEM_INPUT_OPTIONS; /******/ /****s* struct/XML_ELEM_ERROR * NAME * XML_ELEM_ERROR * NOTES * defines an xml parser error * SOURCE */ typedef struct _xml_elem_error { int parser_code; const char* parser_error; long line; long column; long byte_index; } STRUCT_XML_ELEM_ERROR, *XML_ELEM_ERROR; /******/ /*-************************ * begin xml element stuff * **************************/ /****s* struct/xml_elem_attr * NAME * xml_elem_attr * NOTES * representation of an xml attribute, foo="bar" * SOURCE */ typedef struct _xml_element_attr { char* key; /* attribute key */ char* val; /* attribute value */ } xml_element_attr; /******/ /****s* struct/xml_elem_attr * NAME * xml_elem_attr * NOTES * representation of an xml element, eg <candidate name="Harry Browne" party="Libertarian"/> * SOURCE */ typedef struct _xml_element { const char* name; /* element identifier */ simplestring text; /* text contained between element begin/end pairs */ struct _xml_element* parent; /* element's parent */ queue attrs; /* attribute list */ queue children; /* child element list */ } xml_element; /******/ void xml_elem_free(xml_element* root); void xml_elem_free_non_recurse(xml_element* root); xml_element* xml_elem_new(); char* xml_elem_serialize_to_string(xml_element *el, XML_ELEM_OUTPUT_OPTIONS options, int *buf_len); void xml_elem_serialize_to_stream(xml_element *el, FILE *output, XML_ELEM_OUTPUT_OPTIONS options); xml_element* xml_elem_parse_buf(const char* in_buf, int len, XML_ELEM_INPUT_OPTIONS options, XML_ELEM_ERROR error); /*-********************** * end xml element stuff * ************************/ /*-********************** * Begin xml_element API * ************************/ /****d* VALUE/XMLRPC_MACROS * NAME * Some Helpful Macros * NOTES * Some macros for making life easier. Should be self-explanatory. * SEE ALSO * XMLRPC_AddValueToVector () * XMLRPC_VectorGetValueWithID_Case () * XMLRPC_VALUE * SOURCE */ #define xml_elem_next_element(el) ((el) ? (xml_element *)Q_Next(&el->children) : NULL) #define xml_elem_head_element(el) ((el) ? (xml_element *)Q_Head(&el->children) : NULL) #define xml_elem_next_attr(el) ((el) ? (xml_element_attr *)Q_Next(&el->attrs) : NULL) #define xml_elem_head_attr(el) ((el) ? (xml_element_attr *)Q_Head(&el->attrs) : NULL) #define xml_elem_get_name(el) (char *)((el) ? el->name : NULL) #define xml_elem_get_val(el) (char *)((el) ? el->text.str : NULL) /******/ /*-******************** * End xml_element API * **********************/ #ifdef __cplusplus } #endif #endif /* __XML_ELEMENT_H__ */ Index: php4/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c +++ php4/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #include <string.h> #include <stdlib.h> #include "xml_to_dandarpc.h" #include "base64.h" /* list of tokens used in vocab */ #define ELEM_METHODCALL "methodCall" #define ELEM_METHODNAME "methodName" #define ELEM_METHODRESPONSE "methodResponse" #define ELEM_ROOT "simpleRPC" #define ATTR_ARRAY "array" #define ATTR_BASE64 "base64" #define ATTR_BOOLEAN "boolean" #define ATTR_DATETIME "dateTime.iso8601" #define ATTR_DOUBLE "double" #define ATTR_ID "id" #define ATTR_INT "int" #define ATTR_MIXED "mixed" #define ATTR_SCALAR "scalar" #define ATTR_STRING "string" #define ATTR_STRUCT "struct" #define ATTR_TYPE "type" #define ATTR_VECTOR "vector" #define ATTR_VERSION "version" #define VAL_VERSION_0_9 "0.9" XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE xCurrent, xml_element* el) { if(!xCurrent) { xCurrent = XMLRPC_CreateValueEmpty(); } if(el->name) { const char* id = NULL; const char* type = NULL; xml_element_attr* attr_iter = Q_Head(&el->attrs); while(attr_iter) { if(!strcmp(attr_iter->key, ATTR_ID)) { id = attr_iter->val; } if(!strcmp(attr_iter->key, ATTR_TYPE)) { type = attr_iter->val; } attr_iter = Q_Next(&el->attrs); } if(id) { XMLRPC_SetValueID_Case(xCurrent, id, 0, xmlrpc_case_exact); } if(!strcmp(el->name, ATTR_SCALAR)) { if(!type || !strcmp(type, ATTR_STRING)) { XMLRPC_SetValueString(xCurrent, el->text.str, el->text.len); } else if(!strcmp(type, ATTR_INT)) { XMLRPC_SetValueInt(xCurrent, atoi(el->text.str)); } else if(!strcmp(type, ATTR_BOOLEAN)) { XMLRPC_SetValueBoolean(xCurrent, atoi(el->text.str)); } else if(!strcmp(type, ATTR_DOUBLE)) { XMLRPC_SetValueDouble(xCurrent, atof(el->text.str)); } else if(!strcmp(type, ATTR_DATETIME)) { XMLRPC_SetValueDateTime_ISO8601(xCurrent, el->text.str); } else if(!strcmp(type, ATTR_BASE64)) { struct buffer_st buf; base64_decode(&buf, el->text.str, el->text.len); XMLRPC_SetValueBase64(xCurrent, buf.data, buf.offset); buffer_delete(&buf); } } else if(!strcmp(el->name, ATTR_VECTOR)) { xml_element* iter = (xml_element*)Q_Head(&el->children); if(!type || !strcmp(type, ATTR_MIXED)) { XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); } else if(!strcmp(type, ATTR_ARRAY)) { XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_mixed); } else if(!strcmp(type, ATTR_STRUCT)) { XMLRPC_SetIsVector(xCurrent, xmlrpc_vector_struct); } while( iter ) { XMLRPC_VALUE xNext = XMLRPC_CreateValueEmpty(); xml_element_to_DANDARPC_REQUEST_worker(request, xNext, iter); XMLRPC_AddValueToVector(xCurrent, xNext); iter = (xml_element*)Q_Next(&el->children); } } else { xml_element* iter = (xml_element*)Q_Head(&el->children); while( iter ) { xml_element_to_DANDARPC_REQUEST_worker(request, xCurrent, iter); iter = (xml_element*)Q_Next(&el->children); } if(!strcmp(el->name, ELEM_METHODCALL)) { if(request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); } } else if(!strcmp(el->name, ELEM_METHODRESPONSE)) { if(request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); } } else if(!strcmp(el->name, ELEM_METHODNAME)) { if(request) { XMLRPC_RequestSetMethodName(request, el->text.str); } } } } return xCurrent; } XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el) { return xml_element_to_DANDARPC_REQUEST_worker(NULL, NULL, el); } XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el) { if(request) { return XMLRPC_RequestSetData(request, xml_element_to_DANDARPC_REQUEST_worker(request, NULL, el)); } return NULL; } xml_element* DANDARPC_to_xml_element_worker(XMLRPC_REQUEST request, XMLRPC_VALUE node) { #define BUF_SIZE 512 xml_element* root = NULL; if(node) { char buf[BUF_SIZE]; const char* id = XMLRPC_GetValueID(node); XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node); XMLRPC_REQUEST_OUTPUT_OPTIONS output = XMLRPC_RequestGetOutputOptions(request); int bNoAddType = (type == xmlrpc_string && request && output && output->xml_elem_opts.verbosity == xml_elem_no_white_space); xml_element* elem_val = xml_elem_new(); const char* pAttrType = NULL; xml_element_attr* attr_type = bNoAddType ? NULL : malloc(sizeof(xml_element_attr)); if(attr_type) { attr_type->key = strdup(ATTR_TYPE); attr_type->val = 0; Q_PushTail(&elem_val->attrs, attr_type); } elem_val->name = (type == xmlrpc_vector) ? strdup(ATTR_VECTOR) : strdup(ATTR_SCALAR); if(id && *id) { xml_element_attr* attr_id = malloc(sizeof(xml_element_attr)); if(attr_id) { attr_id->key = strdup(ATTR_ID); attr_id->val = strdup(id); Q_PushTail(&elem_val->attrs, attr_id); } } switch(type) { case xmlrpc_string: pAttrType = ATTR_STRING; simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); break; case xmlrpc_int: pAttrType = ATTR_INT; snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_boolean: pAttrType = ATTR_BOOLEAN; snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_double: pAttrType = ATTR_DOUBLE; snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_datetime: pAttrType = ATTR_DATETIME; simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node)); break; case xmlrpc_base64: { struct buffer_st buf; pAttrType = ATTR_BASE64; base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); simplestring_addn(&elem_val->text, buf.data, buf.offset ); buffer_delete(&buf); } break; case xmlrpc_vector: { XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node); XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); switch(my_type) { case xmlrpc_vector_array: pAttrType = ATTR_ARRAY; break; case xmlrpc_vector_mixed: pAttrType = ATTR_MIXED; break; case xmlrpc_vector_struct: pAttrType = ATTR_STRUCT; break; default: break; } /* recurse through sub-elements */ while( xIter ) { xml_element* next_el = DANDARPC_to_xml_element_worker(request, xIter); if(next_el) { Q_PushTail(&elem_val->children, next_el); } xIter = XMLRPC_VectorNext(node); } } break; default: break; } if(pAttrType && attr_type && !bNoAddType) { attr_type->val = strdup(pAttrType); } root = elem_val; } return root; } xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node) { return DANDARPC_to_xml_element_worker(NULL, node); } xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { xml_element* wrapper = NULL; xml_element* root = NULL; if(request) { XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request); const char* pStr = NULL; xml_element_attr* version = malloc(sizeof(xml_element_attr)); version->key = strdup(ATTR_VERSION); version->val = strdup(VAL_VERSION_0_9); wrapper = xml_elem_new(); if(request_type == xmlrpc_request_response) { pStr = ELEM_METHODRESPONSE; } else if(request_type == xmlrpc_request_call) { pStr = ELEM_METHODCALL; } if(pStr) { wrapper->name = strdup(pStr); } root = xml_elem_new(); root->name = strdup(ELEM_ROOT); Q_PushTail(&root->attrs, version); Q_PushTail(&root->children, wrapper); pStr = XMLRPC_RequestGetMethodName(request); if(pStr) { xml_element* method = xml_elem_new(); method->name = strdup(ELEM_METHODNAME); simplestring_add(&method->text, pStr); Q_PushTail(&wrapper->children, method); } Q_PushTail(&wrapper->children, DANDARPC_to_xml_element_worker(request, XMLRPC_RequestGetData(request))); } return root; } Index: php4/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.h +++ php4/ext/xmlrpc/libxmlrpc/xml_to_dandarpc.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef XML_TO_DANDARPC_H #define XML_TO_DANDARPC_H #include "time.h" #include "xmlrpc.h" XMLRPC_VALUE xml_element_to_DANDARPC_VALUE(xml_element* el); XMLRPC_VALUE xml_element_to_DANDARPC_REQUEST(XMLRPC_REQUEST request, xml_element* el); xml_element* DANDARPC_VALUE_to_xml_element(XMLRPC_VALUE node); xml_element* DANDARPC_REQUEST_to_xml_element(XMLRPC_REQUEST request); #endif /* XML_TO_DANDARPC_H */ Index: php4/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c +++ php4/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ static const char rcsid[] = "#(@) $Id: xml_to_xmlrpc.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; #include <string.h> #include <stdlib.h> #include "xml_to_xmlrpc.h" #include "base64.h" /* list of tokens used in vocab */ #define ELEM_ARRAY "array" #define ELEM_BASE64 "base64" #define ELEM_BOOLEAN "boolean" #define ELEM_DATA "data" #define ELEM_DATETIME "dateTime.iso8601" #define ELEM_DOUBLE "double" #define ELEM_FAULT "fault" #define ELEM_FAULTCODE "faultCode" #define ELEM_FAULTSTRING "faultString" #define ELEM_I4 "i4" #define ELEM_INT "int" #define ELEM_MEMBER "member" #define ELEM_METHODCALL "methodCall" #define ELEM_METHODNAME "methodName" #define ELEM_METHODRESPONSE "methodResponse" #define ELEM_NAME "name" #define ELEM_PARAM "param" #define ELEM_PARAMS "params" #define ELEM_STRING "string" #define ELEM_STRUCT "struct" #define ELEM_VALUE "value" XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST_worker(XMLRPC_REQUEST request, XMLRPC_VALUE parent_vector, XMLRPC_VALUE current_val, xml_element* el) { if (!current_val) { /* This should only be the case for the first element */ current_val = XMLRPC_CreateValueEmpty(); } if (el->name) { if (!strcmp(el->name, ELEM_DATA) /* should be ELEM_ARRAY, but there is an extra level. weird */ || ((!strcmp(el->name, ELEM_PARAMS)) && (XMLRPC_RequestGetRequestType(request) == xmlrpc_request_call)) /* this "PARAMS" concept is silly. dave?! */ || !strcmp(el->name, ELEM_FAULT)) { /* so is this "FAULT" nonsense. */ xml_element* iter = (xml_element*)Q_Head(&el->children); XMLRPC_SetIsVector(current_val, xmlrpc_vector_array); while (iter) { XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty(); xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter); XMLRPC_AddValueToVector(current_val, xNextVal); iter = (xml_element*)Q_Next(&el->children); } } else if (!strcmp(el->name, ELEM_STRUCT)) { xml_element* iter = (xml_element*)Q_Head(&el->children); XMLRPC_SetIsVector(current_val, xmlrpc_vector_struct); while ( iter ) { XMLRPC_VALUE xNextVal = XMLRPC_CreateValueEmpty(); xml_element_to_XMLRPC_REQUEST_worker(request, current_val, xNextVal, iter); XMLRPC_AddValueToVector(current_val, xNextVal); iter = (xml_element*)Q_Next(&el->children); } } else if (!strcmp(el->name, ELEM_STRING) || (!strcmp(el->name, ELEM_VALUE) && Q_Size(&el->children) == 0)) { XMLRPC_SetValueString(current_val, el->text.str, el->text.len); } else if (!strcmp(el->name, ELEM_NAME)) { XMLRPC_SetValueID_Case(current_val, el->text.str, 0, xmlrpc_case_exact); } else if (!strcmp(el->name, ELEM_INT) || !strcmp(el->name, ELEM_I4)) { XMLRPC_SetValueInt(current_val, atoi(el->text.str)); } else if (!strcmp(el->name, ELEM_BOOLEAN)) { XMLRPC_SetValueBoolean(current_val, atoi(el->text.str)); } else if (!strcmp(el->name, ELEM_DOUBLE)) { XMLRPC_SetValueDouble(current_val, atof(el->text.str)); } else if (!strcmp(el->name, ELEM_DATETIME)) { XMLRPC_SetValueDateTime_ISO8601(current_val, el->text.str); } else if (!strcmp(el->name, ELEM_BASE64)) { struct buffer_st buf; base64_decode(&buf, el->text.str, el->text.len); XMLRPC_SetValueBase64(current_val, buf.data, buf.offset); buffer_delete(&buf); } else { xml_element* iter; if (!strcmp(el->name, ELEM_METHODCALL)) { if (request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_call); } } else if (!strcmp(el->name, ELEM_METHODRESPONSE)) { if (request) { XMLRPC_RequestSetRequestType(request, xmlrpc_request_response); } } else if (!strcmp(el->name, ELEM_METHODNAME)) { if (request) { XMLRPC_RequestSetMethodName(request, el->text.str); } } iter = (xml_element*)Q_Head(&el->children); while ( iter ) { xml_element_to_XMLRPC_REQUEST_worker(request, parent_vector, current_val, iter); iter = (xml_element*)Q_Next(&el->children); } } } return current_val; } XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el) { return xml_element_to_XMLRPC_REQUEST_worker(NULL, NULL, NULL, el); } XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el) { if (request) { return XMLRPC_RequestSetData(request, xml_element_to_XMLRPC_REQUEST_worker(request, NULL, NULL, el)); } return NULL; } xml_element* XMLRPC_to_xml_element_worker(XMLRPC_VALUE current_vector, XMLRPC_VALUE node, XMLRPC_REQUEST_TYPE request_type, int depth) { #define BUF_SIZE 512 xml_element* root = NULL; if (node) { char buf[BUF_SIZE]; XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(node); XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(node); xml_element* elem_val = xml_elem_new(); /* special case for when root element is not an array */ if (depth == 0 && !(type == xmlrpc_vector && vtype == xmlrpc_vector_array && request_type == xmlrpc_request_call) ) { int bIsFault = (vtype == xmlrpc_vector_struct && XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)); xml_element* next_el = XMLRPC_to_xml_element_worker(NULL, node, request_type, depth + 1); if (next_el) { Q_PushTail(&elem_val->children, next_el); } elem_val->name = strdup(bIsFault ? ELEM_FAULT : ELEM_PARAMS); } else { switch (type) { case xmlrpc_string: elem_val->name = strdup(ELEM_STRING); simplestring_addn(&elem_val->text, XMLRPC_GetValueString(node), XMLRPC_GetValueStringLen(node)); break; case xmlrpc_int: elem_val->name = strdup(ELEM_INT); snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueInt(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_boolean: elem_val->name = strdup(ELEM_BOOLEAN); snprintf(buf, BUF_SIZE, "%i", XMLRPC_GetValueBoolean(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_double: elem_val->name = strdup(ELEM_DOUBLE); snprintf(buf, BUF_SIZE, "%f", XMLRPC_GetValueDouble(node)); simplestring_add(&elem_val->text, buf); break; case xmlrpc_datetime: elem_val->name = strdup(ELEM_DATETIME); simplestring_add(&elem_val->text, XMLRPC_GetValueDateTime_ISO8601(node)); break; case xmlrpc_base64: { struct buffer_st buf; elem_val->name = strdup(ELEM_BASE64); base64_encode(&buf, XMLRPC_GetValueBase64(node), XMLRPC_GetValueStringLen(node)); simplestring_addn(&elem_val->text, buf.data, buf.offset ); buffer_delete(&buf); } break; case xmlrpc_vector: { XMLRPC_VECTOR_TYPE my_type = XMLRPC_GetVectorType(node); XMLRPC_VALUE xIter = XMLRPC_VectorRewind(node); xml_element* root_vector_elem = elem_val; switch (my_type) { case xmlrpc_vector_array: { if(depth == 0) { elem_val->name = strdup(ELEM_PARAMS); } else { /* Hi my name is Dave and I like to make things as confusing * as possible, thus I will throw in this 'data' element * where it absolutely does not belong just so that people * cannot code arrays and structs in a similar and straight * forward manner. Have a good day. * * GRRRRRRRRR! */ xml_element* data = xml_elem_new(); data->name = strdup(ELEM_DATA); elem_val->name = strdup(ELEM_ARRAY); Q_PushTail(&elem_val->children, data); root_vector_elem = data; } } break; case xmlrpc_vector_mixed: /* not officially supported */ case xmlrpc_vector_struct: elem_val->name = strdup(ELEM_STRUCT); break; default: break; } /* recurse through sub-elements */ while ( xIter ) { xml_element* next_el = XMLRPC_to_xml_element_worker(node, xIter, request_type, depth + 1); if (next_el) { Q_PushTail(&root_vector_elem->children, next_el); } xIter = XMLRPC_VectorNext(node); } } break; default: break; } } { XMLRPC_VECTOR_TYPE vtype = XMLRPC_GetVectorType(current_vector); if (depth == 1) { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); /* yet another hack for the "fault" crap */ if (XMLRPC_VectorGetValueWithID(node, ELEM_FAULTCODE)) { root = value; } else { xml_element* param = xml_elem_new(); param->name = strdup(ELEM_PARAM); Q_PushTail(¶m->children, value); root = param; } Q_PushTail(&value->children, elem_val); } else if (vtype == xmlrpc_vector_struct || vtype == xmlrpc_vector_mixed) { xml_element* member = xml_elem_new(); xml_element* name = xml_elem_new(); xml_element* value = xml_elem_new(); member->name = strdup(ELEM_MEMBER); name->name = strdup(ELEM_NAME); value->name = strdup(ELEM_VALUE); simplestring_add(&name->text, XMLRPC_GetValueID(node)); Q_PushTail(&member->children, name); Q_PushTail(&member->children, value); Q_PushTail(&value->children, elem_val); root = member; } else if (vtype == xmlrpc_vector_array) { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); Q_PushTail(&value->children, elem_val); root = value; } else if (vtype == xmlrpc_vector_none) { /* no parent. non-op */ root = elem_val; } else { xml_element* value = xml_elem_new(); value->name = strdup(ELEM_VALUE); Q_PushTail(&value->children, elem_val); root = value; } } } return root; } xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node) { return XMLRPC_to_xml_element_worker(NULL, node, xmlrpc_request_none, 0); } xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request) { xml_element* wrapper = NULL; if (request) { const char* pStr = NULL; XMLRPC_REQUEST_TYPE request_type = XMLRPC_RequestGetRequestType(request); XMLRPC_VALUE xParams = XMLRPC_RequestGetData(request); wrapper = xml_elem_new(); if (request_type == xmlrpc_request_call) { pStr = ELEM_METHODCALL; } else if (request_type == xmlrpc_request_response) { pStr = ELEM_METHODRESPONSE; } if (pStr) { wrapper->name = strdup(pStr); } pStr = XMLRPC_RequestGetMethodName(request); if (pStr) { xml_element* method = xml_elem_new(); method->name = strdup(ELEM_METHODNAME); simplestring_add(&method->text, pStr); Q_PushTail(&wrapper->children, method); } if (xParams) { Q_PushTail(&wrapper->children, XMLRPC_to_xml_element_worker(NULL, XMLRPC_RequestGetData(request), XMLRPC_RequestGetRequestType(request), 0)); } else { /* Despite the spec, the xml-rpc list folk want me to send an empty params element */ xml_element* params = xml_elem_new(); params->name = strdup(ELEM_PARAMS); Q_PushTail(&wrapper->children, params); } } return wrapper; } Index: php4/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.h +++ php4/ext/xmlrpc/libxmlrpc/xml_to_xmlrpc.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef XML_TO_XMLRPC_H #define XML_TO_XMLRPC_H #include "time.h" #include "xmlrpc.h" XMLRPC_VALUE xml_element_to_XMLRPC_VALUE(xml_element* el); XMLRPC_VALUE xml_element_to_XMLRPC_REQUEST(XMLRPC_REQUEST request, xml_element* el); xml_element* XMLRPC_VALUE_to_xml_element(XMLRPC_VALUE node); xml_element* XMLRPC_REQUEST_to_xml_element(XMLRPC_REQUEST request); #endif /* XML_TO_XMLRPC_H */ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc.c +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ static const char rcsid[] = "#(@) $Id: xmlrpc.c,v 1.1 2001/09/06 04:13:29 danda Exp $"; /****h* ABOUT/xmlrpc * NAME * XMLRPC_VALUE * AUTHOR * Dan Libby, aka danda (dan@libby.com) * CREATION DATE * 9/1999 - 10/2000 * HISTORY * 09/1999 -- danda -- Initial API, before I even knew of standard XMLRPC vocab. Response only. * 06/2000 -- danda -- played with expat-ensor from www.ensor.org. Cool, but some flaws. * 07/2000 -- danda -- wrote new implementation to be compatible with xmlrpc standard and * incorporated some ideas from ensor, most notably the separation of * xml dom from xmlrpc api. * 08/2000 -- danda -- support for two vocabularies: danda-rpc and xml-rpc * 08/2000 -- danda -- PHP C extension that uses XMLRPC * 10/15/2000 -- danda -- adding robodoc documentation * TODO * Server method introspection. (Enumerate available methods, describe I/O) * PORTABILITY * Coded on RedHat Linux 6.2. Builds on Solaris x86. Should build on just * about anything with minor mods. * NOTES * Welcome to XMLRPC. For more info on the specification and history, see * http://www.xmlrpc.org. * * This code aims to be a full-featured C implementation of XMLRPC. It does not * have any networking code. Rather, it is intended to be plugged into apps * or libraries with existing networking facilities, eg PHP, apache, perl, mozilla, * home-brew application servers, etc. * * Usage Paradigm: * The user of this library will typically be implementing either an XMLRPC server, * an XMLRPC client, or both. The client will use the library to build an in-memory * representation of a request, and then serialize (encode) that request into XML. The * client will then send the XML to the server via external mechanism. The server will * de-serialize the XML back into an binary representation, call the appropriate registered * method -- thereby generating a response. The response will be serialized into XML and * sent back to the client. The client will de-serialize it into memory, and can * iterate through the results via API. * * Both the request and the response may consist of arbitrarily long, arbitrarily nested * values. The values may be one of several types, as defined by XMLRPC_VALUE_TYPE. * * Features and Architecture: * - The XML parsing (xml_element.c) is completely independent of the XMLRPC api. In fact, * it can be used as a standalone dom implementation. * - Because of this, the same XMLRPC data can be serialized into multiple xml vocabularies. * It is simply a matter of writing a transport. So far, two transports have been defined. * The default xmlrpc vocab (xml_to_xmlrpc.c), and simple-rpc (xml_to_dandarpc.c) which is * proprietary, but imho more readable, and nice for proprietary legacy reasons. * - Various output options, including: xml escaping via CDATA or entity, case folding, * vocab version, and character encoding. * - One to One mapping between C structures and actual values, unlike ensor which forces * one to understand the arcana of the xmlrpc vocab. * - support for mixed indexed/keyed vector types, making it more compatible with * languages such as PHP. * - quite speedy compared to implementations written in interpreted languages. Also, uses * intelligent string handling, so not many strlen() calls, etc. * - comprehensive API for manipulation of values *******/ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <time.h> #include "queue.h" #include "xmlrpc.h" #include "expat.h" #include "base64.h" #include "xml_to_xmlrpc.h" #include "xml_to_dandarpc.h" #include "xml_element.h" #include "xmlrpc_private.h" #include "xmlrpc_introspection_private.h" /*-********************* * Begin Time Functions * ***********************/ static int date_from_ISO8601(const char *text, time_t *value) { struct tm tm; int n; int i; time_t t; tm.tm_isdst = -1; if(strlen(text) < 17) { return -1; } n = 1000; tm.tm_year = 0; for(i = 0; i < 4; i++) { tm.tm_year += (text[i]-'0')*n; n /= 10; } n = 10; tm.tm_mon = 0; for(i = 0; i < 2; i++) { tm.tm_mon += (text[i+4]-'0')*n; n /= 10; } tm.tm_mon --; n = 10; tm.tm_mday = 0; for(i = 0; i < 2; i++) { tm.tm_mday += (text[i+6]-'0')*n; n /= 10; } n = 10; tm.tm_hour = 0; for(i = 0; i < 2; i++) { tm.tm_hour += (text[i+9]-'0')*n; n /= 10; } n = 10; tm.tm_min = 0; for(i = 0; i < 2; i++) { tm.tm_min += (text[i+12]-'0')*n; n /= 10; } n = 10; tm.tm_sec = 0; for(i = 0; i < 2; i++) { tm.tm_sec += (text[i+15]-'0')*n; n /= 10; } tm.tm_year -= 1900; *value = mktime(&tm); return 0; } static int date_to_ISO8601(time_t value, char *buf, int length) { struct tm *tm; tm = localtime(&value); return strftime(buf, length, "%Y%m%dT%H:%M:%S", tm); } /*-******************* * End Time Functions * *********************/ /*-*************************** * Begin XMLRPC_REQUEST funcs * *****************************/ /****f* REQUEST/XMLRPC_RequestNew * NAME * XMLRPC_RequestNew * SYNOPSIS * XMLRPC_REQUEST XMLRPC_RequestNew() * FUNCTION * Creates a new XMLRPC_Request data struct * INPUTS * none * SEE ALSO * XMLRPC_RequestFree () * SOURCE */ XMLRPC_REQUEST XMLRPC_RequestNew() { XMLRPC_REQUEST xRequest = calloc(1, sizeof(STRUCT_XMLRPC_REQUEST)); if(xRequest) { simplestring_init(&xRequest->methodName); } return xRequest; } /*******/ /****f* REQUEST/XMLRPC_RequestFree * NAME * XMLRPC_RequestFree * SYNOPSIS * void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) * FUNCTION * Free XMLRPC Request and all sub-values * INPUTS * request -- previously allocated request struct * bFreeIO -- 1 = also free request value data, if any, 0 = ignore. * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_CleanupValue () * SOURCE */ void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO) { if(request) { simplestring_free(&request->methodName); if(request->io && bFreeIO) { XMLRPC_CleanupValue(request->io); } if(request->error) { XMLRPC_CleanupValue(request->error); } my_free(request); } } /*******/ /* Set Method Name to call */ /****f* REQUEST/XMLRPC_RequestSetMethodName * NAME * XMLRPC_RequestSetMethodName * SYNOPSIS * const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName) * FUNCTION * Set name of method to call with this request. * INPUTS * request -- previously allocated request struct * methodName -- name of method * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestGetMethodName () * XMLRPC_RequestFree () * SOURCE */ const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName) { if(request) { simplestring_clear(&request->methodName); simplestring_add(&request->methodName, methodName); return request->methodName.str; } return NULL; } /*******/ /****f* REQUEST/XMLRPC_RequestGetMethodName * NAME * XMLRPC_RequestGetMethodName * SYNOPSIS * const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) * FUNCTION * Get name of method called by this request * INPUTS * request -- previously allocated request struct * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestSetMethodName () * XMLRPC_RequestFree () * SOURCE */ const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request) { return request ? request->methodName.str : NULL; } /*******/ /****f* REQUEST/XMLRPC_RequestSetRequestType * NAME * XMLRPC_RequestSetRequestType * SYNOPSIS * XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) * FUNCTION * A request struct may be allocated by a caller or by xmlrpc * in response to a request. This allows setting the * request type. * INPUTS * request -- previously allocated request struct * type -- request type [xmlrpc_method_call | xmlrpc_method_response] * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestGetRequestType () * XMLRPC_RequestFree () * XMLRPC_REQUEST_TYPE * SOURCE */ XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type) { if(request) { request->request_type = type; return request->request_type; } return xmlrpc_request_none; } /*******/ /****f* REQUEST/XMLRPC_RequestGetRequestType * NAME * XMLRPC_RequestGetRequestType * SYNOPSIS * XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) * FUNCTION * A request struct may be allocated by a caller or by xmlrpc * in response to a request. This allows setting the * request type. * INPUTS * request -- previously allocated request struct * RESULT * type -- request type [xmlrpc_method_call | xmlrpc_method_response] * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestSetRequestType () * XMLRPC_RequestFree () * XMLRPC_REQUEST_TYPE * SOURCE */ XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request) { return request ? request->request_type : xmlrpc_request_none; } /*******/ /****f* REQUEST/XMLRPC_RequestSetData * NAME * XMLRPC_RequestSetData * SYNOPSIS * XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) * FUNCTION * Associates a block of xmlrpc data with the request. The * data is *not* copied. A pointer is kept. The caller * should be careful not to doubly free the data value, * which may optionally be free'd by XMLRPC_RequestFree(). * INPUTS * request -- previously allocated request struct * data -- previously allocated data struct * RESULT * XMLRPC_VALUE -- pointer to value stored, or NULL * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestGetData () * XMLRPC_RequestFree () * XMLRPC_REQUEST * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data) { if(request && data) { request->io = XMLRPC_CopyValue(data); return request->io; } return NULL; } /*******/ /****f* REQUEST/XMLRPC_RequestGetData * NAME * XMLRPC_RequestGetData * SYNOPSIS * XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) * FUNCTION * Returns data associated with request, if any. * INPUTS * request -- previously allocated request struct * RESULT * XMLRPC_VALUE -- pointer to value stored, or NULL * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestSetData () * XMLRPC_RequestFree () * XMLRPC_REQUEST * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request) { return request ? request->io : NULL; } /*******/ /****f* REQUEST/XMLRPC_RequestSetOutputOptions * NAME * XMLRPC_RequestSetOutputOptions * SYNOPSIS * XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output) * FUNCTION * Sets output options used for generating XML. The output struct * is copied, and may be freed by the caller. * INPUTS * request -- previously allocated request struct * output -- output options struct initialized by caller * RESULT * XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to value stored, or NULL * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestGetOutputOptions () * XMLRPC_RequestFree () * XMLRPC_REQUEST * XMLRPC_REQUEST_OUTPUT_OPTIONS * SOURCE */ XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output) { if(request && output) { memcpy(&request->output, output, sizeof(STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS)); return &request->output; } return NULL; } /*******/ /****f* REQUEST/XMLRPC_RequestGetOutputOptions * NAME * XMLRPC_RequestGetOutputOptions * SYNOPSIS * XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) * FUNCTION * Gets a pointer to output options used for generating XML. * INPUTS * request -- previously allocated request struct * RESULT * XMLRPC_REQUEST_OUTPUT_OPTIONS -- pointer to options stored, or NULL * SEE ALSO * XMLRPC_RequestNew () * XMLRPC_RequestSetOutputOptions () * XMLRPC_RequestFree () * XMLRPC_REQUEST * XMLRPC_REQUEST_OUTPUT_OPTIONS * SOURCE */ XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request) { return request ? &request->output : NULL; } /*******/ /*-************************* * End XMLRPC_REQUEST funcs * ***************************/ /*-*************************** * Begin Serializiation funcs * *****************************/ /****f* SERIALIZE/XMLRPC_VALUE_ToXML * NAME * XMLRPC_VALUE_ToXML * SYNOPSIS * char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val) * FUNCTION * encode XMLRPC_VALUE into XML buffer. Note that the generated * buffer will not contain a methodCall. * INPUTS * val -- previously allocated XMLRPC_VALUE * buf_len -- length of returned buffer, if not null * RESULT * char* -- newly allocated buffer containing XML. * It is the caller's responsibility to free it. * SEE ALSO * XMLRPC_REQUEST_ToXML () * XMLRPC_VALUE_FromXML () * XMLRPC_Free () * XMLRPC_VALUE * SOURCE */ char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len) { xml_element *root_elem = XMLRPC_VALUE_to_xml_element(val); char* pRet = NULL; if(root_elem) { pRet = xml_elem_serialize_to_string(root_elem, NULL, buf_len); xml_elem_free(root_elem); } return pRet; } /*******/ /****f* SERIALIZE/XMLRPC_REQUEST_ToXML * NAME * XMLRPC_REQUEST_ToXML * SYNOPSIS * char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request) * FUNCTION * encode XMLRPC_REQUEST into XML buffer * INPUTS * request -- previously allocated XMLRPC_REQUEST * buf_len -- size of returned buf, if not null * RESULT * char* -- newly allocated buffer containing XML. * It is the caller's responsibility to free it. * SEE ALSO * XMLRPC_REQUEST_ToXML () * XMLRPC_REQUEST_FromXML () * XMLRPC_Free () * XMLRPC_VALUE_ToXML () * XMLRPC_REQUEST * SOURCE */ char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int* buf_len) { if(request) { xml_element *root_elem = (request->output.version == xmlrpc_version_simple) ? DANDARPC_REQUEST_to_xml_element(request) : XMLRPC_REQUEST_to_xml_element(request); char* pRet = NULL; if(root_elem) { pRet = xml_elem_serialize_to_string(root_elem, &request->output.xml_elem_opts, buf_len); xml_elem_free(root_elem); } return pRet; } } /*******/ /****f* SERIALIZE/XMLRPC_VALUE_FromXML * NAME * XMLRPC_VALUE_FromXML * SYNOPSIS * XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int le * FUNCTION * Retrieve XMLRPC_VALUE from XML buffer. Note that this will * ignore any methodCall. See XMLRPC_REQUEST_FromXML * INPUTS * in_buf -- character buffer containing XML * len -- length of buffer * RESULT * XMLRPC_VALUE -- newly allocated data, or NULL if error. Should * be free'd by caller. * SEE ALSO * XMLRPC_VALUE_ToXML () * XMLRPC_REQUEST_FromXML () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) { XMLRPC_VALUE xResponse = NULL; XMLRPC_REQUEST req = XMLRPC_REQUEST_FromXML(in_buf, len, in_options); if(req) { xResponse = req->io; XMLRPC_RequestFree(req, 0); } return xResponse; } /*******/ /* map parser errors to standard xml-rpc errors */ static XMLRPC_VALUE map_expat_errors(XML_ELEM_ERROR error) { XMLRPC_VALUE xReturn = NULL; if(error) { XMLRPC_ERROR_CODE code; char buf[1024]; snprintf(buf, sizeof(buf), "error occurred at line %i, column %i, byte index %i", error->line, error->column, error->byte_index); /* expat specific errors */ switch(error->parser_code) { case XML_ERROR_UNKNOWN_ENCODING: code = xmlrpc_error_parse_unknown_encoding; break; case XML_ERROR_INCORRECT_ENCODING: code = xmlrpc_error_parse_bad_encoding; break; default: code = xmlrpc_error_parse_xml_syntax; break; } xReturn = XMLRPC_UtilityCreateFault(code, buf); } return xReturn; } /****f* SERIALIZE/XMLRPC_REQUEST_FromXML * NAME * XMLRPC_REQUEST_FromXML * SYNOPSIS * XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int le * FUNCTION * Retrieve XMLRPC_REQUEST from XML buffer * INPUTS * in_buf -- character buffer containing XML * len -- length of buffer * RESULT * XMLRPC_REQUEST -- newly allocated data, or NULL if error. Should * be free'd by caller. * SEE ALSO * XMLRPC_REQUEST_ToXML () * XMLRPC_VALUE_FromXML () * XMLRPC_REQUEST * SOURCE */ XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options) { XMLRPC_REQUEST request = XMLRPC_RequestNew(); STRUCT_XML_ELEM_ERROR error = {0}; if(request) { xml_element *root_elem = xml_elem_parse_buf(in_buf, len, (in_options ? &in_options->xml_elem_opts : NULL), &error); if(root_elem) { if(!strcmp(root_elem->name, "simpleRPC")) { request->output.version = xmlrpc_version_simple; xml_element_to_DANDARPC_REQUEST(request, root_elem); } else { request->output.version = xmlrpc_version_1_0; xml_element_to_XMLRPC_REQUEST(request, root_elem); } xml_elem_free(root_elem); } else { if(error.parser_error) { request->error = map_expat_errors(&error); } } } return request; } /*******/ /*-************************ * End Serialization Funcs * **************************/ /****f* VALUE/XMLRPC_CreateValueEmpty * NAME * XMLRPC_CreateValueEmpty * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueEmpty () * FUNCTION * Create an XML value to be used/modified elsewhere. * INPUTS * RESULT * XMLRPC_VALUE. The new value, or NULL on failure. * SEE ALSO * XMLRPC_CleanupValue () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueEmpty() { XMLRPC_VALUE v = calloc(1, sizeof(STRUCT_XMLRPC_VALUE)); if(v) { v->type = xmlrpc_empty; simplestring_init(&v->id); simplestring_init(&v->str); } return v; } static const char* get_string(const char* buf, int bDup) { if(bDup) { return strdup(buf); } return buf; } /*******/ /****f* VALUE/XMLRPC_SetValueID_Case * NAME * XMLRPC_SetValueID_Case * SYNOPSIS * const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case) * FUNCTION * Assign an ID (key) to an XMLRPC value. * INPUTS * value The xml value who's ID we will set. * id The desired new id. * len length of id string if known, or 0 if unknown. * id_case one of XMLRPC_CASE * RESULT * const char* pointer to the newly allocated id string, or NULL * SEE ALSO * XMLRPC_SetValueID () * XMLRPC_GetValueID () * XMLRPC_VALUE * XMLRPC_CASE * SOURCE */ const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case) { const char* pRetval = NULL; if(value) { if(id) { simplestring_clear(&value->id); (len > 0) ? simplestring_addn(&value->id, id, len) : simplestring_add(&value->id, id); /* upper or lower case string in place if required. could be a seperate func. */ if(id_case == xmlrpc_case_lower || id_case == xmlrpc_case_upper) { int i; for(i = 0; i < value->id.len; i++) { value->id.str[i] = (id_case == xmlrpc_case_lower) ? tolower(value->id.str[i]) : toupper(value->id.str[i]); } } pRetval = value->id.str; #ifdef XMLRPC_DEBUG_REFCOUNT printf("set value id: %s\n", pRetval); #endif } } return pRetval; } /*******/ /****f* VALUE/XMLRPC_SetValueString * NAME * XMLRPC_SetValueString * SYNOPSIS * const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) * FUNCTION * Assign a string value to an XMLRPC_VALUE, and set it to type xmlrpc_string * INPUTS * value The xml value who's ID we will set. * val The desired new string val. * len length of val string if known, or 0 if unknown. * RESULT * const char* pointer to the newly allocated value string, or NULL * SEE ALSO * XMLRPC_GetValueString () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* val, int len) { char *pRetval = NULL; if(value && val) { simplestring_clear(&value->str); (len > 0) ? simplestring_addn(&value->str, val, len) : simplestring_add(&value->str, val); value->type = xmlrpc_string; pRetval = (char *)value->str.str; } return pRetval; } /*******/ /****f* VALUE/XMLRPC_SetValueInt * NAME * XMLRPC_SetValueInt * SYNOPSIS * void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) * FUNCTION * Assign an int value to an XMLRPC_VALUE, and set it to type xmlrpc_int * INPUTS * value The xml value who's ID we will set. * val The desired new integer value * RESULT * SEE ALSO * XMLRPC_GetValueInt () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val) { if(value) { value->type = xmlrpc_int; value->i = val; } } /*******/ /****f* VALUE/XMLRPC_SetValueBoolean * NAME * XMLRPC_SetValueBoolean * SYNOPSIS * void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) * FUNCTION * Assign a boolean value to an XMLRPC_VALUE, and set it to type xmlrpc_boolean * INPUTS * value The xml value who's value we will set. * val The desired new boolean value. [0 | 1] * RESULT * SEE ALSO * XMLRPC_GetValueBoolean () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val) { if(value) { value->type = xmlrpc_boolean; value->i = val ? 1 : 0; } } /*******/ /****f* VECTOR/XMLRPC_SetIsVector * NAME * XMLRPC_SetIsVector * SYNOPSIS * int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) * FUNCTION * Set the XMLRPC_VALUE to be a vector (list) type. The vector may be one of * [xmlrpc_array | xmlrpc_struct | xmlrpc_mixed]. An array has only index values. * A struct has key/val pairs. Mixed allows both index and key/val combinations. * INPUTS * value The xml value who's vector type we will set * type New type of vector as enumerated by XMLRPC_VECTOR_TYPE * RESULT * int 1 if successful, 0 otherwise * SEE ALSO * XMLRPC_GetValueType () * XMLRPC_GetVectorType () * XMLRPC_VALUE * XMLRPC_VECTOR_TYPE * XMLRPC_VALUE_TYPE * SOURCE */ int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type) { int bSuccess = 0; if(value && value->type != xmlrpc_vector) { value->v = calloc(1, sizeof(STRUCT_XMLRPC_VECTOR)); if(value->v) { value->v->q = (queue*)malloc(sizeof(queue)); if(value->v->q) { Q_Init(value->v->q); value->v->type = type; value->type = xmlrpc_vector; bSuccess = 1; } } } return bSuccess; } /*******/ /****f* VECTOR/XMLRPC_CreateVector * NAME * XMLRPC_CreateVector * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) * FUNCTION * Create a new vector and optionally set an id. * INPUTS * id The id of the vector, or NULL * type New type of vector as enumerated by XMLRPC_VECTOR_TYPE * RESULT * XMLRPC_VALUE The new vector, or NULL on failure. * SEE ALSO * XMLRPC_CreateValueEmpty () * XMLRPC_SetIsVector () * XMLRPC_GetValueType () * XMLRPC_GetVectorType () * XMLRPC_VALUE * XMLRPC_VECTOR_TYPE * XMLRPC_VALUE_TYPE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type) { XMLRPC_VALUE val = NULL; val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_VECTOR *pSIV = NULL; if(XMLRPC_SetIsVector(val, type)) { if(id) { const char *pSVI = NULL; pSVI = XMLRPC_SetValueID(val, id, 0); if(NULL == pSVI) { val = NULL; } } } else { val = NULL; } } return val; } /*******/ /* Not yet implemented. * * This should use a hash to determine if a given target id has already * been appended. * * Alternately, it could walk the entire vector, but that could be quite * slow for very large lists. */ static int isDuplicateEntry(XMLRPC_VALUE target, XMLRPC_VALUE source) { return 0; } /****f* VECTOR/XMLRPC_AddValueToVector * NAME * XMLRPC_AddValueToVector * SYNOPSIS * int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) * FUNCTION * Add (append) an existing XMLRPC_VALUE to a vector. * INPUTS * target The target vector * source The source value to append * RESULT * int 1 if successful, else 0 * SEE ALSO * XMLRPC_AddValuesToVector () * XMLRPC_VectorGetValueWithID_Case () * XMLRPC_VALUE * NOTES * The function will fail and return 0 if an attempt is made to add * a value with an ID into a vector of type xmlrpc_vector_array. Such * values can only be added to xmlrpc_vector_struct. * SOURCE */ int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source) { if(target && source) { if(target->type == xmlrpc_vector && target->v && target->v->q && target->v->type != xmlrpc_vector_none) { /* guard against putting value of unknown type into vector */ switch(source->type) { case xmlrpc_empty: case xmlrpc_base64: case xmlrpc_boolean: case xmlrpc_datetime: case xmlrpc_double: case xmlrpc_int: case xmlrpc_string: case xmlrpc_vector: /* Guard against putting a key/val pair into an array vector */ if( !(source->id.len && target->v->type == xmlrpc_vector_array) ) { if(isDuplicateEntry(target, source) || Q_PushTail(target->v->q, XMLRPC_CopyValue(source))) { return 1; } } else { fprintf(stderr, "xmlrpc: attempted to add key/val pair to vector of type array\n"); } break; default: fprintf(stderr, "xmlrpc: attempted to add value of unknown type to vector\n"); break; } } } return 0; } /*******/ /****f* VECTOR/XMLRPC_AddValuesToVector * NAME * XMLRPC_AddValuesToVector * SYNOPSIS * XMLRPC_AddValuesToVector ( target, val1, val2, val3, val(n), 0 ) * XMLRPC_AddValuesToVector( XMLRPC_VALUE, ... ) * FUNCTION * Add (append) a series of existing XMLRPC_VALUE to a vector. * INPUTS * target The target vector * ... The source value(s) to append. The last item *must* be 0. * RESULT * int 1 if successful, else 0 * SEE ALSO * XMLRPC_AddValuesToVector () * XMLRPC_VectorGetValueWithID_Case () * XMLRPC_VALUE * NOTES * This function may actually return failure after it has already modified * or added items to target. You can not trust the state of target * if this function returns failure. * SOURCE */ int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...) { int iRetval = 0; if(target) { if(target->type == xmlrpc_vector) { XMLRPC_VALUE v = NULL; va_list vl; va_start(vl, target); do { v = va_arg(vl, XMLRPC_VALUE); if(v) { if(!XMLRPC_AddValueToVector(target, v)) { iRetval = 0; break; } } } while(v); va_end(vl); if(NULL == v) { iRetval = 1; } } } return iRetval; } /*******/ /****f* VECTOR/XMLRPC_VectorGetValueWithID_Case * NAME * XMLRPC_VectorGetValueWithID_Case * SYNOPSIS * XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) * FUNCTION * Get value from vector matching id (key) * INPUTS * vector The source vector * id The key to find * id_case Rule for how to match key * RESULT * int 1 if successful, else 0 * SEE ALSO * XMLRPC_SetValueID_Case () * XMLRPC_VALUE * XMLRPC_CASE_COMPARISON * SOURCE */ XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case) { if(vector && vector->v && vector->v->q) { q_iter qi = Q_Iter_Head_F(vector->v->q); while(qi) { XMLRPC_VALUE xIter = Q_Iter_Get_F(qi); if(xIter && xIter->id.str) { if(id_case == xmlrpc_case_sensitive) { if(!strcmp(xIter->id.str, id)) { return xIter; } } else if(id_case == xmlrpc_case_insensitive) { if(!strcasecmp(xIter->id.str, id)) { return xIter; } } } qi = Q_Iter_Next_F(qi); } } return NULL; } /*******/ int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value) { if(vector && vector->v && vector->v->q && value) { q_iter qi = Q_Iter_Head_F(vector->v->q); while(qi) { XMLRPC_VALUE xIter = Q_Iter_Get_F(qi); if(xIter == value) { XMLRPC_CleanupValue(xIter); Q_Iter_Del(vector->v->q, qi); return 1; } qi = Q_Iter_Next_F(qi); } } return 0; } /****f* VALUE/XMLRPC_CreateValueString * NAME * XMLRPC_CreateValueString * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) * FUNCTION * Create an XMLRPC_VALUE, and assign a string to it * INPUTS * id The id of the value, or NULL * val The desired new string val. * len length of val string if known, or 0 if unknown. * RESULT * newly allocated XMLRPC_VALUE, or NULL * SEE ALSO * XMLRPC_GetValueString () * XMLRPC_CreateValueEmpty () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* val, int len) { XMLRPC_VALUE value = NULL; if(val) { value = XMLRPC_CreateValueEmpty(); if(value) { XMLRPC_SetValueString(value, val, len); if(id) { XMLRPC_SetValueID(value, id, 0); } } } return value; } /*******/ /****f* VALUE/XMLRPC_CreateValueInt * NAME * XMLRPC_CreateValueInt * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) * FUNCTION * Create an XMLRPC_VALUE, and assign an int to it * INPUTS * id The id of the value, or NULL * i The desired new int val. * RESULT * newly allocated XMLRPC_VALUE, or NULL * SEE ALSO * XMLRPC_GetValueInt () * XMLRPC_CreateValueEmpty () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueInt(val, i); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_CreateValueBoolean * NAME * XMLRPC_CreateValueBoolean * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) * FUNCTION * Create an XMLRPC_VALUE, and assign an int to it * INPUTS * id The id of the value, or NULL * i The desired new int val. * RESULT * newly allocated XMLRPC_VALUE, or NULL * SEE ALSO * XMLRPC_GetValueBoolean () * XMLRPC_CreateValueEmpty () * XMLRPC_VALUE * XMLRPC_VALUE_TYPE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int i) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueBoolean(val, i); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_CleanupValue * NAME * XMLRPC_CleanupValue * SYNOPSIS * void XMLRPC_CleanupValue(XMLRPC_VALUE value) * FUNCTION * Frees all memory allocated for an XMLRPC_VALUE and any of its children (if a vector) * INPUTS * value The id of the value to be cleaned up. * RESULT * void * NOTES * Normally this function will be called for the topmost vector, thus free-ing * all children. If a child of a vector is free'd first, results are undefined. * Failure to call this function *will* cause memory leaks. * * Also, this function is implemented using reference counting. Thus a value * may be added and freed from multiple parents so long as a reference is added * first using XMLRPC_CopyValue() * SEE ALSO * XMLRPC_RequestFree () * XMLRPC_CreateValueEmpty () * XMLRPC_CopyValue() * XMLRPC_VALUE * SOURCE */ void XMLRPC_CleanupValue(XMLRPC_VALUE value) { if(value) { if(value->iRefCount > 0) { value->iRefCount --; } #ifdef XMLRPC_DEBUG_REFCOUNT if(value->id.str) { printf("decremented refcount of %s, now %i\n", value->id.str, value->iRefCount); } else { printf("decremented refcount of 0x%x, now %i\n", value, value->iRefCount); } #endif if(value->type == xmlrpc_vector) { if(value->v) { if(value->iRefCount == 0) { XMLRPC_VALUE cur = (XMLRPC_VALUE)Q_Head(value->v->q); while( cur ) { XMLRPC_CleanupValue(cur); /* Make sure some idiot didn't include a vector as a child of itself * and thus it would have already free'd these. */ if(value->v && value->v->q) { cur = Q_Next(value->v->q); } else { break; } } Q_Destroy(value->v->q); my_free(value->v->q); my_free(value->v); } } } if(value->iRefCount == 0) { /* guard against freeing invalid types */ switch(value->type) { case xmlrpc_empty: case xmlrpc_base64: case xmlrpc_boolean: case xmlrpc_datetime: case xmlrpc_double: case xmlrpc_int: case xmlrpc_string: case xmlrpc_vector: #ifdef XMLRPC_DEBUG_REFCOUNT if(value->id.str) { printf("free'd %s\n", value->id.str); } else { printf("free'd 0x%x\n", value); } #endif simplestring_free(&value->id); simplestring_free(&value->str); memset(value, 0, sizeof(STRUCT_XMLRPC_VALUE)); my_free(value); break; default: fprintf(stderr, "xmlrpc: attempted to free value of invalid type\n"); break; } } } } /*******/ /****f* VALUE/XMLRPC_SetValueDateTime * NAME * XMLRPC_SetValueDateTime * SYNOPSIS * void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) * FUNCTION * Assign time value to XMLRPC_VALUE * INPUTS * value The target XMLRPC_VALUE * time The desired new unix time value (time_t) * RESULT * void * SEE ALSO * XMLRPC_GetValueDateTime () * XMLRPC_SetValueDateTime_ISO8601 () * XMLRPC_CreateValueDateTime () * XMLRPC_VALUE * SOURCE */ void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time) { if(value) { char timeBuf[30]; value->type = xmlrpc_datetime; value->i = time; timeBuf[0] = 0; date_to_ISO8601(time, timeBuf, sizeof(timeBuf)); if(timeBuf[0]) { simplestring_clear(&value->str); simplestring_add(&value->str, timeBuf); } } } /*******/ /****f* VALUE/XMLRPC_CopyValue * NAME * XMLRPC_CopyValue * SYNOPSIS * XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) * FUNCTION * Make a copy of an XMLRPC_VALUE * INPUTS * value The target XMLRPC_VALUE * RESULT * XMLRPC_VALUE -- address of the copy * SEE ALSO * XMLRPC_CleanupValue () * NOTES * This function is implemented via reference counting, so the * returned value is going to be the same as the passed in value. * The value must be freed the same number of times it is copied * or there will be a memory leak. * SOURCE */ XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value) { if(value) { value->iRefCount ++; #ifdef XMLRPC_DEBUG_REFCOUNT if(value->id.str) { printf("incremented refcount of %s\n", value->id.str); } #endif } return value; } /*******/ /****f* VALUE/XMLRPC_CreateValueDateTime * NAME * XMLRPC_CreateValueDateTime * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) * FUNCTION * Create new datetime value from time_t * INPUTS * id id of the new value, or NULL * time The desired unix time value (time_t) * RESULT * void * SEE ALSO * XMLRPC_GetValueDateTime () * XMLRPC_SetValueDateTime () * XMLRPC_CreateValueDateTime_ISO8601 () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueDateTime(val, time); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_SetValueDateTime_ISO8601 * NAME * XMLRPC_SetValueDateTime_ISO8601 * SYNOPSIS * void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) * FUNCTION * Set datetime value from IS08601 encoded string * INPUTS * value The target XMLRPC_VALUE * s The desired new time value * RESULT * void * BUGS * This function currently attempts to convert the time string to a valid unix time * value before passing it. Behavior when the string is invalid or out of range * is not well defined, but will probably result in Jan 1, 1970 (0) being passed. * SEE ALSO * XMLRPC_GetValueDateTime_ISO8601 () * XMLRPC_CreateValueDateTime_ISO8601 () * XMLRPC_CreateValueDateTime () * XMLRPC_VALUE * SOURCE */ void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s) { if(value) { time_t time_val = 0; if(s) { date_from_ISO8601(s, &time_val); XMLRPC_SetValueDateTime(value, time_val); } } } /*******/ /****f* VALUE/XMLRPC_CreateValueDateTime_ISO8601 * NAME * XMLRPC_CreateValueDateTime_ISO8601 * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) * FUNCTION * Create datetime value from IS08601 encoded string * INPUTS * id The id of the new value, or NULL * s The desired new time value * RESULT * newly allocated XMLRPC_VALUE, or NULL if no value created. * BUGS * See XMLRPC_SetValueDateTime_ISO8601 () * SEE ALSO * XMLRPC_GetValueDateTime_ISO8601 () * XMLRPC_SetValueDateTime_ISO8601 () * XMLRPC_CreateValueDateTime () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueDateTime_ISO8601(val, s); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_SetValueBase64 * NAME * XMLRPC_SetValueBase64 * SYNOPSIS * void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) * FUNCTION * Set base64 value. Base64 is useful for transferring binary data, such as an image. * INPUTS * value The target XMLRPC_VALUE * s The desired new binary value * len The length of s, or NULL. If buffer is not null terminated, len *must* be passed. * RESULT * void * NOTES * Data is set/stored/retrieved as passed in, but is base64 encoded for XML transfer, and * decoded on the other side. This is transparent to the caller. * SEE ALSO * XMLRPC_GetValueBase64 () * XMLRPC_CreateValueBase64 () * XMLRPC_VALUE * SOURCE */ void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len) { if(value && s) { simplestring_clear(&value->str); (len > 0) ? simplestring_addn(&value->str, s, len) : simplestring_add(&value->str, s); value->type = xmlrpc_base64; } } /*******/ /****f* VALUE/XMLRPC_CreateValueBase64 * NAME * XMLRPC_CreateValueBase64 * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) * FUNCTION * Create base64 value. Base64 is useful for transferring binary data, such as an image. * INPUTS * id id of the new value, or NULL * s The desired new binary value * len The length of s, or NULL. If buffer is not null terminated, len *must* be passed. * RESULT * newly allocated XMLRPC_VALUE, or NULL if error * NOTES * See XMLRPC_SetValueBase64 () * SEE ALSO * XMLRPC_GetValueBase64 () * XMLRPC_SetValueBase64 () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueBase64(val, s, len); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_SetValueDouble * NAME * XMLRPC_SetValueDouble * SYNOPSIS * void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) * FUNCTION * Set double (floating point) value. * INPUTS * value The target XMLRPC_VALUE * val The desired new double value * RESULT * void * SEE ALSO * XMLRPC_GetValueDouble () * XMLRPC_CreateValueDouble () * XMLRPC_VALUE * SOURCE */ void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val) { if(value) { value->type = xmlrpc_double; value->d = val; } } /*******/ /****f* VALUE/XMLRPC_CreateValueDouble * NAME * XMLRPC_CreateValueDouble * SYNOPSIS * XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) * FUNCTION * Create double (floating point) value. * INPUTS * id id of the newly created value, or NULL * d The desired new double value * RESULT * void * SEE ALSO * XMLRPC_GetValueDouble () * XMLRPC_CreateValueDouble () * XMLRPC_VALUE * SOURCE */ XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double d) { XMLRPC_VALUE val = XMLRPC_CreateValueEmpty(); if(val) { XMLRPC_SetValueDouble(val, d); if(id) { XMLRPC_SetValueID(val, id, 0); } } return val; } /*******/ /****f* VALUE/XMLRPC_GetValueString * NAME * XMLRPC_GetValueString * SYNOPSIS * const char* XMLRPC_GetValueString(XMLRPC_VALUE value) * FUNCTION * retrieve string value * INPUTS * value source XMLRPC_VALUE of type xmlrpc_string * RESULT * void * SEE ALSO * XMLRPC_SetValueString () * XMLRPC_GetValueType () * XMLRPC_VALUE * SOURCE */ const char* XMLRPC_GetValueString(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_string) ? value->str.str : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueStringLen * NAME * XMLRPC_GetValueStringLen * SYNOPSIS * int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) * FUNCTION * determine length of string value * INPUTS * value XMLRPC_VALUE of type xmlrpc_string * RESULT * length of string, or 0 * NOTES * SEE ALSO * XMLRPC_SetValueString () * XMLRPC_GetValueString () * SOURCE */ int XMLRPC_GetValueStringLen(XMLRPC_VALUE value) { return ((value) ? value->str.len : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueInt * NAME * XMLRPC_GetValueInt * SYNOPSIS * int XMLRPC_GetValueInt(XMLRPC_VALUE value) * FUNCTION * retrieve integer value. * INPUTS * value XMLRPC_VALUE of type xmlrpc_int * RESULT * integer value or 0 if value is not valid int * NOTES * use XMLRPC_GetValueType () to be sure if 0 is real return value or not * SEE ALSO * XMLRPC_SetValueInt () * XMLRPC_CreateValueInt () * SOURCE */ int XMLRPC_GetValueInt(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_int) ? value->i : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueBoolean * NAME * XMLRPC_GetValueBoolean * SYNOPSIS * int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) * FUNCTION * retrieve boolean value. * INPUTS * XMLRPC_VALUE of type xmlrpc_boolean * RESULT * boolean value or 0 if value is not valid boolean * NOTES * use XMLRPC_GetValueType() to be sure if 0 is real value or not * SEE ALSO * XMLRPC_SetValueBoolean () * XMLRPC_CreateValueBoolean () * SOURCE */ int XMLRPC_GetValueBoolean(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_boolean) ? value->i : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueDouble * NAME * XMLRPC_GetValueDouble * SYNOPSIS * double XMLRPC_GetValueDouble(XMLRPC_VALUE value) * FUNCTION * retrieve double value * INPUTS * XMLRPC_VALUE of type xmlrpc_double * RESULT * double value or 0 if value is not valid double. * NOTES * use XMLRPC_GetValueType() to be sure if 0 is real value or not * SEE ALSO * XMLRPC_SetValueDouble () * XMLRPC_CreateValueDouble () * SOURCE */ double XMLRPC_GetValueDouble(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_double) ? value->d : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueBase64 * NAME * XMLRPC_GetValueBase64 * SYNOPSIS * const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) * FUNCTION * retrieve binary value * INPUTS * XMLRPC_VALUE of type xmlrpc_base64 * RESULT * pointer to binary value or 0 if value is not valid. * SEE ALSO * XMLRPC_SetValueBase64 () * XMLRPC_CreateValueBase64 () * NOTES * Call XMLRPC_GetValueStringLen() to retrieve real length of binary data. strlen() * will not be accurate, as returned data may contain embedded nulls. * SOURCE */ const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_base64) ? value->str.str : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueDateTime * NAME * XMLRPC_GetValueDateTime * SYNOPSIS * time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) * FUNCTION * retrieve time_t value * INPUTS * XMLRPC_VALUE of type xmlrpc_datetime * RESULT * time_t value or 0 if value is not valid datetime. * NOTES * use XMLRPC_GetValueType() to be sure if 0 is real value or not * SEE ALSO * XMLRPC_SetValueDateTime () * XMLRPC_GetValueDateTime_ISO8601 () * XMLRPC_CreateValueDateTime () * SOURCE */ time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value) { return (time_t)((value && value->type == xmlrpc_datetime) ? value->i : 0); } /*******/ /****f* VALUE/XMLRPC_GetValueDateTime_IOS8601 * NAME * XMLRPC_GetValueDateTime_IOS8601 * SYNOPSIS * const char* XMLRPC_GetValueDateTime_IOS8601(XMLRPC_VALUE value) * FUNCTION * retrieve ISO8601 formatted time value * INPUTS * XMLRPC_VALUE of type xmlrpc_datetime * RESULT * const char* value or 0 if value is not valid datetime. * SEE ALSO * XMLRPC_SetValueDateTime_IOS8601 () * XMLRPC_GetValueDateTime () * XMLRPC_CreateValueDateTime_IOS8601 () * SOURCE */ const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value) { return ((value && value->type == xmlrpc_datetime) ? value->str.str : 0); } /*******/ /* Get ID (key) of value or NULL */ /****f* VALUE/XMLRPC_GetValueID * NAME * XMLRPC_GetValueID * SYNOPSIS * const char* XMLRPC_GetValueID(XMLRPC_VALUE value) * FUNCTION * retrieve id (key) of value * INPUTS * XMLRPC_VALUE of any type * RESULT * const char* pointer to id of value, or NULL * NOTES * SEE ALSO * XMLRPC_SetValueID() * XMLRPC_CreateValueEmpty() * SOURCE */ const char* XMLRPC_GetValueID(XMLRPC_VALUE value) { return (const char*)((value && value->id.len) ? value->id.str : 0); } /*******/ /****f* VECTOR/XMLRPC_VectorSize * NAME * XMLRPC_VectorSize * SYNOPSIS * int XMLRPC_VectorSize(XMLRPC_VALUE value) * FUNCTION * retrieve size of vector * INPUTS * XMLRPC_VALUE of type xmlrpc_vector * RESULT * count of items in vector * NOTES * This is a cheap operation even on large vectors. Vector size is * maintained by queue during add/remove ops. * SEE ALSO * XMLRPC_AddValueToVector () * SOURCE */ int XMLRPC_VectorSize(XMLRPC_VALUE value) { int size = 0; if(value && value->type == xmlrpc_vector && value->v) { size = Q_Size(value->v->q); } return size; } /*******/ /****f* VECTOR/XMLRPC_VectorRewind * NAME * XMLRPC_VectorRewind * SYNOPSIS * XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) * FUNCTION * reset vector to first item * INPUTS * XMLRPC_VALUE of type xmlrpc_vector * RESULT * first XMLRPC_VALUE in list, or NULL if empty or error. * NOTES * Be careful to rewind any vector passed in to you if you expect to * iterate through the entire list. * SEE ALSO * XMLRPC_VectorNext () * SOURCE */ XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value) { XMLRPC_VALUE xReturn = NULL; if(value && value->type == xmlrpc_vector && value->v) { xReturn = (XMLRPC_VALUE)Q_Head(value->v->q); } return xReturn; } /*******/ /****f* VECTOR/XMLRPC_VectorNext * NAME * XMLRPC_VectorNext * SYNOPSIS * XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) * FUNCTION * Iterate vector to next item in list. * INPUTS * XMLRPC_VALUE of type xmlrpc_vector * RESULT * Next XMLRPC_VALUE in vector, or NULL if at end. * NOTES * SEE ALSO * XMLRPC_VectorRewind () * SOURCE */ XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value) { XMLRPC_VALUE xReturn = NULL; if(value && value->type == xmlrpc_vector && value->v) { xReturn = (XMLRPC_VALUE)Q_Next(value->v->q); } return xReturn; } /*******/ /****f* VALUE/XMLRPC_GetValueType * NAME * XMLRPC_GetValueType * SYNOPSIS * XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) * FUNCTION * determine data type of the XMLRPC_VALUE * INPUTS * XMLRPC_VALUE target of query * RESULT * data type of value as enumerated by XMLRPC_VALUE_TYPE * NOTES * all values are of type xmlrpc_empty until set. * SEE ALSO * XMLRPC_SetValue* * XMLRPC_CreateValue* * XMLRPC_Append* * SOURCE */ XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE value) { return value ? value->type : xmlrpc_empty; } /*******/ /* Vector type accessor */ /****f* VALUE/XMLRPC_GetVectorType * NAME * XMLRPC_GetVectorType * SYNOPSIS * XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) * FUNCTION * determine vector type of the XMLRPC_VALUE * INPUTS * XMLRPC_VALUE of type xmlrpc_vector * RESULT * vector type of value as enumerated by XMLRPC_VECTOR_TYPE * NOTES * xmlrpc_none is returned if value is not a vector * SEE ALSO * XMLRPC_SetIsVector () * XMLRPC_GetValueType () * SOURCE */ XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE value) { return(value && value->v) ? value->v->type : xmlrpc_none; } /*******/ /*-******************* * Begin Server Funcs * *********************/ /****f* VALUE/XMLRPC_ServerCreate * NAME * XMLRPC_ServerCreate * SYNOPSIS * XMLRPC_SERVER XMLRPC_ServerCreate() * FUNCTION * Allocate/Init XMLRPC Server Resources. * INPUTS * none * RESULT * newly allocated XMLRPC_SERVER * NOTES * SEE ALSO * XMLRPC_ServerDestroy () * XMLRPC_GetGlobalServer () * SOURCE */ XMLRPC_SERVER XMLRPC_ServerCreate() { XMLRPC_SERVER server = calloc(1, sizeof(STRUCT_XMLRPC_SERVER)); if(server) { Q_Init(&server->methodlist); Q_Init(&server->docslist); /* register system methods */ xsm_register(server); } return server; } /*******/ /* Return global server. Not locking! Not Thread Safe! */ /****f* VALUE/XMLRPC_GetGlobalServer * NAME * XMLRPC_GetGlobalServer * SYNOPSIS * XMLRPC_SERVER XMLRPC_GetGlobalServer() * FUNCTION * Allocates a global (process-wide) server, or returns pointer if pre-existing. * INPUTS * none * RESULT * pointer to global server, or 0 if error. * NOTES * ***WARNING*** This function is not thread safe. It is included only for the very lazy. * Multi-threaded programs that use this may experience problems. * BUGS * There is currently no way to cleanup the global server gracefully. * SEE ALSO * XMLRPC_ServerCreate () * SOURCE */ XMLRPC_SERVER XMLRPC_GetGlobalServer() { static XMLRPC_SERVER xsServer = 0; if(!xsServer) { xsServer = XMLRPC_ServerCreate(); } return xsServer; } /*******/ /****f* VALUE/XMLRPC_ServerDestroy * NAME * XMLRPC_ServerDestroy * SYNOPSIS * void XMLRPC_ServerDestroy(XMLRPC_SERVER server) * FUNCTION * Free Server Resources * INPUTS * server The server to be free'd * RESULT * void * NOTES * This frees the server struct and any methods that have been added. * SEE ALSO * XMLRPC_ServerCreate () * SOURCE */ void XMLRPC_ServerDestroy(XMLRPC_SERVER server) { if(server) { doc_method* dm = Q_Head(&server->docslist); server_method* sm = Q_Head(&server->methodlist); while( dm ) { my_free(dm); dm = Q_Next(&server->docslist); } while( sm ) { if(sm->name) { my_free(sm->name); } if(sm->desc) { XMLRPC_CleanupValue(sm->desc); } my_free(sm); sm = Q_Next(&server->methodlist); } if(server->xIntrospection) { XMLRPC_CleanupValue(server->xIntrospection); } Q_Destroy(&server->methodlist); Q_Destroy(&server->docslist); my_free(server); } } /*******/ /****f* VALUE/XMLRPC_ServerRegisterMethod * NAME * XMLRPC_ServerRegisterMethod * SYNOPSIS * void XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb) * FUNCTION * Register new XMLRPC method with server * INPUTS * server The XMLRPC_SERVER to register the method with * name public name of the method * cb C function that implements the method * RESULT * int - 1 if success, else 0 * NOTES * A C function must be registered for every "method" that the server recognizes. The * method name is equivalent to <methodCall><name> method name </name></methodCall> in the * XML syntax. * SEE ALSO * XMLRPC_ServerFindMethod () * XMLRPC_ServerCallMethod () * SOURCE */ int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb) { if(server && name && cb) { server_method* sm = malloc(sizeof(server_method)); if(sm) { sm->name = strdup(name); sm->method = cb; sm->desc = NULL; return Q_PushTail(&server->methodlist, sm); } } return 0; } /*******/ inline server_method* find_method(XMLRPC_SERVER server, const char* name) { server_method* sm; q_iter qi = Q_Iter_Head_F(&server->methodlist); while( qi ) { sm = Q_Iter_Get_F(qi); if(sm && !strcmp(sm->name, name)) { return sm; } qi = Q_Iter_Next_F(qi); } return NULL; } const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) { switch(type) { case xmlrpc_none: return "none"; case xmlrpc_empty: return "empty"; case xmlrpc_base64: return "base64"; case xmlrpc_boolean: return "boolean"; case xmlrpc_datetime: return "datetime"; case xmlrpc_double: return "double"; case xmlrpc_int: return "int"; case xmlrpc_string: return "string"; case xmlrpc_vector: switch(vtype) { case xmlrpc_vector_none: return "none"; case xmlrpc_vector_array: return "array"; case xmlrpc_vector_mixed: return "mixed vector (struct)"; case xmlrpc_vector_struct: return "struct"; } } } /****f* VALUE/XMLRPC_ServerFindMethod * NAME * XMLRPC_ServerFindMethod * SYNOPSIS * XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName) * FUNCTION * retrieve C callback associated with a given method name. * INPUTS * server The XMLRPC_SERVER the method is registered with * callName the method to find * RESULT * previously registered XMLRPC_Callback, or NULL * NOTES * Typically, this is used to determine if a requested method exists, without actually calling it. * SEE ALSO * XMLRPC_ServerCallMethod () * XMLRPC_ServerRegisterMethod () * SOURCE */ XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName) { if(server && callName) { q_iter qi = Q_Iter_Head_F(&server->methodlist); while( qi ) { server_method* sm = Q_Iter_Get_F(qi); if(sm && !strcmp(sm->name, callName)) { return sm->method; } qi = Q_Iter_Next_F(qi); } } return NULL; } /*******/ /* Call method specified in request */ /****f* VALUE/XMLRPC_ServerCallMethod * NAME * XMLRPC_ServerCallMethod * SYNOPSIS * XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData) * FUNCTION * * INPUTS * server The XMLRPC_SERVER the method is registered with * request the request to handle * userData any additional data to pass to the C callback, or NULL * RESULT * XMLRPC_VALUE allocated by the callback, or NULL * NOTES * It is typically the caller's responsibility to free the returned value. * * Often the caller will want to serialize the result as XML, via * XMLRPC_VALUE_To_XML () or XMLRPC_REQUEST_To_XML () * SEE ALSO * XMLRPC_ServerFindMethod () * XMLRPC_ServerRegisterMethod () * XMLRPC_CleanupValue () * SOURCE */ XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData) { XMLRPC_VALUE xReturn = NULL; /* check for error set during request parsing / generation */ if(request && request->error) { xReturn = XMLRPC_CopyValue(request->error); } else if(server && request && request->methodName.str) { XMLRPC_Callback cb = XMLRPC_ServerFindMethod(server, request->methodName.str); if(cb) { xReturn = cb(server, request, userData); } else { xReturn = XMLRPC_UtilityCreateFault(xmlrpc_error_unknown_method, request->methodName.str); } } return xReturn; } /*******/ /*-***************** * End server funcs * *******************/ /*-*********************************** * Begin XMLRPC General Options funcs * *************************************/ /* For options used by XMLRPC_VALUE funcs that otherwise do not have * parameters for options. Kind of gross. :( */ typedef struct _xmlrpc_options { XMLRPC_CASE id_case; XMLRPC_CASE_COMPARISON id_case_compare; } STRUCT_XMLRPC_OPTIONS, *XMLRPC_OPTIONS; static XMLRPC_OPTIONS XMLRPC_GetDefaultOptions() { static STRUCT_XMLRPC_OPTIONS options = { xmlrpc_case_exact, xmlrpc_case_sensitive }; return &options; } /****f* VALUE/XMLRPC_GetDefaultIdCase * NAME * XMLRPC_GetDefaultIdCase * SYNOPSIS * XMLRPC_CASE XMLRPC_GetDefaultIdCase() * FUNCTION * Gets default case options used by XMLRPC_VALUE funcs * INPUTS * none * RESULT * XMLRPC_CASE * BUGS * Nasty and gross. Should be server specific, but that requires changing all * the XMLRPC_VALUE api's. * SEE ALSO * XMLRPC_SetDefaultIdCase () * SOURCE */ XMLRPC_CASE XMLRPC_GetDefaultIdCase() { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); return options->id_case; } /*******/ /****f* VALUE/XMLRPC_SetDefaultIdCase * NAME * XMLRPC_SetDefaultIdCase * SYNOPSIS * XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) * FUNCTION * Sets default case options used by XMLRPC_VALUE funcs * INPUTS * id_case case options as enumerated by XMLRPC_CASE * RESULT * XMLRPC_CASE -- newly set option * BUGS * Nasty and gross. Should be server specific, but that requires changing all * the XMLRPC_VALUE api's. * SEE ALSO * XMLRPC_GetDefaultIdCase () * SOURCE */ XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case) { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); options->id_case = id_case; return options->id_case; } /*******/ /****f* VALUE/XMLRPC_GetDefaultIdCaseComparison * NAME * XMLRPC_GetDefaultIdCaseComparison * SYNOPSIS * XMLRPC_CASE XMLRPC_GetDefaultIdCaseComparison( ) * FUNCTION * Gets default case comparison options used by XMLRPC_VALUE funcs * INPUTS * none * RESULT * XMLRPC_CASE_COMPARISON default * BUGS * Nasty and gross. Should be server specific, but that requires changing all * the XMLRPC_VALUE api's. * SEE ALSO * XMLRPC_SetDefaultIdCaseComparison () * SOURCE */ XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison() { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); return options->id_case_compare; } /*******/ /****f* VALUE/XMLRPC_SetDefaultIdCaseComparison * NAME * XMLRPC_SetDefaultIdCaseComparison * SYNOPSIS * XMLRPC_CASE XMLRPC_SetDefaultIdCaseComparison( XMLRPC_CASE_COMPARISON id_case_compare ) * FUNCTION * Gets default case comparison options used by XMLRPC_VALUE funcs * INPUTS * id_case_compare case comparison rule to set as default * RESULT * XMLRPC_CASE_COMPARISON newly set default * BUGS * Nasty and gross. Should be server specific, but that requires changing all * the XMLRPC_VALUE api's. * SEE ALSO * XMLRPC_GetDefaultIdCaseComparison () * SOURCE */ XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case_compare) { XMLRPC_OPTIONS options = XMLRPC_GetDefaultOptions(); options->id_case_compare = id_case_compare; return options->id_case_compare; } /*******/ /*-********************************* * End XMLRPC General Options funcs * ***********************************/ /*-****************** * Utility API funcs * ********************/ /****f* UTILITY/XMLRPC_UtilityCreateFault * NAME * XMLRPC_UtilityCreateFault * SYNOPSIS * XMLRPC_VALUE XMLRPC_UtilityCreateFault( int fault_code, const char* fault_string ) * FUNCTION * generates a struct containing a string member with id "faultString" and an int member * with id "faultCode". When using the xmlrpc xml serialization, these will be translated * to <fault><value><struct>... format. * INPUTS * fault_code application specific error code. can be 0. * fault_string application specific error string. cannot be null. * RESULT * XMLRPC_VALUE a newly created struct vector representing the error, or null on error. * NOTES * This is a utility function. xmlrpc "faults" are not directly represented in this xmlrpc * API or data structures. It is the author's view, that this API is intended for simple * data types, and a "fault" is a complex data type consisting of multiple simple data * types. This function is provided for convenience only, the same result could be * achieved directly by the application. * * This function now supports some "standardized" fault codes, as specified at. * http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php. * If one of these fault codes is received, the description string will automatically * be prefixed with a standard error string and 2 newlines. * * The actual transformation between this complex type and the xml "<fault>" element takes * place in the xmlrpc to xml serialization layer. This step is not performed when using the * simplerpc serialization, meaning that there will be no "<fault>" element in that * serialization. There will simply be a standard struct with 2 child elements. * imho, the "<fault>" element is unnecessary and/or out of place as part of the standard API. * SOURCE */ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string) { XMLRPC_VALUE xOutput = NULL; char* string = NULL; simplestring description; simplestring_init(&description); switch (fault_code) { case xmlrpc_error_parse_xml_syntax: string = xmlrpc_error_parse_xml_syntax_str; break; case xmlrpc_error_parse_unknown_encoding: string = xmlrpc_error_parse_unknown_encoding_str; break; case xmlrpc_error_parse_bad_encoding: string = xmlrpc_error_parse_bad_encoding_str; break; case xmlrpc_error_invalid_xmlrpc: string = xmlrpc_error_invalid_xmlrpc_str; break; case xmlrpc_error_unknown_method: string = xmlrpc_error_unknown_method_str; break; case xmlrpc_error_invalid_params: string = xmlrpc_error_invalid_params_str; break; case xmlrpc_error_internal_server: string = xmlrpc_error_internal_server_str; break; case xmlrpc_error_application: string = xmlrpc_error_application_str; break; case xmlrpc_error_system: string = xmlrpc_error_system_str; break; case xmlrpc_error_transport: string = xmlrpc_error_transport_str; break; } simplestring_add(&description, string); if(string && fault_string) { simplestring_add(&description, "\n\n"); } simplestring_add(&description, fault_string); if(description.len) { xOutput = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); XMLRPC_VectorAppendString(xOutput, "faultString", description.str, description.len); XMLRPC_VectorAppendInt(xOutput, "faultCode", fault_code); } simplestring_free(&description); return xOutput; } /*******/ /****f* UTILITY/XMLRPC_Free * NAME * XMLRPC_Free * SYNOPSIS * void XMLRPC_Free(void* mem) * FUNCTION * frees a block of memory allocated by xmlrpc. * INPUTS * mem memory to free * RESULT * void * NOTES * Useful for OS's where memory must be free'd * in the same library in which it is allocated. * SOURCE */ void XMLRPC_Free(void* mem) { my_free(mem); } /*******/ /****f* UTILITY/XMLRPC_GetVersionString * NAME * XMLRPC_GetVersionString * SYNOPSIS * const char* XMLRPC_GetVersionString() * FUNCTION * returns library version string * INPUTS * * RESULT * const char* * NOTES * SOURCE */ const char* XMLRPC_GetVersionString() { return XMLRPC_VERSION_STR; } /*******/ /*-********************** * End Utility API funcs * ************************/ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc.h +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ #ifndef XMLRPC_ALREADY_INCLUDED #define XMLRPC_ALREADY_INCLUDED 1 /* includes */ #include "xml_element.h" #include <time.h> /* for time_t */ #ifdef __cplusplus extern "C" { #endif /* this number, representing the date, must be increased each time the API changes */ #define XMLRPC_API_NO 20010721 /* this string should be changed with each packaged release */ #define XMLRPC_VERSION_STR "xmlrpc-epi v. " VERSION /* where to find more info. shouldn't need to change much */ #define XMLRPC_HOME_PAGE_STR "http://xmlprc-epi.sourceforge.net/" /****d* VALUE/XMLRPC_VALUE_TYPE * NAME * XMLRPC_VALUE_TYPE * NOTES * Defines data types for XMLRPC_VALUE * SEE ALSO * XMLRPC_VECTOR_TYPE * XMLRPC_REQUEST_TYPE * SOURCE */ typedef enum _XMLRPC_VALUE_TYPE { xmlrpc_none, /* not a value */ xmlrpc_empty, /* empty value, eg NULL */ xmlrpc_base64, /* base64 value, eg binary data */ xmlrpc_boolean, /* boolean [0 | 1] */ xmlrpc_datetime, /* datetime [ISO8601 | time_t] */ xmlrpc_double, /* double / floating point */ xmlrpc_int, /* integer */ xmlrpc_string, /* string */ xmlrpc_vector /* vector, aka list, array */ } XMLRPC_VALUE_TYPE; /*******/ /****d* VALUE/XMLRPC_VECTOR_TYPE * NAME * XMLRPC_VECTOR_TYPE * NOTES * Defines data types for XMLRPC_VECTOR * SEE ALSO * XMLRPC_VALUE_TYPE * XMLRPC_REQUEST_TYPE * SOURCE */ typedef enum _XMLRPC_VECTOR_TYPE { xmlrpc_vector_none, /* not an array */ xmlrpc_vector_array, /* no values may have key names */ xmlrpc_vector_mixed, /* some values may have key names */ xmlrpc_vector_struct /* all values must have key names */ } XMLRPC_VECTOR_TYPE; /*******/ /****d* VALUE/XMLRPC_REQUEST_TYPE * NAME * XMLRPC_REQUEST_TYPE * NOTES * Defines data types for XMLRPC_REQUEST * SEE ALSO * XMLRPC_VALUE_TYPE * XMLRPC_VECTOR_TYPE * SOURCE */ typedef enum _xmlrpc_request_type { xmlrpc_request_none, /* not a valid request */ xmlrpc_request_call, /* calling/invoking a method */ xmlrpc_request_response, /* responding to a method call */ } XMLRPC_REQUEST_TYPE; /*******/ /****d* VALUE/XMLRPC_ERROR_CODE * NAME * XMLRPC_ERROR_CODE * NOTES * All existing error codes * SEE ALSO * XMLRPC_REQUEST_ERROR * SOURCE */ typedef enum _xmlrpc_error_code { xmlrpc_error_none = 0, /* not an error */ xmlrpc_error_parse_xml_syntax = -32700, xmlrpc_error_parse_unknown_encoding = -32701, xmlrpc_error_parse_bad_encoding = -32702, xmlrpc_error_invalid_xmlrpc = -32600, xmlrpc_error_unknown_method = -32601, xmlrpc_error_invalid_params = -32602, xmlrpc_error_internal_server = -32603, xmlrpc_error_application = -32500, xmlrpc_error_system = -32400, xmlrpc_error_transport = -32300 } XMLRPC_ERROR_CODE; /******/ #define xmlrpc_error_parse_xml_syntax_str "parse error. not well formed." #define xmlrpc_error_parse_unknown_encoding_str "parse error. unknown encoding" #define xmlrpc_error_parse_bad_encoding_str "parse error. invalid character for encoding" #define xmlrpc_error_invalid_xmlrpc_str "server error. xml-rpc not conforming to spec" #define xmlrpc_error_unknown_method_str "server error. method not found." #define xmlrpc_error_invalid_params_str "server error. invalid method parameters" #define xmlrpc_error_internal_server_str "server error. internal xmlrpc library error" #define xmlrpc_error_application_str "application error." #define xmlrpc_error_system_str "system error." #define xmlrpc_error_transport_str "transport error." /****d* VALUE/XMLRPC_VERSION * NAME * XMLRPC_VERSION * NOTES * Defines xml vocabulary used for generated xml * SEE ALSO * XMLRPC_REQUEST_OUTPUT_OPTIONS * XMLRPC_REQUEST_To_XML () * SOURCE */ typedef enum _xmlrpc_version { xmlrpc_version_none, /* not a recognized vocabulary */ xmlrpc_version_1_0, /* xmlrpc 1.0 standard vocab */ xmlrpc_version_simple = 2, /* alt more readable vocab */ xmlrpc_version_danda = 2 /* same as simple. legacy */ } XMLRPC_VERSION; /******/ /****s* VALUE/XMLRPC_REQUEST_OUTPUT_OPTIONS * NAME * XMLRPC_REQUEST_OUTPUT_OPTIONS * NOTES * Defines output options for generated xml * SEE ALSO * XMLRPC_VERSION * XML_ELEM_OUTPUT_OPTIONS * XMLRPC_REQUEST_To_XML () * SOURCE */ typedef struct _xmlrpc_request_output_options { STRUCT_XML_ELEM_OUTPUT_OPTIONS xml_elem_opts; /* xml_element specific output options */ XMLRPC_VERSION version; /* xml vocabulary to use */ } STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS, *XMLRPC_REQUEST_OUTPUT_OPTIONS; /******/ /****s* VALUE/XMLRPC_REQUEST_INPUT_OPTIONS * NAME * XMLRPC_REQUEST_INPUT_OPTIONS * NOTES * Defines options for reading in xml data * SEE ALSO * XMLRPC_VERSION * XML_ELEM_INPUT_OPTIONS * XMLRPC_REQUEST_From_XML () * SOURCE */ typedef struct _xmlrpc_request_input_options { STRUCT_XML_ELEM_INPUT_OPTIONS xml_elem_opts; /* xml_element specific output options */ } STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS, *XMLRPC_REQUEST_INPUT_OPTIONS; /******/ /****s* VALUE/XMLRPC_ERROR * NAME * XMLRPC_ERROR * NOTES * For the reporting and handling of errors * SOURCE */ typedef struct _xmlrpc_error { XMLRPC_ERROR_CODE code; STRUCT_XML_ELEM_ERROR xml_elem_error; /* xml_element errors (parser errors) */ } STRUCT_XMLRPC_ERROR, *XMLRPC_ERROR; /******/ /****d* VALUE/XMLRPC_CASE_COMPARISON * NAME * XMLRPC_CASE_COMPARISON * NOTES * Defines case comparison options for XMLRPC_VALUE/VECTOR API's * SEE ALSO * XMLRPC_CASE * XMLRPC_VALUE * SOURCE */ typedef enum _xmlrpc_case_comparison { xmlrpc_case_insensitive, /* use case-insensitive compare */ xmlrpc_case_sensitive /* use case-sensitive compare */ } XMLRPC_CASE_COMPARISON; /******/ /****d* VALUE/XMLRPC_CASE * NAME * XMLRPC_CASE * NOTES * Defines case behavior when setting IDs in XMLRPC_VALUE API's * SEE ALSO * XMLRPC_CASE_COMPARISON * XMLRPC_VALUE * SOURCE */ typedef enum _xmlrpc_case { xmlrpc_case_exact, /* leave case alone */ xmlrpc_case_lower, /* lower-case id */ xmlrpc_case_upper /* upper-case id */ } XMLRPC_CASE; /******/ /* if you don't like these defaults, you can set them with XMLRPC_SetDefaultIdCase*() */ #define XMLRPC_DEFAULT_ID_CASE XMLRPC_GetDefaultIdCase() #define XMLRPC_DEFAULT_ID_CASE_SENSITIVITY XMLRPC_GetDefaultIdCaseComparison() /* opaque (non-public) types. defined locally in xmlrpc.c */ typedef struct _xmlrpc_request* XMLRPC_REQUEST; typedef struct _xmlrpc_server* XMLRPC_SERVER; typedef struct _xmlrpc_value* XMLRPC_VALUE; /****d* VALUE/XMLRPC_Callback * NAME * XMLRPC_Callback * NOTES * Function prototype for user defined method handlers (callbacks). * SEE ALSO * XMLRPC_ServerRegisterMethod () * XMLRPC_ServerCallMethod () * XMLRPC_REQUEST * XMLRPC_VALUE * SOURCE */ typedef XMLRPC_VALUE (*XMLRPC_Callback)(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); /******/ /* ID Case Defaults */ XMLRPC_CASE XMLRPC_GetDefaultIdCase(); XMLRPC_CASE XMLRPC_SetDefaultIdCase(XMLRPC_CASE id_case); XMLRPC_CASE_COMPARISON XMLRPC_GetDefaultIdCaseComparison(); XMLRPC_CASE_COMPARISON XMLRPC_SetDefaultIdCaseComparison(XMLRPC_CASE_COMPARISON id_case); /* Vector manipulation */ int XMLRPC_VectorSize(XMLRPC_VALUE value); XMLRPC_VALUE XMLRPC_VectorRewind(XMLRPC_VALUE value); XMLRPC_VALUE XMLRPC_VectorNext(XMLRPC_VALUE value); int XMLRPC_SetIsVector(XMLRPC_VALUE value, XMLRPC_VECTOR_TYPE type); int XMLRPC_AddValueToVector(XMLRPC_VALUE target, XMLRPC_VALUE source); int XMLRPC_AddValuesToVector(XMLRPC_VALUE target, ...); int XMLRPC_VectorRemoveValue(XMLRPC_VALUE vector, XMLRPC_VALUE value); XMLRPC_VALUE XMLRPC_VectorGetValueWithID_Case(XMLRPC_VALUE vector, const char* id, XMLRPC_CASE_COMPARISON id_case); /* Create values */ XMLRPC_VALUE XMLRPC_CreateValueBoolean(const char* id, int truth); XMLRPC_VALUE XMLRPC_CreateValueBase64(const char* id, const char* s, int len); XMLRPC_VALUE XMLRPC_CreateValueDateTime(const char* id, time_t time); XMLRPC_VALUE XMLRPC_CreateValueDateTime_ISO8601(const char* id, const char *s); XMLRPC_VALUE XMLRPC_CreateValueDouble(const char* id, double f); XMLRPC_VALUE XMLRPC_CreateValueInt(const char* id, int i); XMLRPC_VALUE XMLRPC_CreateValueString(const char* id, const char* s, int len); XMLRPC_VALUE XMLRPC_CreateValueEmpty(); XMLRPC_VALUE XMLRPC_CreateVector(const char* id, XMLRPC_VECTOR_TYPE type); /* Cleanup values */ void XMLRPC_CleanupValue(XMLRPC_VALUE value); XMLRPC_VALUE XMLRPC_CopyValue(XMLRPC_VALUE value); /* Set Values */ void XMLRPC_SetValueDateTime(XMLRPC_VALUE value, time_t time); void XMLRPC_SetValueDateTime_ISO8601(XMLRPC_VALUE value, const char* s); void XMLRPC_SetValueDouble(XMLRPC_VALUE value, double val); void XMLRPC_SetValueInt(XMLRPC_VALUE value, int val); void XMLRPC_SetValueBoolean(XMLRPC_VALUE value, int val); const char *XMLRPC_SetValueString(XMLRPC_VALUE value, const char* s, int len); void XMLRPC_SetValueBase64(XMLRPC_VALUE value, const char* s, int len); const char *XMLRPC_SetValueID_Case(XMLRPC_VALUE value, const char* id, int len, XMLRPC_CASE id_case); #define XMLRPC_SetValueID(value, id, len) XMLRPC_SetValueID_Case(value, id, len, XMLRPC_DEFAULT_ID_CASE) /* Get Values */ const char* XMLRPC_GetValueString(XMLRPC_VALUE value); int XMLRPC_GetValueStringLen(XMLRPC_VALUE value); int XMLRPC_GetValueInt(XMLRPC_VALUE value); int XMLRPC_GetValueBoolean(XMLRPC_VALUE value); double XMLRPC_GetValueDouble(XMLRPC_VALUE value); const char* XMLRPC_GetValueBase64(XMLRPC_VALUE value); time_t XMLRPC_GetValueDateTime(XMLRPC_VALUE value); const char* XMLRPC_GetValueDateTime_ISO8601(XMLRPC_VALUE value); const char* XMLRPC_GetValueID(XMLRPC_VALUE value); /* Type introspection */ XMLRPC_VALUE_TYPE XMLRPC_GetValueType(XMLRPC_VALUE v); XMLRPC_VECTOR_TYPE XMLRPC_GetVectorType(XMLRPC_VALUE v); /* Parsing and Creating XML */ XMLRPC_REQUEST XMLRPC_REQUEST_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options); XMLRPC_VALUE XMLRPC_VALUE_FromXML(const char* in_buf, int len, XMLRPC_REQUEST_INPUT_OPTIONS in_options); char* XMLRPC_REQUEST_ToXML(XMLRPC_REQUEST request, int *buf_len); char* XMLRPC_VALUE_ToXML(XMLRPC_VALUE val, int* buf_len); /* Request manipulation funcs */ const char* XMLRPC_RequestSetMethodName(XMLRPC_REQUEST request, const char* methodName); const char* XMLRPC_RequestGetMethodName(XMLRPC_REQUEST request); XMLRPC_REQUEST XMLRPC_RequestNew(); void XMLRPC_RequestFree(XMLRPC_REQUEST request, int bFreeIO); XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestSetOutputOptions(XMLRPC_REQUEST request, XMLRPC_REQUEST_OUTPUT_OPTIONS output); XMLRPC_REQUEST_OUTPUT_OPTIONS XMLRPC_RequestGetOutputOptions(XMLRPC_REQUEST request); XMLRPC_VALUE XMLRPC_RequestSetData(XMLRPC_REQUEST request, XMLRPC_VALUE data); XMLRPC_VALUE XMLRPC_RequestGetData(XMLRPC_REQUEST request); XMLRPC_REQUEST_TYPE XMLRPC_RequestSetRequestType(XMLRPC_REQUEST request, XMLRPC_REQUEST_TYPE type); XMLRPC_REQUEST_TYPE XMLRPC_RequestGetRequestType(XMLRPC_REQUEST request); /* Server Creation/Destruction; Method Registration and Invocation */ XMLRPC_SERVER XMLRPC_ServerCreate(); XMLRPC_SERVER XMLRPC_GetGlobalServer(); /* better to use XMLRPC_ServerCreate if you can */ void XMLRPC_ServerDestroy(XMLRPC_SERVER server); int XMLRPC_ServerRegisterMethod(XMLRPC_SERVER server, const char *name, XMLRPC_Callback cb); XMLRPC_Callback XMLRPC_ServerFindMethod(XMLRPC_SERVER server, const char* callName); XMLRPC_VALUE XMLRPC_ServerCallMethod(XMLRPC_SERVER server, XMLRPC_REQUEST request, void* userData); #include "xmlrpc_introspection.h" /* Public Utility funcs */ XMLRPC_VALUE XMLRPC_UtilityCreateFault(int fault_code, const char* fault_string); void XMLRPC_Free(void* mem); const char* XMLRPC_GetVersionString(); /****d* VALUE/XMLRPC_MACROS * NAME * Some Helpful Macros * NOTES * Some macros for making life easier. Should be self-explanatory. * SEE ALSO * XMLRPC_AddValueToVector () * XMLRPC_VectorGetValueWithID_Case () * XMLRPC_VALUE * SOURCE */ /* Append values to vector */ #define XMLRPC_VectorAppendString(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueString(id, s, len)) #define XMLRPC_VectorAppendBase64(vector, id, s, len) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBase64(id, s, len)) #define XMLRPC_VectorAppendDateTime(vector, id, time) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime(id, time)) #define XMLRPC_VectorAppendDateTime_ISO8601(vector, id, s) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDateTime_ISO8601(id, s)) #define XMLRPC_VectorAppendDouble(vector, id, f) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueDouble(id, f)) #define XMLRPC_VectorAppendInt(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueInt(id, i)) #define XMLRPC_VectorAppendBoolean(vector, id, i) XMLRPC_AddValueToVector(vector, XMLRPC_CreateValueBoolean(id, i)) /* Get named values from vector */ #define XMLRPC_VectorGetValueWithID(vector, id) XMLRPC_VectorGetValueWithID_Case(vector, id, XMLRPC_DEFAULT_ID_CASE_SENSITIVITY) #define XMLRPC_VectorGetStringWithID(vector, id) XMLRPC_GetValueString(XMLRPC_VectorGetValueWithID(vector, id)) #define XMLRPC_VectorGetBase64WithID(vector, id) XMLRPC_GetValueBase64(XMLRPC_VectorGetValueWithID(vector, id)) #define XMLRPC_VectorGetDateTimeWithID(vector, id) XMLRPC_GetValueDateTime(XMLRPC_VectorGetValueWithID(vector, id)) #define XMLRPC_VectorGetDoubleWithID(vector, id) XMLRPC_GetValueDouble(XMLRPC_VectorGetValueWithID(vector, id)) #define XMLRPC_VectorGetIntWithID(vector, id) XMLRPC_GetValueInt(XMLRPC_VectorGetValueWithID(vector, id)) #define XMLRPC_VectorGetBooleanWithID(vector, id) XMLRPC_GetValueBoolean(XMLRPC_VectorGetValueWithID(vector, id)) /******/ #ifdef __cplusplus } #endif #endif /* not XMLRPC_ALREADY_INCLUDED */ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc.m4 +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc.m4 CPPFLAGS="$CPPFLAGS -DVERSION=\\\"0.42\\\"" AC_DEFUN(XMLRPC_CHECKS,[ AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_PROG_LN_S]) AC_REQUIRE([AC_PROG_RANLIB]) AC_DEFINE(UNDEF_THREADS_HACK,,[ ]) XMLRPC_HEADER_CHECKS XMLRPC_TYPE_CHECKS XMLRPC_FUNCTION_CHECKS PHP_FAST_OUTPUT($ext_builddir/libxmlrpc/Makefile) ]) Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.c /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /****h* ABOUT/xmlrpc_introspection * AUTHOR * Dan Libby, aka danda (dan@libby.com) * HISTORY * 4/10/2001 -- danda -- initial introspection support * TODO * NOTES *******/ #include "queue.h" #include "xmlrpc.h" #include "xmlrpc_private.h" #include "xmlrpc_introspection_private.h" #include <string.h> #include <stdlib.h> #include <stdarg.h> /* forward declarations for static (non public, non api) funcs */ static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData); /*-********************************** * Introspection Callbacks (methods) * ************************************/ /* iterates through a list of structs and finds the one with key "name" matching * needle. slow, would benefit from a struct key hash. */ inline XMLRPC_VALUE find_named_value(XMLRPC_VALUE list, const char* needle) { XMLRPC_VALUE xIter = XMLRPC_VectorRewind(list); while(xIter) { const char* name = XMLRPC_VectorGetStringWithID(xIter, xi_token_name); if(name && !strcmp(name, needle)) { return xIter; } xIter = XMLRPC_VectorNext(list); } return NULL; } /* iterates through docs callbacks and calls any that have not yet been called */ static void check_docs_loaded(XMLRPC_SERVER server, void* userData) { if(server) { q_iter qi = Q_Iter_Head_F(&server->docslist); while( qi ) { doc_method* dm = Q_Iter_Get_F(qi); if(dm && !dm->b_called) { dm->method(server, userData); dm->b_called = 1; } qi = Q_Iter_Next_F(qi); } } } /* utility function for xi_system_describe_methods_cb */ inline void describe_method(XMLRPC_SERVER server, XMLRPC_VALUE vector, const char* method) { if(method) { server_method* sm = find_method(server, method); if(sm) { XMLRPC_AddValueToVector(vector, sm->desc); } } } /* system.describeMethods() callback */ static XMLRPC_VALUE xi_system_describe_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { XMLRPC_VALUE xParams = XMLRPC_VectorRewind(XMLRPC_RequestGetData(input)); XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); XMLRPC_VALUE xMethodList = XMLRPC_CreateVector("methodList", xmlrpc_vector_array); XMLRPC_VALUE xTypeList = NULL; int bAll = 1; /* lazy loading of introspection data */ check_docs_loaded(server, userData); xTypeList = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList"); XMLRPC_AddValueToVector(xResponse, xTypeList); XMLRPC_AddValueToVector(xResponse, xMethodList); /* check if we have any param */ if(xParams) { /* check if string or vector (1 or n) */ XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(xParams); if(type == xmlrpc_string) { /* just one. spit it out. */ describe_method(server, xMethodList, XMLRPC_GetValueString(xParams)); bAll = 0; } else if(type == xmlrpc_vector) { /* multiple. spit all out */ XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xParams); while(xIter) { describe_method(server, xMethodList, XMLRPC_GetValueString(xIter)); xIter = XMLRPC_VectorNext(xParams); } bAll = 0; } } /* otherwise, default to sending all methods */ if(bAll) { q_iter qi = Q_Iter_Head_F(&server->methodlist); while( qi ) { server_method* sm = Q_Iter_Get_F(qi); if(sm) { XMLRPC_AddValueToVector(xMethodList, sm->desc); } qi = Q_Iter_Next_F(qi); } } return xResponse; } /* this complies with system.listMethods as defined at http://xmlrpc.usefulinc.com/doc/reserved.html */ static XMLRPC_VALUE xi_system_list_methods_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { XMLRPC_VALUE xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); q_iter qi = Q_Iter_Head_F(&server->methodlist); while( qi ) { server_method* sm = Q_Iter_Get_F(qi); if(sm) { XMLRPC_VectorAppendString(xResponse, 0, sm->name, 0); } qi = Q_Iter_Next_F(qi); } return xResponse; } /* this complies with system.methodSignature as defined at * http://xmlrpc.usefulinc.com/doc/sysmethodsig.html */ static XMLRPC_VALUE xi_system_method_signature_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input))); XMLRPC_VALUE xResponse = NULL; /* lazy loading of introspection data */ check_docs_loaded(server, userData); if(method) { server_method* sm = find_method(server, method); if(sm && sm->desc) { XMLRPC_VALUE xTypesArray = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); XMLRPC_VALUE xIter, xParams, xSig, xSigIter; const char* type; /* array of possible signatures. */ xResponse = XMLRPC_CreateVector(NULL, xmlrpc_vector_array); /* find first signature */ xSig = XMLRPC_VectorGetValueWithID(sm->desc, xi_token_signatures); xSigIter = XMLRPC_VectorRewind( xSig ); /* iterate through sigs */ while(xSigIter) { /* first type is the return value */ type = XMLRPC_VectorGetStringWithID(XMLRPC_VectorRewind( XMLRPC_VectorGetValueWithID(xSigIter, xi_token_returns)), xi_token_type); XMLRPC_AddValueToVector(xTypesArray, XMLRPC_CreateValueString(NULL, type ? type : type_to_str(xmlrpc_none, 0), 0)); /* the rest are parameters */ xParams = XMLRPC_VectorGetValueWithID(xSigIter, xi_token_params); xIter = XMLRPC_VectorRewind(xParams); /* iter through params, adding to types array */ while(xIter) { XMLRPC_AddValueToVector(xTypesArray, XMLRPC_CreateValueString(NULL, XMLRPC_VectorGetStringWithID(xIter, xi_token_type), 0)); xIter = XMLRPC_VectorNext(xParams); } /* add types for this signature */ XMLRPC_AddValueToVector(xResponse, xTypesArray); xSigIter = XMLRPC_VectorNext( xSig ); } } } return xResponse; } /* this complies with system.methodHelp as defined at * http://xmlrpc.usefulinc.com/doc/sysmethhelp.html */ static XMLRPC_VALUE xi_system_method_help_cb(XMLRPC_SERVER server, XMLRPC_REQUEST input, void* userData) { const char* method = XMLRPC_GetValueString(XMLRPC_VectorRewind(XMLRPC_RequestGetData(input))); XMLRPC_VALUE xResponse = NULL; /* lazy loading of introspection data */ check_docs_loaded(server, userData); if(method) { server_method* sm = find_method(server, method); if(sm && sm->desc) { const char* help = XMLRPC_VectorGetStringWithID(sm->desc, xi_token_purpose); /* returns a documentation string, or empty string */ xResponse = XMLRPC_CreateValueString(NULL, help ? help : xi_token_empty, 0); } } return xResponse; } /*-************************************** * End Introspection Callbacks (methods) * ****************************************/ /*-************************ * Introspection Utilities * **************************/ /* performs registration of introspection methods */ void xi_register_system_methods(XMLRPC_SERVER server) { XMLRPC_ServerRegisterMethod(server, xi_token_system_list_methods, xi_system_list_methods_cb); XMLRPC_ServerRegisterMethod(server, xi_token_system_method_help, xi_system_method_help_cb); XMLRPC_ServerRegisterMethod(server, xi_token_system_method_signature, xi_system_method_signature_cb); XMLRPC_ServerRegisterMethod(server, xi_token_system_describe_methods, xi_system_describe_methods_cb); } /* describe a value (param, return, type) */ static XMLRPC_VALUE describeValue_worker(const char* type, const char* id, const char* desc, int optional, const char* default_val, XMLRPC_VALUE sub_params) { XMLRPC_VALUE xParam = NULL; if(id || desc) { xParam = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); XMLRPC_VectorAppendString(xParam, xi_token_name, id, 0); XMLRPC_VectorAppendString(xParam, xi_token_type, type, 0); XMLRPC_VectorAppendString(xParam, xi_token_description, desc, 0); if(optional != 2) { XMLRPC_VectorAppendInt(xParam, xi_token_optional, optional); } if(optional == 1 && default_val) { XMLRPC_VectorAppendString(xParam, xi_token_default, default_val, 0); } XMLRPC_AddValueToVector(xParam, sub_params); } return xParam; } /* convert an xml tree conforming to spec <url tbd> to XMLRPC_VALUE * suitable for use with XMLRPC_ServerAddIntrospectionData */ XMLRPC_VALUE xml_element_to_method_description(xml_element* el, XMLRPC_ERROR err) { XMLRPC_VALUE xReturn = NULL; if(el->name) { const char* name = NULL; const char* type = NULL; const char* basetype = NULL; const char* desc = NULL; const char* def = NULL; int optional = 0; xml_element_attr* attr_iter = Q_Head(&el->attrs); /* grab element attributes up front to save redundant while loops */ while(attr_iter) { if(!strcmp(attr_iter->key, "name")) { name = attr_iter->val; } else if(!strcmp(attr_iter->key, "type")) { type = attr_iter->val; } else if(!strcmp(attr_iter->key, "basetype")) { basetype = attr_iter->val; } else if(!strcmp(attr_iter->key, "desc")) { desc = attr_iter->val; } else if(!strcmp(attr_iter->key, "optional")) { if(attr_iter->val && !strcmp(attr_iter->val, "yes")) { optional = 1; } } else if(!strcmp(attr_iter->key, "default")) { def = attr_iter->val; } attr_iter = Q_Next(&el->attrs); } /* value and typeDescription behave about the same */ if(!strcmp(el->name, "value") || !strcmp(el->name, "typeDescription")) { XMLRPC_VALUE xSubList = NULL; const char* ptype = !strcmp(el->name, "value") ? type : basetype; if(ptype) { if(Q_Size(&el->children) && !strcmp(ptype, "array") || !strcmp(ptype, "struct") || !strcmp(ptype, "mixed")) { xSubList = XMLRPC_CreateVector("member", xmlrpc_vector_array); if(xSubList) { xml_element* elem_iter = Q_Head(&el->children); while(elem_iter) { XMLRPC_AddValueToVector(xSubList, xml_element_to_method_description(elem_iter, err)); elem_iter = Q_Next(&el->children); } } } xReturn = describeValue_worker(ptype, name, (desc ? desc : (xSubList ? NULL : el->text.str)), optional, def, xSubList); } } /* these three kids are about equivalent */ else if(!strcmp(el->name, "params") || !strcmp(el->name, "returns") || !strcmp(el->name, "signature")) { if(Q_Size(&el->children)) { xml_element* elem_iter = Q_Head(&el->children); xReturn = XMLRPC_CreateVector(!strcmp(el->name, "signature") ? NULL : el->name, xmlrpc_vector_struct); while(elem_iter) { XMLRPC_AddValueToVector(xReturn, xml_element_to_method_description(elem_iter, err)); elem_iter = Q_Next(&el->children); } } } else if(!strcmp(el->name, "methodDescription")) { xml_element* elem_iter = Q_Head(&el->children); xReturn = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); XMLRPC_VectorAppendString(xReturn, xi_token_name, name, 0); while(elem_iter) { XMLRPC_AddValueToVector(xReturn, xml_element_to_method_description(elem_iter, err)); elem_iter = Q_Next(&el->children); } } /* items are slightly special */ else if(!strcmp(el->name, "item")) { xReturn = XMLRPC_CreateValueString(name, el->text.str, el->text.len); } /* sure. we'll let any ol element with children through */ else if(Q_Size(&el->children)) { xml_element* elem_iter = Q_Head(&el->children); xReturn = XMLRPC_CreateVector(el->name, xmlrpc_vector_mixed); while(elem_iter) { XMLRPC_AddValueToVector(xReturn, xml_element_to_method_description(elem_iter, err)); elem_iter = Q_Next(&el->children); } } /* or anything at all really, so long as its got some text. * no reason being all snotty about a spec, right? */ else if(el->name && el->text.len) { xReturn = XMLRPC_CreateValueString(el->name, el->text.str, el->text.len); } } return xReturn; } /*-**************************** * End Introspection Utilities * ******************************/ /*-****************** * Introspection API * ********************/ /****f* VALUE/XMLRPC_IntrospectionCreateDescription * NAME * XMLRPC_IntrospectionCreateDescription * SYNOPSIS * XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) * FUNCTION * converts raw xml describing types and methods into an * XMLRPC_VALUE suitable for use with XMLRPC_ServerAddIntrospectionData() * INPUTS * xml - xml data conforming to introspection spec at <url tbd> * err - optional pointer to error struct. filled in if error occurs and not NULL. * RESULT * XMLRPC_VALUE - newly created value, or NULL if fatal error. * BUGS * Currently does little or no validation of xml. * Only parse errors are currently reported in err, not structural errors. * SEE ALSO * XMLRPC_ServerAddIntrospectionData () * SOURCE */ XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR err) { XMLRPC_VALUE xReturn = NULL; xml_element* root = xml_elem_parse_buf(xml, 0, 0, err ? &err->xml_elem_error : NULL); if(root) { xReturn = xml_element_to_method_description(root, err); xml_elem_free(root); } return xReturn; } /*******/ /****f* SERVER/XMLRPC_ServerAddIntrospectionData * NAME * XMLRPC_ServerAddIntrospectionData * SYNOPSIS * int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) * FUNCTION * updates server with additional introspection data * INPUTS * server - target server * desc - introspection data, should be a struct generated by * XMLRPC_IntrospectionCreateDescription () * RESULT * int - 1 if success, else 0 * NOTES * - function will fail if neither typeList nor methodList key is present in struct. * - if method or type already exists, it will be replaced. * - desc is never freed by the server. caller is responsible for cleanup. * BUGS * - horribly slow lookups. prime candidate for hash improvements. * - uglier and more complex than I like to see for API functions. * SEE ALSO * XMLRPC_ServerAddIntrospectionData () * XMLRPC_ServerRegisterIntrospectionCallback () * XMLRPC_CleanupValue () * SOURCE */ int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc) { int bSuccess = 0; if(server && desc) { XMLRPC_VALUE xNewTypes = XMLRPC_VectorGetValueWithID(desc, "typeList"); XMLRPC_VALUE xNewMethods = XMLRPC_VectorGetValueWithID(desc, "methodList"); XMLRPC_VALUE xServerTypes = XMLRPC_VectorGetValueWithID(server->xIntrospection, "typeList"); if(xNewMethods) { XMLRPC_VALUE xMethod = XMLRPC_VectorRewind(xNewMethods); while(xMethod) { const char* name = XMLRPC_VectorGetStringWithID(xMethod, xi_token_name); server_method* sm = find_method(server, name); if(sm) { if(sm->desc) { XMLRPC_CleanupValue(sm->desc); } sm->desc = XMLRPC_CopyValue(xMethod); bSuccess = 1; } xMethod = XMLRPC_VectorNext(xNewMethods); } } if(xNewTypes) { if(!xServerTypes) { if(!server->xIntrospection) { server->xIntrospection = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct); } XMLRPC_AddValueToVector(server->xIntrospection, xNewTypes); bSuccess = 1; } else { XMLRPC_VALUE xIter = XMLRPC_VectorRewind(xNewTypes); while(xIter) { /* get rid of old values */ XMLRPC_VALUE xPrev = find_named_value(xServerTypes, XMLRPC_VectorGetStringWithID(xIter, xi_token_name)); if(xPrev) { XMLRPC_VectorRemoveValue(xServerTypes, xPrev); } XMLRPC_AddValueToVector(xServerTypes, xIter); bSuccess = 1; xIter = XMLRPC_VectorNext(xNewTypes); } } } } return bSuccess; } /*******/ /****f* SERVER/XMLRPC_ServerRegisterIntrospectionCallback * NAME * XMLRPC_ServerRegisterIntrospectionCallback * SYNOPSIS * int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) * FUNCTION * registers a callback for lazy generation of introspection data * INPUTS * server - target server * cb - callback that will generate introspection data * RESULT * int - 1 if success, else 0 * NOTES * parsing xml and generating introspection data is fairly expensive, thus a * server may wish to wait until this data is actually requested before generating * it. Any number of callbacks may be registered at any time. A given callback * will only ever be called once, the first time an introspection request is * processed after the time of callback registration. * SEE ALSO * XMLRPC_ServerAddIntrospectionData () * XMLRPC_IntrospectionCreateDescription () * SOURCE */ int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb) { int bSuccess = 0; if(server && cb) { doc_method* dm = calloc(1, sizeof(doc_method)); if(dm) { dm->method = cb; dm->b_called = 0; if(Q_PushTail(&server->docslist, dm)) { bSuccess = 1; } else { my_free(dm); } } } return 0; } /*******/ /*-********************** * End Introspection API * ************************/ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.h +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* IMPORTANT! * * only public (official API) things should be in this file. Anything else * should go in <group>_private.h, or in the appropriate .c file. */ #ifndef __XI_INTROSPECTION_H /* * Avoid include redundancy. */ #define __XI_INTROSPECTION_H /*---------------------------------------------------------------------------- * xmlrpc_introspection.h * * Purpose: * define public introspection API * Comments: */ /*---------------------------------------------------------------------------- * Constants */ #define xi_token_params "params" #define xi_token_returns "returns" #define xi_token_related "related" #define xi_token_sub "sub" /*---------------------------------------------------------------------------- * Includes */ /*---------------------------------------------------------------------------- * Structures */ /****d* VALUE/XMLRPC_IntrospectionCallback * NAME * XMLRPC_IntrospectionCallback * NOTES * Function prototype for lazy documentation generation (not generated until requested). * SOURCE */ typedef void (*XMLRPC_IntrospectionCallback)(XMLRPC_SERVER server, void* userData); /******/ /*---------------------------------------------------------------------------- * Globals */ /*---------------------------------------------------------------------------- * Functions */ XMLRPC_VALUE XMLRPC_IntrospectionCreateDescription(const char* xml, XMLRPC_ERROR error); int XMLRPC_ServerAddIntrospectionData(XMLRPC_SERVER server, XMLRPC_VALUE desc); int XMLRPC_ServerRegisterIntrospectionCallback(XMLRPC_SERVER server, XMLRPC_IntrospectionCallback cb); /*---------------------------------------------------------------------------- * Macros */ #endif /* __XI_INTROSPECTION_H */ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc_introspection_private.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2001 Dan Libby, Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* IMPORTANT! * * only non-public things should be in this file. It is fine for any .c file * in xmlrpc/src to include it, but users of the public API should never * include it, and thus *.h files that are part of the public API should * never include it, or they would break if this file is not present. */ #ifndef __XI_INTROSPECTION_PRIVATE_H /* * Avoid include redundancy. */ #define __XI_INTROSPECTION_PRIVATE_H /*---------------------------------------------------------------------------- * xmlrpc_introspection_private.h * * Purpose: * define non-public introspection routines * Comments: */ /*---------------------------------------------------------------------------- * Constants */ #define xi_token_default "default" #define xi_token_description "description" #define xi_token_name "name" #define xi_token_optional "optional" #define xi_token_params "params" #define xi_token_purpose "purpose" #define xi_token_returns "returns" #define xi_token_signatures "signatures" #define xi_token_type "type" #define xi_token_version "version" #define xi_token_empty "" #define xi_token_system_describe_methods "system.describeMethods" #define xi_token_system_list_methods "system.listMethods" #define xi_token_system_method_help "system.methodHelp" #define xi_token_system_method_signature "system.methodSignature" /*---------------------------------------------------------------------------- * Includes */ /*---------------------------------------------------------------------------- * Structures */ typedef struct _doc_method { XMLRPC_IntrospectionCallback method; int b_called; } doc_method; /*---------------------------------------------------------------------------- * Globals */ /*---------------------------------------------------------------------------- * Functions */ void xi_register_system_methods(XMLRPC_SERVER server); /*---------------------------------------------------------------------------- * Macros */ #endif /* __XI_INTROSPECTION_PRIVATE_H */ Index: php4/ext/xmlrpc/libxmlrpc/xmlrpc_private.h +++ php4/ext/xmlrpc/libxmlrpc/xmlrpc_private.h /* This file is part of libXMLRPC - a C library for xml-encoded function calls. Author: Dan Libby (dan@libby.com) Epinions.com may be contacted at feedback@epinions-inc.com */ /* Copyright 2000 Epinions, Inc. Subject to the following 3 conditions, Epinions, Inc. permits you, free of charge, to (a) use, copy, distribute, modify, perform and display this software and associated documentation files (the "Software"), and (b) permit others to whom the Software is furnished to do so as well. 1) The above copyright notice and this permission notice shall be included without modification in all copies or substantial portions of the Software. 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. */ /* only non-public things should be in this file. It is fine for any .c file * in xmlrpc/src to include it, but users of the public API should never * include it, and thus *.h files that are part of the public API should * never include it, or they would break if this file is not present. */ #ifndef XMLRPC_PRIVATE_ALREADY_INCLUDED /* * Avoid include redundancy. */ #define XMLRPC_PRIVATE_ALREADY_INCLUDED #ifdef __cplusplus extern "C" { #endif /*---------------------------------------------------------------------------- * xmlrpc_private.h * * Purpose: * define non-public intra-library routines & data * Comments: */ /*---------------------------------------------------------------------------- * Constants */ /*---------------------------------------------------------------------------- * Includes */ /*---------------------------------------------------------------------------- * Structures */ /* Some of these are typedef'd in xmlrpc.h for public use */ typedef struct _xmlrpc_vector* XMLRPC_VECTOR; /****s* VALUE/XMLRPC_VALUE * NAME * XMLRPC_VALUE * NOTES * A value of variable data type. The most important object in this API. :) * * This struct is opaque to callers and should be accessed only via accessor functions. * SEE ALSO * XMLRPC_REQUEST * XMLRPC_CreateValueEmpty () * XMLRPC_CleanupValue () * SOURCE */ typedef struct _xmlrpc_value { XMLRPC_VALUE_TYPE type; /* data type of this value */ XMLRPC_VECTOR v; /* vector type specific info */ simplestring str; /* string value buffer */ simplestring id; /* id of this value. possibly empty. */ int i; /* integer value. */ double d; /* double value */ int iRefCount; /* So we know when we can delete the value . */ } STRUCT_XMLRPC_VALUE; /******/ /****s* VALUE/XMLRPC_REQUEST * NAME * XMLRPC_REQUEST * NOTES * Internal representation of an XML request. * * This struct is opaque to callers and should be accessed only via accessor functions. * * SEE ALSO * XMLRPC_VALUE * XMLRPC_RequestNew () * XMLRPC_RequestFree () * SOURCE */ typedef struct _xmlrpc_request { XMLRPC_VALUE io; /* data associated with this request */ simplestring methodName; /* name of method being called */ XMLRPC_REQUEST_TYPE request_type; /* type of request */ STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS output; /* xml output options */ XMLRPC_VALUE error; /* error codes */ } STRUCT_XMLRPC_REQUEST; /******/ /* Vector type. Used by XMLRPC_VALUE. Never visible to users of the API. */ typedef struct _xmlrpc_vector { XMLRPC_VECTOR_TYPE type; /* vector type */ const char* id; /* ??? unused? */ queue *q; /* list of child values */ } STRUCT_XMLRPC_VECTOR; /******/ /****s* VALUE/XMLRPC_SERVER * NAME * XMLRPC_SERVER * NOTES * internal representation of an xmlrpc server * * This struct is opaque to callers and should be accessed only via accessor functions. * * SEE ALSO * XMLRPC_ServerCreate () * XMLRPC_ServerDestroy () * SOURCE */ typedef struct _xmlrpc_server { queue methodlist; /* list of callback methods */ queue docslist; /* list of introspection callbacks */ XMLRPC_VALUE xIntrospection; } STRUCT_XMLRPC_SERVER; /******/ typedef struct _server_method { char* name; XMLRPC_VALUE desc; XMLRPC_Callback method; } server_method; /*---------------------------------------------------------------------------- * Globals */ /*---------------------------------------------------------------------------- * Functions */ server_method* find_method(XMLRPC_SERVER server, const char* name); const char* type_to_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype); /*---------------------------------------------------------------------------- * Macros */ #define my_free(thing) if(thing) {free(thing); thing = 0;} #ifdef __cplusplus } #endif #endif /* XMLRPC_PRIVATE_ALREADY_INCLUDED */