diff options
author | Dan Libby <danda@php.net> | 2001-09-06 04:13:30 +0000 |
---|---|---|
committer | Dan Libby <danda@php.net> | 2001-09-06 04:13:30 +0000 |
commit | 6883b9211652163ef3f50f448db2ddd8fae6ff99 (patch) | |
tree | dd1897cb279181689cd41c92318cfb8b30963a05 /ext/rpc/xmlrpc/xmlrpc-epi-php.c | |
parent | 903c831be5cd65e5070182a74f5336eba168807d (diff) | |
download | php-git-6883b9211652163ef3f50f448db2ddd8fae6ff99.tar.gz |
adding xmlrpc extension, per Stig's request
Diffstat (limited to 'ext/rpc/xmlrpc/xmlrpc-epi-php.c')
-rw-r--r-- | ext/rpc/xmlrpc/xmlrpc-epi-php.c | 1421 |
1 files changed, 1421 insertions, 0 deletions
diff --git a/ext/rpc/xmlrpc/xmlrpc-epi-php.c b/ext/rpc/xmlrpc/xmlrpc-epi-php.c new file mode 100644 index 0000000000..589adaad05 --- /dev/null +++ b/ext/rpc/xmlrpc/xmlrpc-epi-php.c @@ -0,0 +1,1421 @@ +/* + 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: + */ + |