diff options
author | Lorry Tar Creator <lorry-tar-importer@baserock.org> | 2013-03-14 05:42:27 +0000 |
---|---|---|
committer | <> | 2013-04-03 16:25:08 +0000 |
commit | c4dd7a1a684490673e25aaf4fabec5df138854c4 (patch) | |
tree | 4d57c44caae4480efff02b90b9be86f44bf25409 /ext/xmlrpc/xmlrpc-epi-php.c | |
download | php2-master.tar.gz |
Imported from /home/lorry/working-area/delta_php2/php-5.4.13.tar.bz2.HEADphp-5.4.13master
Diffstat (limited to 'ext/xmlrpc/xmlrpc-epi-php.c')
-rw-r--r-- | ext/xmlrpc/xmlrpc-epi-php.c | 1546 |
1 files changed, 1546 insertions, 0 deletions
diff --git a/ext/xmlrpc/xmlrpc-epi-php.c b/ext/xmlrpc/xmlrpc-epi-php.c new file mode 100644 index 0000000..925b554 --- /dev/null +++ b/ext/xmlrpc/xmlrpc-epi-php.c @@ -0,0 +1,1546 @@ +/* + 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 5 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2013 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.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. | + +----------------------------------------------------------------------+ + | Author: Dan Libby | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/********************************************************************** +* BUGS: * +* - when calling a php user function, there appears to be no way to * +* distinguish between a return value of null, and no return value * +* at all. The xml serialization layer(s) will then return a value * +* of null, when the right thing may be no value at all. (SOAP) * +**********************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "ext/standard/info.h" +#include "ext/standard/php_string.h" +#include "ext/date/php_date.h" +#include "php_ini.h" +#include "php_xmlrpc.h" +#include "xmlrpc.h" + +#define PHP_EXT_VERSION "0.51" + +static int le_xmlrpc_server; + +/* {{{ arginfo */ +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_encode, 0, 0, 1) + ZEND_ARG_INFO(0, value) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_decode, 0, 0, 1) + ZEND_ARG_INFO(0, value) + ZEND_ARG_INFO(0, encoding) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_decode_request, 0, 0, 2) + ZEND_ARG_INFO(0, xml) + ZEND_ARG_INFO(1, method) + ZEND_ARG_INFO(0, encoding) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_encode_request, 0, 0, 2) + ZEND_ARG_INFO(0, method) + ZEND_ARG_INFO(0, params) + ZEND_ARG_INFO(0, output_options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_set_type, 0, 0, 2) + ZEND_ARG_INFO(1, value) + ZEND_ARG_INFO(0, type) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_is_fault, 0, 0, 1) + ZEND_ARG_INFO(0, arg) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO(arginfo_xmlrpc_server_create, 0) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_destroy, 0, 0, 1) + ZEND_ARG_INFO(0, server) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_register_method, 0, 0, 3) + ZEND_ARG_INFO(0, server) + ZEND_ARG_INFO(0, method_name) + ZEND_ARG_INFO(0, function) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_call_method, 0, 0, 3) + ZEND_ARG_INFO(0, server) + ZEND_ARG_INFO(0, xml) + ZEND_ARG_INFO(0, user_data) + ZEND_ARG_INFO(0, output_options) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_parse_method_descriptions, 0, 0, 1) + ZEND_ARG_INFO(0, xml) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_add_introspection_data, 0, 0, 2) + ZEND_ARG_INFO(0, server) + ZEND_ARG_INFO(0, desc) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_register_introspection_callback, 0, 0, 2) + ZEND_ARG_INFO(0, server) + ZEND_ARG_INFO(0, function) +ZEND_END_ARG_INFO() +/* }}} */ + +const zend_function_entry xmlrpc_functions[] = { + PHP_FE(xmlrpc_encode, arginfo_xmlrpc_encode) + PHP_FE(xmlrpc_decode, arginfo_xmlrpc_decode) + PHP_FE(xmlrpc_decode_request, arginfo_xmlrpc_decode_request) + PHP_FE(xmlrpc_encode_request, arginfo_xmlrpc_encode_request) + PHP_FE(xmlrpc_get_type, arginfo_xmlrpc_encode) + PHP_FE(xmlrpc_set_type, arginfo_xmlrpc_set_type) + PHP_FE(xmlrpc_is_fault, arginfo_xmlrpc_is_fault) + PHP_FE(xmlrpc_server_create, arginfo_xmlrpc_server_create) + PHP_FE(xmlrpc_server_destroy, arginfo_xmlrpc_server_destroy) + PHP_FE(xmlrpc_server_register_method, arginfo_xmlrpc_server_register_method) + PHP_FE(xmlrpc_server_call_method, arginfo_xmlrpc_server_call_method) + PHP_FE(xmlrpc_parse_method_descriptions, arginfo_xmlrpc_parse_method_descriptions) + PHP_FE(xmlrpc_server_add_introspection_data, arginfo_xmlrpc_server_add_introspection_data) + PHP_FE(xmlrpc_server_register_introspection_callback, arginfo_xmlrpc_server_register_introspection_callback) + PHP_FE_END +}; + +zend_module_entry xmlrpc_module_entry = { + STANDARD_MODULE_HEADER, + "xmlrpc", + xmlrpc_functions, + PHP_MINIT(xmlrpc), + NULL, + NULL, + NULL, + PHP_MINFO(xmlrpc), + PHP_EXT_VERSION, + STANDARD_MODULE_PROPERTIES +}; + +#ifdef COMPILE_DL_XMLRPC +ZEND_GET_MODULE(xmlrpc) +#endif + +/******************************* +* local structures and defines * +*******************************/ + +/* per server data */ +typedef struct _xmlrpc_server_data { + zval* method_map; + zval* introspection_map; + XMLRPC_SERVER server_ptr; +} xmlrpc_server_data; + + +/* how to format output */ +typedef struct _php_output_options { + int b_php_out; + int b_auto_version; + STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out; +} php_output_options; + +/* data passed to C callback */ +typedef struct _xmlrpc_callback_data { + zval* xmlrpc_method; + zval* php_function; + zval* caller_params; + zval* return_data; + xmlrpc_server_data* server; + char php_executed; +} xmlrpc_callback_data; + +/* 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 VERSION_VALUE_SOAP11 "soap 1.1" +#define VERSION_VALUE_AUTO "auto" + +#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" +#define OBJECT_VALUE_TS_ATTR "timestamp" + +/* faults */ +#define FAULT_CODE "faultCode" +#define FAULT_CODE_LEN (sizeof(FAULT_CODE) - 1) +#define FAULT_STRING "faultString" +#define FAULT_STRING_LEN (sizeof(FAULT_STRING) - 1) + +/*********************** +* forward declarations * +***********************/ +XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval** newvalue); +static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data); +int sset_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type); +zval* decode_request_worker(char *xml_in, int xml_in_len, char *encoding_in, zval* method_name_out); +const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype); +XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str); +XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str); +int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type); + +/********************* +* startup / shutdown * +*********************/ + +static void destroy_server_data(xmlrpc_server_data *server TSRMLS_DC) +{ + 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 TSRMLS_DC) +{ + if (rsrc && rsrc->ptr) { + destroy_server_data((xmlrpc_server_data*) rsrc->ptr TSRMLS_CC); + } +} + +/* module init */ +PHP_MINIT_FUNCTION(xmlrpc) +{ + le_xmlrpc_server = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number); + + 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(); +} + +/******************* +* 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. + */ +#if 0 +static int add_long(zval* 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(zval* 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(zval* 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(zval* 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); +} + +#endif + +static int add_zval(zval* list, const char* id, zval** val) +{ + if (list && val) { + if (id) { + int id_len = strlen(id); + if (!(id_len > 1 && id[0] == '0') && is_numeric_string((char *)id, id_len, NULL, NULL, 0) == IS_LONG) { + long index = strtol(id, NULL, 0); + return zend_hash_index_update(Z_ARRVAL_P(list), index, (void *) val, sizeof(zval **), NULL); + } else { + return zend_hash_update(Z_ARRVAL_P(list), (char*) id, strlen(id) + 1, (void *) val, sizeof(zval **), NULL); + } + } else { + return zend_hash_next_index_insert(Z_ARRVAL_P(list), (void *) val, sizeof(zval **), NULL); + } + } + /* what is the correct return on error? */ + return 0; +} + +#define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0) + + +/************************* +* 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, zval* output_opts) +{ + if (options) { + + /* defaults */ + options->b_php_out = 0; + options->b_auto_version = 1; + 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 && Z_TYPE_P(output_opts) == IS_ARRAY) { + zval** val; + + /* type of output (xml/php) */ + if (zend_hash_find(Z_ARRVAL_P(output_opts), OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**) &val) == SUCCESS) { + if (Z_TYPE_PP(val) == IS_STRING) { + if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_PHP)) { + options->b_php_out = 1; + } else if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_XML)) { + options->b_php_out = 0; + } + } + } + + /* verbosity of generated xml */ + if (zend_hash_find(Z_ARRVAL_P(output_opts), VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, (void**) &val) == SUCCESS) { + if (Z_TYPE_PP(val) == IS_STRING) { + if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NO_WHITE_SPACE)) { + options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space; + } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NEWLINES_ONLY)) { + options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only; + } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_PRETTY)) { + options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty; + } + } + } + + /* version of xml to output */ + if (zend_hash_find(Z_ARRVAL_P(output_opts), VERSION_KEY, VERSION_KEY_LEN + 1, (void**) &val) == SUCCESS) { + if (Z_TYPE_PP(val) == IS_STRING) { + options->b_auto_version = 0; + if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_XMLRPC)) { + options->xmlrpc_out.version = xmlrpc_version_1_0; + } else if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_SIMPLE)) { + options->xmlrpc_out.version = xmlrpc_version_simple; + } else if (!strcmp((*val)->value.str.val, VERSION_VALUE_SOAP11)) { + options->xmlrpc_out.version = xmlrpc_version_soap_1_1; + } else { /* if(!strcmp((*val)->value.str.val, VERSION_VALUE_AUTO)) { */ + options->b_auto_version = 1; + } + } + } + + /* encoding code set */ + if (zend_hash_find(Z_ARRVAL_P(output_opts), ENCODING_KEY, ENCODING_KEY_LEN + 1, (void**)&val) == SUCCESS) { + if (Z_TYPE_PP(val) == IS_STRING) { + options->xmlrpc_out.xml_elem_opts.encoding = estrdup(Z_STRVAL_PP(val)); + } + } + + /* escaping options */ + if (zend_hash_find(Z_ARRVAL_P(output_opts), ESCAPING_KEY, ESCAPING_KEY_LEN + 1, (void**)&val) == SUCCESS) { + /* multiple values allowed. check if array */ + if (Z_TYPE_PP(val) == IS_ARRAY) { + zval** iter_val; + + zend_hash_internal_pointer_reset(Z_ARRVAL_PP(val)); + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping; + + while (1) { + if (zend_hash_get_current_data(Z_ARRVAL_PP(val), (void**)&iter_val) == SUCCESS) { + if (Z_TYPE_PP(iter_val) == IS_STRING && Z_STRVAL_PP(iter_val)) { + if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_CDATA)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping; + } else if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_ASCII)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping; + } else if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_PRINT)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping; + } else if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_MARKUP)) { + options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping; + } + } + } else { + break; + } + zend_hash_move_forward(Z_ARRVAL_PP(val)); + } + /* else, check for single value */ + } else if (Z_TYPE_PP(val) == IS_STRING) { + if (!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_CDATA)) { + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping; + } else if (!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_ASCII)) { + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping; + } else if (!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_PRINT)) { + options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping; + } else if (!strcmp(Z_STRVAL_PP(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, last_num = 0; + 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; + } else if (last_num > 0 && last_num != num_index-1) { + bStruct = 1; + break; + } + bArray = 1; + last_num = num_index; + } 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, zval* in_val, int depth TSRMLS_DC) +{ + XMLRPC_VALUE xReturn = NULL; + + if (in_val) { + zval* val = NULL; + XMLRPC_VALUE_TYPE type = get_zval_xmlrpc_type(in_val, &val); + + if (val) { + switch (type) { + case xmlrpc_base64: + if (Z_TYPE_P(val) == IS_NULL) { + xReturn = XMLRPC_CreateValueEmpty(); + XMLRPC_SetValueID(xReturn, key, 0); + } else { + xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val)); + } + break; + case xmlrpc_datetime: + convert_to_string(val); + xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL_P(val)); + break; + case xmlrpc_boolean: + convert_to_boolean(val); + xReturn = XMLRPC_CreateValueBoolean(key, Z_LVAL_P(val)); + break; + case xmlrpc_int: + convert_to_long(val); + xReturn = XMLRPC_CreateValueInt(key, Z_LVAL_P(val)); + break; + case xmlrpc_double: + convert_to_double(val); + xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL_P(val)); + break; + case xmlrpc_string: + convert_to_string(val); + xReturn = XMLRPC_CreateValueString(key, Z_STRVAL_P(val), Z_STRLEN_P(val)); + break; + case xmlrpc_vector: + { + unsigned long num_index; + zval** pIter; + char* my_key; + HashTable *ht = NULL; + zval *val_arr; + XMLRPC_VECTOR_TYPE vtype; + + ht = HASH_OF(val); + if (ht && ht->nApplyCount > 1) { + php_error_docref(NULL TSRMLS_CC, E_ERROR, "XML-RPC doesn't support circular references"); + return NULL; + } + + MAKE_STD_ZVAL(val_arr); + MAKE_COPY_ZVAL(&val, val_arr); + convert_to_array(val_arr); + + vtype = determine_vector_type(Z_ARRVAL_P(val_arr)); + xReturn = XMLRPC_CreateVector(key, vtype); + + zend_hash_internal_pointer_reset(Z_ARRVAL_P(val_arr)); + while(zend_hash_get_current_data(Z_ARRVAL_P(val_arr), (void**)&pIter) == SUCCESS) { + int res = my_zend_hash_get_current_key(Z_ARRVAL_P(val_arr), &my_key, &num_index); + + switch (res) { + case HASH_KEY_NON_EXISTANT: + break; + case HASH_KEY_IS_STRING: + case HASH_KEY_IS_LONG: + ht = HASH_OF(*pIter); + if (ht) { + ht->nApplyCount++; + } + if (res == HASH_KEY_IS_LONG) { + char *num_str = NULL; + + if (vtype != xmlrpc_vector_array) { + spprintf(&num_str, 0, "%ld", num_index); + } + XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(num_str, *pIter, depth++ TSRMLS_CC)); + if (num_str) { + efree(num_str); + } + } else { + XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(my_key, *pIter, depth++ TSRMLS_CC)); + } + if (ht) { + ht->nApplyCount--; + } + break; + } + zend_hash_move_forward(Z_ARRVAL_P(val_arr)); + } + zval_ptr_dtor(&val_arr); + } + break; + default: + break; + } + } + } + return xReturn; +} + +static XMLRPC_VALUE PHP_to_XMLRPC(zval* root_val TSRMLS_DC) +{ + return PHP_to_XMLRPC_worker(NULL, root_val, 0 TSRMLS_CC); +} + +/* recursively convert xmlrpc values into php values */ +static zval* XMLRPC_to_PHP(XMLRPC_VALUE el) +{ + zval* elem = NULL; + 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: + Z_TYPE_P(elem) = IS_NULL; + break; + case xmlrpc_string: + pStr = XMLRPC_GetValueString(el); + if (pStr) { + Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el); + Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem)); + Z_TYPE_P(elem) = IS_STRING; + } + break; + case xmlrpc_int: + Z_LVAL_P(elem) = XMLRPC_GetValueInt(el); + Z_TYPE_P(elem) = IS_LONG; + break; + case xmlrpc_boolean: + Z_LVAL_P(elem) = XMLRPC_GetValueBoolean(el); + Z_TYPE_P(elem) = IS_BOOL; + break; + case xmlrpc_double: + Z_DVAL_P(elem) = XMLRPC_GetValueDouble(el); + Z_TYPE_P(elem) = IS_DOUBLE; + break; + case xmlrpc_datetime: + Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el); + Z_STRVAL_P(elem) = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), Z_STRLEN_P(elem)); + Z_TYPE_P(elem) = IS_STRING; + break; + case xmlrpc_base64: + pStr = XMLRPC_GetValueBase64(el); + if (pStr) { + Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el); + Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem)); + Z_TYPE_P(elem) = IS_STRING; + } + break; + case xmlrpc_vector: + array_init(elem); + { + XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el); + + while( xIter ) { + zval *val = XMLRPC_to_PHP(xIter); + if (val) { + add_zval(elem, XMLRPC_GetValueID(xIter), &val); + } + xIter = XMLRPC_VectorNext(el); + } + } + break; + default: + break; + } + set_zval_xmlrpc_type(elem, type); + } + return elem; +} + +/* {{{ proto string xmlrpc_encode_request(string method, mixed params [, array output_options]) + Generates XML for a method request */ +PHP_FUNCTION(xmlrpc_encode_request) +{ + XMLRPC_REQUEST xRequest = NULL; + char *outBuf; + zval *vals, *out_opts = NULL; + char *method = NULL; + int method_len; + php_output_options out; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!z|a", &method, &method_len, &vals, &out_opts) == FAILURE) { + return; + } + + set_output_options(&out, out_opts ? out_opts : 0); + + if (return_value_used) { + xRequest = XMLRPC_RequestNew(); + + if (xRequest) { + XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out); + if (method == NULL) { + XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response); + } else { + XMLRPC_RequestSetMethodName(xRequest, method); + XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call); + } + if (Z_TYPE_P(vals) != IS_NULL) { + XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(vals TSRMLS_CC)); + } + + outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0); + if (outBuf) { + RETVAL_STRING(outBuf, 1); + free(outBuf); + } + XMLRPC_RequestFree(xRequest, 1); + } + } + + if (strcmp(out.xmlrpc_out.xml_elem_opts.encoding, ENCODING_DEFAULT) != 0) { + efree((char *)out.xmlrpc_out.xml_elem_opts.encoding); + } +} +/* }}} */ + +/* {{{ proto string xmlrpc_encode(mixed value) + Generates XML for a PHP value */ +PHP_FUNCTION(xmlrpc_encode) +{ + XMLRPC_VALUE xOut = NULL; + zval **arg1; + char *outBuf; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg1) == FAILURE) { + return; + } + + if (return_value_used) { + /* convert native php type to xmlrpc type */ + xOut = PHP_to_XMLRPC(*arg1 TSRMLS_CC); + + /* 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); + } + } +} +/* }}} */ + +zval* decode_request_worker(char *xml_in, int xml_in_len, char *encoding_in, zval* method_name_out) /* {{{ */ +{ + zval* retval = NULL; + XMLRPC_REQUEST response; + STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}}; + const char *method_name; + opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in) : ENCODING_DEFAULT; + + /* generate XMLRPC_REQUEST from raw xml */ + response = XMLRPC_REQUEST_FromXML(xml_in, xml_in_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) { + method_name = XMLRPC_RequestGetMethodName(response); + if (method_name) { + zval_dtor(method_name_out); + Z_TYPE_P(method_name_out) = IS_STRING; + Z_STRVAL_P(method_name_out) = estrdup(method_name); + Z_STRLEN_P(method_name_out) = strlen(Z_STRVAL_P(method_name_out)); + } else if (retval) { + zval_ptr_dtor(&retval); + retval = NULL; + } + } + } + + /* dust, sweep, and mop */ + XMLRPC_RequestFree(response, 1); + } + return retval; +} +/* }}} */ + +/* {{{ proto array xmlrpc_decode_request(string xml, string& method [, string encoding]) + Decodes XML into native PHP types */ +PHP_FUNCTION(xmlrpc_decode_request) +{ + char *xml, *encoding = NULL; + zval **method; + int xml_len, encoding_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sZ|s", &xml, &xml_len, &method, &encoding, &encoding_len) == FAILURE) { + return; + } + + + if (return_value_used) { + zval* retval = decode_request_worker(xml, xml_len, encoding_len ? encoding : NULL, *method); + if (retval) { + *return_value = *retval; + FREE_ZVAL(retval); + } + } +} +/* }}} */ + +/* {{{ proto array xmlrpc_decode(string xml [, string encoding]) + Decodes XML into native PHP types */ +PHP_FUNCTION(xmlrpc_decode) +{ + char *arg1, *arg2 = NULL; + int arg1_len, arg2_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &arg1, &arg1_len, &arg2, &arg2_len) == FAILURE) { + return; + } + + if (return_value_used) { + zval* retval = decode_request_worker(arg1, arg1_len, arg2_len ? arg2 : NULL, NULL); + if (retval) { + *return_value = *retval; + FREE_ZVAL(retval); + } + } +} +/* }}} */ + +/************************* +* server related methods * +*************************/ + +/* {{{ proto resource xmlrpc_server_create(void) + Creates an xmlrpc server */ +PHP_FUNCTION(xmlrpc_server_create) +{ + if (zend_parse_parameters_none() == FAILURE) { + return; + } + + if (return_value_used) { + zval *method_map, *introspection_map; + xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data)); + MAKE_STD_ZVAL(method_map); + MAKE_STD_ZVAL(introspection_map); + + array_init(method_map); + array_init(introspection_map); + + /* allocate server data. free'd in destroy_server_data() */ + 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, le_xmlrpc_server); + } +} +/* }}} */ + +/* {{{ proto int xmlrpc_server_destroy(resource server) + Destroys server resources */ +PHP_FUNCTION(xmlrpc_server_destroy) +{ + zval *arg1; + int bSuccess = FAILURE, type; + xmlrpc_server_data *server; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg1) == FAILURE) { + return; + } + + server = zend_list_find(Z_LVAL_P(arg1), &type); + + if (server && type == le_xmlrpc_server) { + bSuccess = zend_list_delete(Z_LVAL_P(arg1)); + + /* 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) /* {{{ */ +{ + xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data; + zval** php_function; + zval* xmlrpc_params; + zval* callback_params[3]; + TSRMLS_FETCH(); + + zval_dtor(pData->xmlrpc_method); + zval_dtor(pData->return_data); + + /* convert xmlrpc to native php types */ + ZVAL_STRING(pData->xmlrpc_method, XMLRPC_RequestGetMethodName(xRequest), 1); + xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest)); + + /* check if the called method has been previous registered */ + if(zend_hash_find(Z_ARRVAL_P(pData->server->method_map), + Z_STRVAL_P(pData->xmlrpc_method), + Z_STRLEN_P(pData->xmlrpc_method) + 1, + (void**)&php_function) == SUCCESS) { + + pData->php_function = *php_function; + } + + /* 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 TSRMLS_CC); + + pData->php_executed = 1; + + zval_ptr_dtor(&xmlrpc_params); + + return PHP_to_XMLRPC(pData->return_data TSRMLS_CC); +} +/* }}} */ + +/* 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) /* {{{ */ +{ + zval retval, **php_function; + zval *callback_params[1]; + char *php_function_name; + xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data; + TSRMLS_FETCH(); + + /* setup data hoojum */ + callback_params[0] = pData->caller_params; + + /* loop through and call all registered callbacks */ + zend_hash_internal_pointer_reset(Z_ARRVAL_P(pData->server->introspection_map)); + while (1) { + if (zend_hash_get_current_data(Z_ARRVAL_P(pData->server->introspection_map), (void**)&php_function) == SUCCESS) { + if (zend_is_callable(*php_function, 0, &php_function_name TSRMLS_CC)) { + /* php func prototype: function string user_func($user_params) */ + if (call_user_function(CG(function_table), NULL, *php_function, &retval, 1, callback_params TSRMLS_CC) == SUCCESS) { + XMLRPC_VALUE xData; + STRUCT_XMLRPC_ERROR err = {0}; + + /* return value should be a string */ + convert_to_string(&retval); + + xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL(retval), &err); + + if (xData) { + if (!XMLRPC_ServerAddIntrospectionData(server, xData)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", php_function_name); + } + XMLRPC_CleanupValue(xData); + } else { + /* could not create description */ + if (err.xml_elem_error.parser_code) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error: [line %ld, column %ld, 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_name); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s()", php_function_name); + } + } + zval_dtor(&retval); + } else { + /* user func failed */ + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error calling user introspection callback: %s()", php_function_name); + } + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid callback '%s' passed", php_function_name); + } + efree(php_function_name); + } else { + break; + } + zend_hash_move_forward(Z_ARRVAL_P(pData->server->introspection_map)); + } + + /* so we don't call the same callbacks ever again */ + zend_hash_clean(Z_ARRVAL_P(pData->server->introspection_map)); +} +/* }}} */ + +/* {{{ proto bool xmlrpc_server_register_method(resource server, string method_name, string function) + Register a PHP function to handle method matching method_name */ +PHP_FUNCTION(xmlrpc_server_register_method) +{ + char *method_key; + int method_key_len; + zval *handle, *method_name_save, **method_name; + int type; + xmlrpc_server_data* server; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsZ", &handle, &method_key, &method_key_len, &method_name) == FAILURE) { + return; + } + + server = zend_list_find(Z_LVAL_P(handle), &type); + + if (type == 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, php_xmlrpc_callback)) { + /* save for later use */ + ALLOC_ZVAL(method_name_save); + MAKE_COPY_ZVAL(method_name, method_name_save); + + /* register our php method */ + add_zval(server->method_map, method_key, &method_name_save); + + RETURN_BOOL(1); + } + } + RETURN_BOOL(0); +} +/* }}} */ + +/* {{{ proto bool xmlrpc_server_register_introspection_callback(resource server, string function) + Register a PHP function to generate documentation */ +PHP_FUNCTION(xmlrpc_server_register_introspection_callback) +{ + zval **method_name, *handle, *method_name_save; + int type; + xmlrpc_server_data* server; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rZ", &handle, &method_name) == FAILURE) { + return; + } + + server = zend_list_find(Z_LVAL_P(handle), &type); + + if (type == le_xmlrpc_server) { + /* save for later use */ + ALLOC_ZVAL(method_name_save); + MAKE_COPY_ZVAL(method_name, method_name_save); + + /* register our php method */ + add_zval(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(resource server, string xml, mixed user_data [, array output_options]) + Parses XML requests and call methods */ +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; + zval **caller_params, *handle, *output_opts = NULL; + char *rawxml; + int rawxml_len, type; + php_output_options out; + int argc =ZEND_NUM_ARGS(); + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsZ|a", &handle, &rawxml, &rawxml_len, &caller_params, &output_opts) != SUCCESS) { + return; + } + /* user output options */ + if (argc == 3) { + set_output_options(&out, NULL); + } else { + set_output_options(&out, output_opts); + } + + server = zend_list_find(Z_LVAL_P(handle), &type); + + if (type == 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, rawxml_len, &input_opts); + + if (xRequest) { + const char* methodname = XMLRPC_RequestGetMethodName(xRequest); + 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); + Z_TYPE_P(data.return_data) = IS_NULL; /* in case value is never init'd, we don't dtor to think it is a string or something */ + Z_TYPE_P(data.xmlrpc_method) = IS_NULL; + + /* setup some data to pass to the callback function */ + data.caller_params = *caller_params; + data.php_executed = 0; + data.server = server; + + /* 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 && 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 && !out.b_php_out && !xAnswer) { + xAnswer = PHP_to_XMLRPC(data.return_data TSRMLS_CC); + } + + /* 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; + + /* automagically determine output serialization type from request type */ + if (out.b_auto_version) { + XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest); + if (opts) { + out.xmlrpc_out.version = opts->version; + } + } + /* set some required request hoojum */ + XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out); + XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response); + XMLRPC_RequestSetData(xResponse, xAnswer); + XMLRPC_RequestSetMethodName(xResponse, methodname); + + /* 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); + } + } else { /* or as native php types? */ + *return_value = *data.return_data; + zval_copy_ctor(return_value); + } + + /* cleanup after ourselves. what a sty! */ + zval_ptr_dtor(&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(resource server, array desc) + Adds introspection documentation */ +PHP_FUNCTION(xmlrpc_server_add_introspection_data) +{ + zval *handle, *desc; + int type; + xmlrpc_server_data* server; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra", &handle, &desc) == FAILURE) { + return; + } + + server = zend_list_find(Z_LVAL_P(handle), &type); + + if (type == le_xmlrpc_server) { + XMLRPC_VALUE xDesc = PHP_to_XMLRPC(desc TSRMLS_CC); + 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) + Decodes XML into a list of method descriptions */ +PHP_FUNCTION(xmlrpc_parse_method_descriptions) +{ + zval *retval; + char *arg1; + int arg1_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg1, &arg1_len) == FAILURE) { + return; + } + + if (return_value_used) { + STRUCT_XMLRPC_ERROR err = {0}; + XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1, &err); + if (xVal) { + retval = XMLRPC_to_PHP(xVal); + + if (retval) { + RETVAL_ZVAL(retval, 1, 1); + } + /* dust, sweep, and mop */ + XMLRPC_CleanupValue(xVal); + } else { + /* could not create description */ + if (err.xml_elem_error.parser_code) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data", + err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid xml structure. Unable to create introspection data"); + } + + php_error_docref(NULL TSRMLS_CC, 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(void) /* {{{ */ +{ + 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_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype) /* {{{ */ +{ + int bSuccess = FAILURE; + TSRMLS_FETCH(); + + /* we only really care about strings because they can represent + * base64 and datetime. all other types have corresponding php types + */ + if (Z_TYPE_P(value) == IS_STRING) { + if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) { + const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none); + zval* type; + + MAKE_STD_ZVAL(type); + + Z_TYPE_P(type) = IS_STRING; + Z_STRVAL_P(type) = estrdup(typestr); + Z_STRLEN_P(type) = strlen(typestr); + + if (newtype == xmlrpc_datetime) { + XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, value->value.str.val); + if (v) { + time_t timestamp = (time_t) php_parse_date((char *)XMLRPC_GetValueDateTime_ISO8601(v), NULL); + if (timestamp != -1) { + zval* ztimestamp; + + MAKE_STD_ZVAL(ztimestamp); + + ztimestamp->type = IS_LONG; + ztimestamp->value.lval = timestamp; + + convert_to_object(value); + if (SUCCESS == zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL)) { + bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR), (void *) &ztimestamp, sizeof(zval *), NULL); + } + } else { + zval_ptr_dtor(&type); + } + XMLRPC_CleanupValue(v); + } else { + zval_ptr_dtor(&type); + } + } else { + convert_to_object(value); + bSuccess = zend_hash_update(Z_OBJPROP_P(value), 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_zval_xmlrpc_type(zval* value, zval** newvalue) /* {{{ */ +{ + XMLRPC_VALUE_TYPE type = xmlrpc_none; + TSRMLS_FETCH(); + + if (value) { + switch (Z_TYPE_P(value)) { + 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: + { + zval** attr; + type = xmlrpc_vector; + + if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**) &attr) == SUCCESS) { + if (Z_TYPE_PP(attr) == IS_STRING) { + type = xmlrpc_str_as_type(Z_STRVAL_PP(attr)); + } + } + break; + } + } + + /* if requested, return an unmolested (magic removed) copy of the value */ + if (newvalue) { + zval** val; + + if ((type == xmlrpc_base64 && Z_TYPE_P(value) != IS_NULL) || type == xmlrpc_datetime) { + if (zend_hash_find(Z_OBJPROP_P(value), 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) + Sets xmlrpc type, base64 or datetime, for a PHP string value */ +PHP_FUNCTION(xmlrpc_set_type) +{ + zval **arg; + char *type; + int type_len; + XMLRPC_VALUE_TYPE vtype; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zs", &arg, &type, &type_len) == FAILURE) { + return; + } + + vtype = xmlrpc_str_as_type(type); + if (vtype != xmlrpc_none) { + if (set_zval_xmlrpc_type(*arg, vtype) == SUCCESS) { + RETURN_TRUE; + } + } else { + zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type); + } + RETURN_FALSE; +} +/* }}} */ + +/* {{{ proto string xmlrpc_get_type(mixed value) + Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */ +PHP_FUNCTION(xmlrpc_get_type) +{ + zval **arg; + XMLRPC_VALUE_TYPE type; + XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) { + return; + } + + type = get_zval_xmlrpc_type(*arg, 0); + if (type == xmlrpc_vector) { + vtype = determine_vector_type((Z_TYPE_PP(arg) == IS_OBJECT) ? Z_OBJPROP_PP(arg) : Z_ARRVAL_PP(arg)); + } + + RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype), 1); +} +/* }}} */ + +/* {{{ proto bool xmlrpc_is_fault(array) + Determines if an array value represents an XMLRPC fault. */ +PHP_FUNCTION(xmlrpc_is_fault) +{ + zval *arg, **val; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arg) == FAILURE) { + return; + } + + /* The "correct" way to do this would be to call the xmlrpc + * library XMLRPC_ValueIsFault() func. However, doing that + * would require us to create an xmlrpc value from the php + * array, which is rather expensive, especially if it was + * a big array. Thus, we resort to this not so clever hackery. + */ + if (zend_hash_find(Z_ARRVAL_P(arg), FAULT_CODE, FAULT_CODE_LEN + 1, (void**) &val) == SUCCESS && + zend_hash_find(Z_ARRVAL_P(arg), FAULT_STRING, FAULT_STRING_LEN + 1, (void**) &val) == SUCCESS) { + RETURN_TRUE; + } + + RETURN_FALSE; +} +/* }}} */ + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + */ + |