Back to index

php5  5.3.10
xmlrpc-epi-php.c
Go to the documentation of this file.
00001 /*
00002   This file is part of, or distributed with, libXMLRPC - a C library for 
00003   xml-encoded function calls.
00004 
00005   Author: Dan Libby (dan@libby.com)
00006   Epinions.com may be contacted at feedback@epinions-inc.com
00007 */
00008 
00009 /*  
00010   Copyright 2001 Epinions, Inc. 
00011 
00012   Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
00013   of charge, to (a) use, copy, distribute, modify, perform and display this 
00014   software and associated documentation files (the "Software"), and (b) 
00015   permit others to whom the Software is furnished to do so as well.  
00016 
00017   1) The above copyright notice and this permission notice shall be included 
00018   without modification in all copies or substantial portions of the 
00019   Software.  
00020 
00021   2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
00022   ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
00023   IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
00024   PURPOSE OR NONINFRINGEMENT.  
00025 
00026   3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
00027   SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
00028   OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
00029   NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
00030   DAMAGES.    
00031 
00032 */
00033 
00034 /* auto-generated portions of this file are also subject to the php license */
00035 
00036 /*
00037    +----------------------------------------------------------------------+
00038    | PHP Version 5                                                        |
00039    +----------------------------------------------------------------------+
00040    | Copyright (c) 1997-2012 The PHP Group                                |
00041    +----------------------------------------------------------------------+
00042    | This source file is subject to version 3.01 of the PHP license,      |
00043    | that is bundled with this package in the file LICENSE, and is        |
00044    | available through the world-wide-web at the following url:           |
00045    | http://www.php.net/license/3_01.txt                                  |
00046    | If you did not receive a copy of the PHP license and are unable to   |
00047    | obtain it through the world-wide-web, please send a note to          |
00048    | license@php.net so we can mail you a copy immediately.               |
00049    +----------------------------------------------------------------------+
00050    | Author: Dan Libby                                                    |
00051    +----------------------------------------------------------------------+
00052  */
00053 
00054 /* $Id: xmlrpc-epi-php.c 321634 2012-01-01 13:15:04Z felipe $ */
00055 
00056 /**********************************************************************
00057 * BUGS:                                                               *
00058 *  - when calling a php user function, there appears to be no way to  *
00059 *    distinguish between a return value of null, and no return value  *
00060 *    at all.  The xml serialization layer(s) will then return a value *
00061 *    of null, when the right thing may be no value at all. (SOAP)     *
00062 **********************************************************************/
00063 
00064 #ifdef HAVE_CONFIG_H
00065 #include "config.h"
00066 #endif
00067 
00068 #include "php.h"
00069 #include "ext/standard/info.h"
00070 #include "ext/standard/php_string.h"
00071 #include "ext/date/php_date.h"
00072 #include "php_ini.h"
00073 #include "php_xmlrpc.h"
00074 #include "xmlrpc.h"
00075 
00076 #define PHP_EXT_VERSION "0.51"
00077 
00078 static int le_xmlrpc_server;
00079 
00080 /* {{{ arginfo */
00081 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_encode, 0, 0, 1)
00082        ZEND_ARG_INFO(0, value)
00083 ZEND_END_ARG_INFO()
00084 
00085 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_decode, 0, 0, 1)
00086        ZEND_ARG_INFO(0, value)
00087        ZEND_ARG_INFO(0, encoding)
00088 ZEND_END_ARG_INFO()
00089 
00090 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_decode_request, 0, 0, 2)
00091        ZEND_ARG_INFO(0, xml)
00092        ZEND_ARG_INFO(1, method)
00093        ZEND_ARG_INFO(0, encoding)
00094 ZEND_END_ARG_INFO()
00095 
00096 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_encode_request, 0, 0, 2)
00097        ZEND_ARG_INFO(0, method)
00098        ZEND_ARG_INFO(0, params)
00099        ZEND_ARG_INFO(0, output_options)
00100 ZEND_END_ARG_INFO()
00101 
00102 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_set_type, 0, 0, 2)
00103        ZEND_ARG_INFO(1, value)
00104        ZEND_ARG_INFO(0, type)
00105 ZEND_END_ARG_INFO()
00106 
00107 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_is_fault, 0, 0, 1)
00108        ZEND_ARG_INFO(0, arg)
00109 ZEND_END_ARG_INFO()
00110 
00111 ZEND_BEGIN_ARG_INFO(arginfo_xmlrpc_server_create, 0)
00112 ZEND_END_ARG_INFO()
00113 
00114 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_destroy, 0, 0, 1)
00115        ZEND_ARG_INFO(0, server)
00116 ZEND_END_ARG_INFO()
00117 
00118 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_register_method, 0, 0, 3)
00119        ZEND_ARG_INFO(0, server)
00120        ZEND_ARG_INFO(0, method_name)
00121        ZEND_ARG_INFO(0, function)
00122 ZEND_END_ARG_INFO()
00123 
00124 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_call_method, 0, 0, 3)
00125        ZEND_ARG_INFO(0, server)
00126        ZEND_ARG_INFO(0, xml)
00127        ZEND_ARG_INFO(0, user_data)
00128        ZEND_ARG_INFO(0, output_options)
00129 ZEND_END_ARG_INFO()
00130 
00131 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_parse_method_descriptions, 0, 0, 1)
00132        ZEND_ARG_INFO(0, xml)
00133 ZEND_END_ARG_INFO()
00134 
00135 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_add_introspection_data, 0, 0, 2)
00136        ZEND_ARG_INFO(0, server)
00137        ZEND_ARG_INFO(0, desc)
00138 ZEND_END_ARG_INFO()
00139 
00140 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_register_introspection_callback, 0, 0, 2)
00141        ZEND_ARG_INFO(0, server)
00142        ZEND_ARG_INFO(0, function)
00143 ZEND_END_ARG_INFO()
00144 /* }}} */
00145 
00146 const zend_function_entry xmlrpc_functions[] = {
00147        PHP_FE(xmlrpc_encode,                                                               arginfo_xmlrpc_encode) 
00148        PHP_FE(xmlrpc_decode,                                                               arginfo_xmlrpc_decode)
00149        PHP_FE(xmlrpc_decode_request,                                                arginfo_xmlrpc_decode_request)
00150        PHP_FE(xmlrpc_encode_request,                                                arginfo_xmlrpc_encode_request)
00151        PHP_FE(xmlrpc_get_type,                                                             arginfo_xmlrpc_encode)
00152        PHP_FE(xmlrpc_set_type,                                                             arginfo_xmlrpc_set_type)
00153        PHP_FE(xmlrpc_is_fault,                                                             arginfo_xmlrpc_is_fault)
00154        PHP_FE(xmlrpc_server_create,                                                 arginfo_xmlrpc_server_create)
00155        PHP_FE(xmlrpc_server_destroy,                                                arginfo_xmlrpc_server_destroy)
00156        PHP_FE(xmlrpc_server_register_method,                                 arginfo_xmlrpc_server_register_method)
00157        PHP_FE(xmlrpc_server_call_method,                                     arginfo_xmlrpc_server_call_method)
00158        PHP_FE(xmlrpc_parse_method_descriptions,                       arginfo_xmlrpc_parse_method_descriptions)
00159        PHP_FE(xmlrpc_server_add_introspection_data,                   arginfo_xmlrpc_server_add_introspection_data)
00160        PHP_FE(xmlrpc_server_register_introspection_callback,   arginfo_xmlrpc_server_register_introspection_callback)
00161        PHP_FE_END
00162 };
00163 
00164 zend_module_entry xmlrpc_module_entry = {
00165        STANDARD_MODULE_HEADER,
00166        "xmlrpc",
00167        xmlrpc_functions,
00168        PHP_MINIT(xmlrpc),
00169        NULL,
00170        NULL,
00171        NULL,
00172        PHP_MINFO(xmlrpc),
00173        PHP_EXT_VERSION,
00174        STANDARD_MODULE_PROPERTIES
00175 };
00176 
00177 #ifdef COMPILE_DL_XMLRPC
00178 ZEND_GET_MODULE(xmlrpc)
00179 #endif
00180 
00181 /*******************************
00182 * local structures and defines *
00183 *******************************/
00184 
00185 /* per server data */
00186 typedef struct _xmlrpc_server_data {
00187        zval* method_map;
00188        zval* introspection_map;
00189        XMLRPC_SERVER server_ptr;
00190 } xmlrpc_server_data;
00191 
00192 
00193 /* how to format output */
00194 typedef struct _php_output_options {
00195        int b_php_out;
00196        int b_auto_version;
00197        STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out;
00198 } php_output_options;
00199 
00200 /* data passed to C callback */
00201 typedef struct _xmlrpc_callback_data {
00202        zval* xmlrpc_method;
00203        zval* php_function;
00204        zval* caller_params;
00205        zval* return_data;
00206        xmlrpc_server_data* server;
00207        char php_executed;
00208 } xmlrpc_callback_data;
00209 
00210 /* output options */
00211 #define OUTPUT_TYPE_KEY       "output_type"
00212 #define OUTPUT_TYPE_KEY_LEN   (sizeof(OUTPUT_TYPE_KEY) - 1)
00213 #define OUTPUT_TYPE_VALUE_PHP "php"
00214 #define OUTPUT_TYPE_VALUE_XML "xml"
00215 
00216 #define VERBOSITY_KEY                  "verbosity"
00217 #define VERBOSITY_KEY_LEN              (sizeof(VERBOSITY_KEY) - 1)
00218 #define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space"
00219 #define VERBOSITY_VALUE_NEWLINES_ONLY  "newlines_only"
00220 #define VERBOSITY_VALUE_PRETTY         "pretty"
00221 
00222 #define ESCAPING_KEY             "escaping"
00223 #define ESCAPING_KEY_LEN         (sizeof(ESCAPING_KEY) - 1)
00224 #define ESCAPING_VALUE_CDATA     "cdata"
00225 #define ESCAPING_VALUE_NON_ASCII "non-ascii"
00226 #define ESCAPING_VALUE_NON_PRINT "non-print"
00227 #define ESCAPING_VALUE_MARKUP    "markup"
00228 
00229 #define VERSION_KEY          "version"
00230 #define VERSION_KEY_LEN      (sizeof(VERSION_KEY) - 1)
00231 #define VERSION_VALUE_SIMPLE "simple"
00232 #define VERSION_VALUE_XMLRPC "xmlrpc"
00233 #define VERSION_VALUE_SOAP11 "soap 1.1"
00234 #define VERSION_VALUE_AUTO   "auto"
00235 
00236 #define ENCODING_KEY     "encoding"
00237 #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1)
00238 #define ENCODING_DEFAULT "iso-8859-1"
00239 
00240 /* value types */
00241 #define OBJECT_TYPE_ATTR  "xmlrpc_type"
00242 #define OBJECT_VALUE_ATTR "scalar"
00243 #define OBJECT_VALUE_TS_ATTR "timestamp"
00244 
00245 /* faults */
00246 #define FAULT_CODE       "faultCode"
00247 #define FAULT_CODE_LEN   (sizeof(FAULT_CODE) - 1)
00248 #define FAULT_STRING     "faultString"
00249 #define FAULT_STRING_LEN (sizeof(FAULT_STRING) - 1)
00250 
00251 /***********************
00252 * forward declarations *
00253 ***********************/
00254 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval** newvalue);
00255 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data);
00256 int sset_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
00257 zval* decode_request_worker(char *xml_in, int xml_in_len, char *encoding_in, zval* method_name_out);
00258 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype);
00259 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str);
00260 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str);
00261 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
00262 
00263 /*********************
00264 * startup / shutdown *
00265 *********************/
00266 
00267 static void destroy_server_data(xmlrpc_server_data *server TSRMLS_DC)
00268 {
00269        if (server) {
00270               XMLRPC_ServerDestroy(server->server_ptr);
00271 
00272               zval_dtor(server->method_map);
00273               FREE_ZVAL(server->method_map);
00274 
00275               zval_dtor(server->introspection_map);
00276               FREE_ZVAL(server->introspection_map);
00277 
00278               efree(server);
00279        }
00280 }
00281 
00282 /* called when server is being destructed. either when xmlrpc_server_destroy
00283  * is called, or when request ends.  */
00284 static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
00285 {
00286        if (rsrc && rsrc->ptr) {
00287               destroy_server_data((xmlrpc_server_data*) rsrc->ptr TSRMLS_CC);
00288        }
00289 }
00290 
00291 /* module init */
00292 PHP_MINIT_FUNCTION(xmlrpc)
00293 {
00294        le_xmlrpc_server = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);
00295 
00296        return SUCCESS;
00297 }
00298 
00299 /* display info in phpinfo() */
00300 PHP_MINFO_FUNCTION(xmlrpc)
00301 {
00302        php_info_print_table_start();
00303        php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString());
00304        php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION);
00305        php_info_print_table_row(2, "author", "Dan Libby");
00306        php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net");
00307        php_info_print_table_row(2, "open sourced by", "Epinions.com");
00308        php_info_print_table_end();
00309 }
00310 
00311 /*******************
00312 * random utilities *
00313 *******************/
00314 
00315 /* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc).
00316  * Could easily be further generalized to work with objects.
00317  */
00318 #if 0
00319 static int add_long(zval* list, char* id, int num) {
00320        if(id) return add_assoc_long(list, id, num);
00321        else   return add_next_index_long(list, num);
00322 }
00323 
00324 static int add_double(zval* list, char* id, double num) {
00325        if(id) return add_assoc_double(list, id, num);
00326        else   return add_next_index_double(list, num);
00327 }
00328 
00329 static int add_string(zval* list, char* id, char* string, int duplicate) {
00330        if(id) return add_assoc_string(list, id, string, duplicate);
00331        else   return add_next_index_string(list, string, duplicate);
00332 }
00333 
00334 static int add_stringl(zval* list, char* id, char* string, uint length, int duplicate) {
00335        if(id) return add_assoc_stringl(list, id, string, length, duplicate);
00336        else   return add_next_index_stringl(list, string, length, duplicate);
00337 }
00338 
00339 #endif
00340 
00341 static int add_zval(zval* list, const char* id, zval** val)
00342 {
00343        if (list && val) {
00344               if (id) {
00345                      int id_len = strlen(id);
00346                      if (!(id_len > 1 && id[0] == '0') && is_numeric_string((char *)id, id_len, NULL, NULL, 0) == IS_LONG) {
00347                             long index = strtol(id, NULL, 0);
00348                             return zend_hash_index_update(Z_ARRVAL_P(list), index, (void *) val, sizeof(zval **), NULL);
00349                      } else {
00350                             return zend_hash_update(Z_ARRVAL_P(list), (char*) id, strlen(id) + 1, (void *) val, sizeof(zval **), NULL);
00351                      }
00352               } else {
00353                      return zend_hash_next_index_insert(Z_ARRVAL_P(list), (void *) val, sizeof(zval **), NULL); 
00354               }
00355        }
00356        /* what is the correct return on error? */
00357        return 0;
00358 }
00359 
00360 #define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0)
00361 
00362 
00363 /*************************
00364 * input / output options *
00365 *************************/
00366 
00367 /* parse an array (user input) into output options suitable for use by xmlrpc engine
00368  * and determine whether to return data as xml or php vars */
00369 static void set_output_options(php_output_options* options, zval* output_opts)
00370 {
00371        if (options) {
00372 
00373               /* defaults */
00374               options->b_php_out = 0;
00375               options->b_auto_version = 1;
00376               options->xmlrpc_out.version = xmlrpc_version_1_0;
00377               options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT;
00378               options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
00379               options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping;
00380 
00381               if (output_opts && Z_TYPE_P(output_opts) == IS_ARRAY) {
00382                      zval** val;
00383 
00384                      /* type of output (xml/php) */
00385                      if (zend_hash_find(Z_ARRVAL_P(output_opts), OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**) &val) == SUCCESS) {
00386                             if (Z_TYPE_PP(val) == IS_STRING) {
00387                                    if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_PHP)) {
00388                                           options->b_php_out = 1;
00389                                    } else if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_XML)) {
00390                                           options->b_php_out = 0;
00391                                    }
00392                             }
00393                      }
00394 
00395                      /* verbosity of generated xml */
00396                      if (zend_hash_find(Z_ARRVAL_P(output_opts), VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, (void**) &val) == SUCCESS) {
00397                             if (Z_TYPE_PP(val) == IS_STRING) {
00398                                    if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NO_WHITE_SPACE)) {
00399                                           options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space;
00400                                    } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NEWLINES_ONLY)) {
00401                                           options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only;
00402                                    } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_PRETTY)) {
00403                                           options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
00404                                    }
00405                             }
00406                      }
00407 
00408                      /* version of xml to output */
00409                      if (zend_hash_find(Z_ARRVAL_P(output_opts), VERSION_KEY, VERSION_KEY_LEN + 1, (void**) &val) == SUCCESS) {
00410                             if (Z_TYPE_PP(val) == IS_STRING) {
00411                                    options->b_auto_version = 0;
00412                                    if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_XMLRPC)) {
00413                                           options->xmlrpc_out.version = xmlrpc_version_1_0;
00414                                    } else if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_SIMPLE)) {
00415                                           options->xmlrpc_out.version = xmlrpc_version_simple;
00416                                    } else if (!strcmp((*val)->value.str.val, VERSION_VALUE_SOAP11)) {
00417                                                  options->xmlrpc_out.version = xmlrpc_version_soap_1_1;
00418                                    } else { /* if(!strcmp((*val)->value.str.val, VERSION_VALUE_AUTO)) { */
00419                                                  options->b_auto_version = 1;
00420                                    }
00421                             }
00422                      }
00423 
00424                      /* encoding code set */
00425                      if (zend_hash_find(Z_ARRVAL_P(output_opts), ENCODING_KEY, ENCODING_KEY_LEN + 1, (void**)&val) == SUCCESS) {
00426                             if (Z_TYPE_PP(val) == IS_STRING) {
00427                                    options->xmlrpc_out.xml_elem_opts.encoding = estrdup(Z_STRVAL_PP(val));
00428                             }
00429                      }
00430 
00431                      /* escaping options */
00432                      if (zend_hash_find(Z_ARRVAL_P(output_opts), ESCAPING_KEY, ESCAPING_KEY_LEN + 1, (void**)&val) == SUCCESS) {
00433                             /* multiple values allowed.  check if array */
00434                             if (Z_TYPE_PP(val) == IS_ARRAY) {
00435                                    zval** iter_val;
00436 
00437                                    zend_hash_internal_pointer_reset(Z_ARRVAL_PP(val));
00438                                    options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping;
00439 
00440                                    while (1) {
00441                                           if (zend_hash_get_current_data(Z_ARRVAL_PP(val), (void**)&iter_val) == SUCCESS) {
00442                                                  if (Z_TYPE_PP(iter_val) == IS_STRING && Z_STRVAL_PP(iter_val)) {
00443                                                         if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_CDATA)) {
00444                                                                options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping;
00445                                                         } else if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_ASCII)) {
00446                                                                options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping;
00447                                                         } else if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_PRINT)) {
00448                                                                options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping;
00449                                                         } else if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_MARKUP)) {
00450                                                                options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping;
00451                                                         }
00452                                                  }
00453                                           } else {
00454                                                  break;
00455                                           }
00456                                           zend_hash_move_forward(Z_ARRVAL_PP(val));
00457                                    }
00458                             /* else, check for single value */
00459                             } else if (Z_TYPE_PP(val) == IS_STRING) {
00460                                    if (!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_CDATA)) {
00461                                           options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping;
00462                                    } else if (!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_ASCII)) {
00463                                           options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping;
00464                                    } else if (!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_PRINT)) {
00465                                           options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping;
00466                                    } else if (!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_MARKUP)) {
00467                                           options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping;
00468                                    }
00469                             }
00470                      }
00471               }
00472        }
00473 }
00474 
00475 
00476 /******************
00477 * encode / decode *
00478 ******************/
00479 
00480 /* php arrays have no distinction between array and struct types.
00481  * they even allow mixed.  Thus, we determine the type by iterating
00482  * through the entire array and figuring out each element.
00483  * room for some optimation here if we stop after a specific # of elements.
00484  */
00485 static XMLRPC_VECTOR_TYPE determine_vector_type (HashTable *ht)
00486 {
00487        int bArray = 0, bStruct = 0, bMixed = 0;
00488        unsigned long num_index, last_num = 0;
00489        char* my_key;
00490 
00491        zend_hash_internal_pointer_reset(ht);
00492        while (1) {
00493               int res = my_zend_hash_get_current_key(ht, &my_key, &num_index);
00494               
00495               if (res == HASH_KEY_IS_LONG) {
00496                      if (bStruct) {
00497                             bMixed = 1;
00498                             break;
00499                      } else if (last_num > 0 && last_num != num_index-1) {
00500                             bStruct = 1;
00501                             break;
00502                      }
00503                      bArray = 1;
00504                      last_num = num_index;
00505               } else if (res == HASH_KEY_NON_EXISTANT) {
00506                      break;
00507               } else if (res == HASH_KEY_IS_STRING) {
00508                      if (bArray) {
00509                             bMixed = 1;
00510                             break;
00511                      }
00512                      bStruct = 1;
00513               }
00514               zend_hash_move_forward(ht);
00515        }
00516        return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array);
00517 }
00518 
00519 /* recursively convert php values into xmlrpc values */
00520 static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int depth TSRMLS_DC)
00521 {
00522        XMLRPC_VALUE xReturn = NULL;
00523 
00524        if (in_val) {
00525               zval* val = NULL;
00526               XMLRPC_VALUE_TYPE type = get_zval_xmlrpc_type(in_val, &val);
00527        
00528               if (val) {
00529                      switch (type) {
00530                             case xmlrpc_base64:
00531                                    if (Z_TYPE_P(val) == IS_NULL) {
00532                                           xReturn = XMLRPC_CreateValueEmpty();
00533                                           XMLRPC_SetValueID(xReturn, key, 0);
00534                                    } else {
00535                                           xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
00536                                    }
00537                                    break;
00538                             case xmlrpc_datetime:
00539                                    convert_to_string(val);
00540                                    xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL_P(val));
00541                                    break;
00542                             case xmlrpc_boolean:
00543                                    convert_to_boolean(val);
00544                                    xReturn = XMLRPC_CreateValueBoolean(key, Z_LVAL_P(val));
00545                                    break;
00546                             case xmlrpc_int:
00547                                    convert_to_long(val);
00548                                    xReturn = XMLRPC_CreateValueInt(key, Z_LVAL_P(val));
00549                                    break;
00550                             case xmlrpc_double:
00551                                    convert_to_double(val);
00552                                    xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL_P(val));
00553                                    break;
00554                             case xmlrpc_string:
00555                                    convert_to_string(val);
00556                                    xReturn = XMLRPC_CreateValueString(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
00557                                    break;
00558                             case xmlrpc_vector:
00559                                    {
00560                                           unsigned long num_index;
00561                                           zval** pIter;
00562                                           char* my_key;
00563                                           HashTable *ht = NULL;
00564                                           zval *val_arr;
00565                                           XMLRPC_VECTOR_TYPE vtype;
00566 
00567                                           ht = HASH_OF(val);
00568                                           if (ht && ht->nApplyCount > 1) {
00569                                                  php_error_docref(NULL TSRMLS_CC, E_ERROR, "XML-RPC doesn't support circular references");
00570                                                  return NULL;
00571                                           }
00572 
00573                                           MAKE_STD_ZVAL(val_arr);
00574                                           MAKE_COPY_ZVAL(&val, val_arr);
00575                                           convert_to_array(val_arr);
00576                                           
00577                                           vtype = determine_vector_type(Z_ARRVAL_P(val_arr));
00578                                           xReturn = XMLRPC_CreateVector(key, vtype);
00579 
00580                                           zend_hash_internal_pointer_reset(Z_ARRVAL_P(val_arr));
00581                                           while(zend_hash_get_current_data(Z_ARRVAL_P(val_arr), (void**)&pIter) == SUCCESS) {
00582                                                  int res = my_zend_hash_get_current_key(Z_ARRVAL_P(val_arr), &my_key, &num_index);
00583 
00584                                                  switch (res) {
00585                                                         case HASH_KEY_NON_EXISTANT:
00586                                                                break;
00587                                                         case HASH_KEY_IS_STRING:
00588                                                         case HASH_KEY_IS_LONG:
00589                                                                ht = HASH_OF(*pIter);
00590                                                                if (ht) {
00591                                                                       ht->nApplyCount++;
00592                                                                }
00593                                                                if (res == HASH_KEY_IS_LONG) {
00594                                                                       char *num_str = NULL;
00595                                                                       
00596                                                                       if (vtype != xmlrpc_vector_array) {
00597                                                                              spprintf(&num_str, 0, "%ld", num_index);
00598                                                                       }
00599                                                                       XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(num_str, *pIter, depth++ TSRMLS_CC));
00600                                                                       if (num_str) {
00601                                                                              efree(num_str);
00602                                                                       }
00603                                                                } else {
00604                                                                       XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(my_key, *pIter, depth++ TSRMLS_CC));
00605                                                                }
00606                                                                if (ht) {
00607                                                                       ht->nApplyCount--;
00608                                                                }
00609                                                                break;
00610                                                  }
00611                                                  zend_hash_move_forward(Z_ARRVAL_P(val_arr));
00612                                           }      
00613                                           zval_ptr_dtor(&val_arr);
00614                                    }
00615                                    break;
00616                             default:
00617                                    break;
00618                      }
00619               }
00620        }
00621        return xReturn;
00622 }
00623 
00624 static XMLRPC_VALUE PHP_to_XMLRPC(zval* root_val TSRMLS_DC)
00625 {
00626        return PHP_to_XMLRPC_worker(NULL, root_val, 0 TSRMLS_CC);
00627 }
00628 
00629 /* recursively convert xmlrpc values into php values */
00630 static zval* XMLRPC_to_PHP(XMLRPC_VALUE el)
00631 {
00632        zval* elem = NULL;
00633        const char* pStr;
00634 
00635        if (el) {
00636               XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
00637 
00638               MAKE_STD_ZVAL(elem); /* init. very important.  spent a frustrating day finding this out. */
00639 
00640               switch(type) {
00641                      case xmlrpc_empty:
00642                             Z_TYPE_P(elem) = IS_NULL;
00643                             break;
00644                      case xmlrpc_string:
00645                             pStr = XMLRPC_GetValueString(el);
00646                             if (pStr) {
00647                                    Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
00648                                    Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
00649                                    Z_TYPE_P(elem) = IS_STRING;
00650                             }
00651                             break;
00652                      case xmlrpc_int:
00653                             Z_LVAL_P(elem) = XMLRPC_GetValueInt(el);
00654                             Z_TYPE_P(elem) = IS_LONG;
00655                             break;
00656                      case xmlrpc_boolean:
00657                             Z_LVAL_P(elem) = XMLRPC_GetValueBoolean(el);
00658                             Z_TYPE_P(elem) = IS_BOOL;
00659                             break;
00660                      case xmlrpc_double:
00661                             Z_DVAL_P(elem) = XMLRPC_GetValueDouble(el);
00662                             Z_TYPE_P(elem) = IS_DOUBLE;
00663                             break;
00664                      case xmlrpc_datetime:
00665                             Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
00666                             Z_STRVAL_P(elem) = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), Z_STRLEN_P(elem));
00667                             Z_TYPE_P(elem) = IS_STRING;
00668                             break;
00669                      case xmlrpc_base64:
00670                             pStr = XMLRPC_GetValueBase64(el);
00671                             if (pStr) {
00672                                    Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
00673                                    Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
00674                                    Z_TYPE_P(elem) = IS_STRING;
00675                             }
00676                             break;
00677                      case xmlrpc_vector:
00678                             array_init(elem);
00679                             {
00680                                    XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
00681 
00682                                    while( xIter ) {
00683                                           zval *val = XMLRPC_to_PHP(xIter);
00684                                           if (val) {
00685                                                  add_zval(elem, XMLRPC_GetValueID(xIter), &val);
00686                                           }
00687                                           xIter = XMLRPC_VectorNext(el);
00688                                    }
00689                             }
00690                             break;
00691                      default:
00692                             break;
00693               }
00694               set_zval_xmlrpc_type(elem, type);
00695        }
00696        return elem;
00697 }
00698 
00699 /* {{{ proto string xmlrpc_encode_request(string method, mixed params [, array output_options])
00700    Generates XML for a method request */
00701 PHP_FUNCTION(xmlrpc_encode_request)
00702 {
00703        XMLRPC_REQUEST xRequest = NULL;
00704        char *outBuf;
00705        zval *vals, *out_opts = NULL;
00706        char *method = NULL;
00707        int method_len;
00708        php_output_options out;
00709 
00710        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!z|a", &method, &method_len, &vals, &out_opts) == FAILURE) {
00711               return;
00712        }
00713 
00714        set_output_options(&out, out_opts ? out_opts : 0);
00715 
00716        if (return_value_used) {
00717               xRequest = XMLRPC_RequestNew();
00718 
00719               if (xRequest) {
00720                      XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
00721                      if (method == NULL) {
00722                             XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
00723                      } else {
00724                             XMLRPC_RequestSetMethodName(xRequest, method);
00725                             XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
00726                      }
00727                      if (Z_TYPE_P(vals) != IS_NULL) {
00728                             XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(vals TSRMLS_CC));
00729                      }
00730 
00731                      outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
00732                      if (outBuf) {
00733                             RETVAL_STRING(outBuf, 1);
00734                             free(outBuf);
00735                      }
00736                      XMLRPC_RequestFree(xRequest, 1);
00737               }
00738        }
00739        
00740        if (strcmp(out.xmlrpc_out.xml_elem_opts.encoding, ENCODING_DEFAULT) != 0) {
00741               efree((char *)out.xmlrpc_out.xml_elem_opts.encoding);
00742        }
00743 }
00744 /* }}} */
00745 
00746 /* {{{ proto string xmlrpc_encode(mixed value)
00747    Generates XML for a PHP value */
00748 PHP_FUNCTION(xmlrpc_encode)
00749 {
00750        XMLRPC_VALUE xOut = NULL;
00751        zval **arg1;
00752        char *outBuf;
00753 
00754        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg1) == FAILURE) {
00755               return;
00756        }
00757 
00758        if (return_value_used) {
00759               /* convert native php type to xmlrpc type */
00760               xOut = PHP_to_XMLRPC(*arg1 TSRMLS_CC);
00761 
00762               /* generate raw xml from xmlrpc data */
00763               outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
00764 
00765               if (xOut) {
00766                      if (outBuf) {
00767                             RETVAL_STRING(outBuf, 1);
00768                             free(outBuf);
00769                      }
00770                      /* cleanup */
00771                      XMLRPC_CleanupValue(xOut);
00772               }
00773        }
00774 }
00775 /* }}} */
00776 
00777 zval* decode_request_worker(char *xml_in, int xml_in_len, char *encoding_in, zval* method_name_out) /* {{{ */
00778 {
00779        zval* retval = NULL;
00780        XMLRPC_REQUEST response;
00781        STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}};
00782        const char *method_name;
00783        opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in) : ENCODING_DEFAULT;
00784 
00785        /* generate XMLRPC_REQUEST from raw xml */
00786        response = XMLRPC_REQUEST_FromXML(xml_in, xml_in_len, &opts);
00787        if (response) {
00788               /* convert xmlrpc data to native php types */
00789               retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response));
00790 
00791               if (XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
00792                      if (method_name_out) {
00793                             method_name = XMLRPC_RequestGetMethodName(response);
00794                             if (method_name) {
00795                                    zval_dtor(method_name_out);
00796                                    Z_TYPE_P(method_name_out) = IS_STRING;
00797                                    Z_STRVAL_P(method_name_out) = estrdup(method_name);
00798                                    Z_STRLEN_P(method_name_out) = strlen(Z_STRVAL_P(method_name_out));
00799                             } else if (retval) {
00800                                    zval_ptr_dtor(&retval);
00801                                    retval = NULL;
00802                             }
00803                      }
00804               }
00805 
00806               /* dust, sweep, and mop */
00807               XMLRPC_RequestFree(response, 1);
00808        }
00809        return retval;
00810 }
00811 /* }}} */
00812 
00813 /* {{{ proto array xmlrpc_decode_request(string xml, string& method [, string encoding])
00814    Decodes XML into native PHP types */
00815 PHP_FUNCTION(xmlrpc_decode_request)
00816 {
00817        char *xml, *encoding = NULL;
00818        zval **method;
00819        int xml_len, encoding_len = 0;
00820 
00821        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sZ|s", &xml, &xml_len, &method, &encoding, &encoding_len) == FAILURE) {
00822               return;
00823        }
00824 
00825 
00826        if (return_value_used) {
00827               zval* retval = decode_request_worker(xml, xml_len, encoding_len ? encoding : NULL, *method);
00828               if (retval) {
00829                      *return_value = *retval;
00830                      FREE_ZVAL(retval);
00831               }
00832        }
00833 }
00834 /* }}} */
00835 
00836 /* {{{ proto array xmlrpc_decode(string xml [, string encoding])
00837    Decodes XML into native PHP types */
00838 PHP_FUNCTION(xmlrpc_decode)
00839 {
00840        char *arg1, *arg2 = NULL;
00841        int arg1_len, arg2_len = 0;
00842 
00843        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &arg1, &arg1_len, &arg2, &arg2_len) == FAILURE) {
00844               return;
00845        }
00846 
00847        if (return_value_used) {
00848               zval* retval = decode_request_worker(arg1, arg1_len, arg2_len ? arg2 : NULL, NULL);
00849               if (retval) {
00850                      *return_value = *retval;
00851                      FREE_ZVAL(retval);
00852               }
00853        }
00854 }
00855 /* }}} */
00856 
00857 /*************************
00858 * server related methods *
00859 *************************/
00860 
00861 /* {{{ proto resource xmlrpc_server_create(void)
00862    Creates an xmlrpc server */
00863 PHP_FUNCTION(xmlrpc_server_create)
00864 {
00865        if (zend_parse_parameters_none() == FAILURE) {
00866               return;
00867        }
00868 
00869        if (return_value_used) {
00870               zval *method_map, *introspection_map;
00871               xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
00872               MAKE_STD_ZVAL(method_map);
00873               MAKE_STD_ZVAL(introspection_map);
00874               
00875               array_init(method_map);
00876               array_init(introspection_map);
00877               
00878               /* allocate server data.  free'd in destroy_server_data() */
00879               server->method_map = method_map;
00880               server->introspection_map = introspection_map;
00881               server->server_ptr = XMLRPC_ServerCreate();
00882 
00883               XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
00884 
00885               /* store for later use */
00886               ZEND_REGISTER_RESOURCE(return_value,server, le_xmlrpc_server);
00887        }
00888 }
00889 /* }}} */
00890 
00891 /* {{{ proto int xmlrpc_server_destroy(resource server)
00892    Destroys server resources */
00893 PHP_FUNCTION(xmlrpc_server_destroy)
00894 {
00895        zval *arg1;
00896        int bSuccess = FAILURE, type;
00897        xmlrpc_server_data *server;
00898 
00899        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg1) == FAILURE) {
00900               return;
00901        }
00902 
00903        server = zend_list_find(Z_LVAL_P(arg1), &type);
00904 
00905        if (server && type == le_xmlrpc_server) {
00906               bSuccess = zend_list_delete(Z_LVAL_P(arg1));
00907 
00908               /* called by hashtable destructor
00909                * destroy_server_data(server);
00910                */
00911        }
00912        RETVAL_LONG(bSuccess == SUCCESS);
00913 }
00914 /* }}} */
00915            
00916 /* called by xmlrpc C engine as method handler for all registered methods.
00917  * it then calls the corresponding PHP function to handle the method.
00918  */
00919 static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data) /* {{{ */
00920 {
00921        xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
00922        zval** php_function;
00923        zval* xmlrpc_params;
00924        zval* callback_params[3];
00925        TSRMLS_FETCH();
00926 
00927        zval_dtor(pData->xmlrpc_method);
00928        zval_dtor(pData->return_data);
00929 
00930        /* convert xmlrpc to native php types */
00931        ZVAL_STRING(pData->xmlrpc_method, XMLRPC_RequestGetMethodName(xRequest), 1);
00932        xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest));
00933        
00934        /* check if the called method has been previous registered */
00935        if(zend_hash_find(Z_ARRVAL_P(pData->server->method_map),
00936                       Z_STRVAL_P(pData->xmlrpc_method), 
00937                       Z_STRLEN_P(pData->xmlrpc_method) + 1, 
00938                       (void**)&php_function) == SUCCESS) {
00939 
00940               pData->php_function = *php_function;
00941        }
00942 
00943        /* setup data hoojum */
00944        callback_params[0] = pData->xmlrpc_method;
00945        callback_params[1] = xmlrpc_params;
00946        callback_params[2] = pData->caller_params;
00947 
00948        /* Use same C function for all methods */
00949 
00950        /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
00951        call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params TSRMLS_CC);
00952 
00953        pData->php_executed = 1;
00954 
00955        zval_ptr_dtor(&xmlrpc_params);
00956 
00957        return PHP_to_XMLRPC(pData->return_data TSRMLS_CC);
00958 }
00959 /* }}} */
00960 
00961 /* called by the C server when it first receives an introspection request.  We pass this on to
00962  * our PHP listeners, if any
00963  */
00964 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) /* {{{ */
00965 {
00966        zval retval, **php_function;
00967        zval *callback_params[1];
00968        char *php_function_name;
00969        xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
00970        TSRMLS_FETCH();
00971 
00972        /* setup data hoojum */
00973        callback_params[0] = pData->caller_params;
00974 
00975        /* loop through and call all registered callbacks */
00976        zend_hash_internal_pointer_reset(Z_ARRVAL_P(pData->server->introspection_map));
00977        while (1) {
00978               if (zend_hash_get_current_data(Z_ARRVAL_P(pData->server->introspection_map), (void**)&php_function) == SUCCESS) {
00979                      if (zend_is_callable(*php_function, 0, &php_function_name TSRMLS_CC)) {
00980                             /* php func prototype: function string user_func($user_params) */
00981                             if (call_user_function(CG(function_table), NULL, *php_function, &retval, 1, callback_params TSRMLS_CC) == SUCCESS) {
00982                                    XMLRPC_VALUE xData;
00983                                    STRUCT_XMLRPC_ERROR err = {0};
00984 
00985                                    /* return value should be a string */
00986                                    convert_to_string(&retval);
00987 
00988                                    xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL(retval), &err);
00989 
00990                                    if (xData) {
00991                                           if (!XMLRPC_ServerAddIntrospectionData(server, xData)) {
00992                                                  php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", php_function_name);
00993                                           }
00994                                           XMLRPC_CleanupValue(xData);
00995                                    } else {
00996                                           /* could not create description */
00997                                           if (err.xml_elem_error.parser_code) {
00998                                                  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()", 
00999                                                         err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, php_function_name);
01000                                           } else {
01001                                                  php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s()", php_function_name);
01002                                           }
01003                                    }
01004                                    zval_dtor(&retval);
01005                             } else {
01006                                    /* user func failed */
01007                                    php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error calling user introspection callback: %s()", php_function_name);
01008                             }
01009                      } else {
01010                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid callback '%s' passed", php_function_name);
01011                      }
01012                      efree(php_function_name);
01013               } else {
01014                      break;
01015               }
01016               zend_hash_move_forward(Z_ARRVAL_P(pData->server->introspection_map));
01017        }
01018        
01019        /* so we don't call the same callbacks ever again */
01020        zend_hash_clean(Z_ARRVAL_P(pData->server->introspection_map));
01021 }
01022 /* }}} */
01023 
01024 /* {{{ proto bool xmlrpc_server_register_method(resource server, string method_name, string function)
01025    Register a PHP function to handle method matching method_name */
01026 PHP_FUNCTION(xmlrpc_server_register_method)
01027 {
01028        char *method_key;
01029        int method_key_len;
01030        zval *handle, *method_name_save, **method_name;
01031        int type;
01032        xmlrpc_server_data* server;
01033 
01034        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsZ", &handle, &method_key, &method_key_len, &method_name) == FAILURE) {
01035               return;
01036        }
01037 
01038        server = zend_list_find(Z_LVAL_P(handle), &type);
01039 
01040        if (type == le_xmlrpc_server) {
01041               /* register with C engine. every method just calls our standard callback, 
01042                * and it then dispatches to php as necessary
01043                */
01044               if (XMLRPC_ServerRegisterMethod(server->server_ptr, method_key, php_xmlrpc_callback)) {
01045                      /* save for later use */
01046                      MAKE_STD_ZVAL(method_name_save);
01047                      *method_name_save = **method_name;
01048                      zval_copy_ctor(method_name_save);
01049 
01050                      /* register our php method */
01051                      add_zval(server->method_map, method_key, &method_name_save);
01052 
01053                      RETURN_BOOL(1);
01054               }
01055        }
01056        RETURN_BOOL(0);
01057 }
01058 /* }}} */
01059 
01060 /* {{{ proto bool xmlrpc_server_register_introspection_callback(resource server, string function)
01061    Register a PHP function to generate documentation */
01062 PHP_FUNCTION(xmlrpc_server_register_introspection_callback)
01063 {
01064        zval **method_name, *handle, *method_name_save;
01065        int type;
01066        xmlrpc_server_data* server;
01067 
01068        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rZ", &handle, &method_name) == FAILURE) {
01069               return;
01070        }
01071 
01072        server = zend_list_find(Z_LVAL_P(handle), &type);
01073 
01074        if (type == le_xmlrpc_server) {
01075               /* save for later use */
01076               MAKE_STD_ZVAL(method_name_save);
01077               *method_name_save = **method_name;
01078               zval_copy_ctor(method_name_save);
01079 
01080               /* register our php method */
01081               add_zval(server->introspection_map, NULL, &method_name_save);
01082 
01083               RETURN_BOOL(1);
01084        }
01085        RETURN_BOOL(0);
01086 }
01087 /* }}} */
01088 
01089 /* this function is itchin for a re-write */
01090 
01091 /* {{{ proto mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])
01092    Parses XML requests and call methods */
01093 PHP_FUNCTION(xmlrpc_server_call_method)
01094 {
01095        xmlrpc_callback_data data = {0};
01096        XMLRPC_REQUEST xRequest;
01097        STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
01098        xmlrpc_server_data* server;
01099        zval **caller_params, *handle, *output_opts = NULL;
01100        char *rawxml;
01101        int rawxml_len, type;
01102        php_output_options out;
01103        int argc =ZEND_NUM_ARGS();
01104        
01105        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsZ|a", &handle, &rawxml, &rawxml_len, &caller_params, &output_opts) != SUCCESS) {
01106               return;
01107        }
01108        /* user output options */
01109        if (argc == 3) {
01110               set_output_options(&out, NULL);
01111        } else {
01112               set_output_options(&out, output_opts);
01113        }
01114 
01115        server = zend_list_find(Z_LVAL_P(handle), &type);
01116 
01117        if (type == le_xmlrpc_server) {
01118               /* HACK: use output encoding for now */
01119               input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
01120 
01121               /* generate an XMLRPC_REQUEST from the raw xml input */
01122               xRequest = XMLRPC_REQUEST_FromXML(rawxml, rawxml_len, &input_opts);
01123 
01124               if (xRequest) {
01125                      const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
01126                      XMLRPC_VALUE xAnswer = NULL;
01127                      MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important.  spent a frustrating day finding this out. */
01128                      MAKE_STD_ZVAL(data.return_data);
01129                      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 */
01130                      Z_TYPE_P(data.xmlrpc_method) = IS_NULL;
01131 
01132                      /* setup some data to pass to the callback function */
01133                      data.caller_params = *caller_params;
01134                      data.php_executed = 0;
01135                      data.server = server;
01136 
01137                      /* We could just call the php method directly ourselves at this point, but we do this 
01138                       * with a C callback in case the xmlrpc library ever implements some cool usage stats,
01139                       * or somesuch.
01140                       */
01141                      xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
01142                      if (xAnswer && out.b_php_out) {
01143                             zval_dtor(data.return_data);
01144                             FREE_ZVAL(data.return_data);
01145                             data.return_data = XMLRPC_to_PHP(xAnswer);
01146                      } else if (data.php_executed && !out.b_php_out && !xAnswer) {
01147                             xAnswer = PHP_to_XMLRPC(data.return_data TSRMLS_CC);
01148                      }
01149 
01150                      /* should we return data as xml? */
01151                      if (!out.b_php_out) {
01152                             XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
01153                             if (xResponse) {
01154                                    char *outBuf = 0;
01155                                    int buf_len = 0;
01156 
01157                                    /* automagically determine output serialization type from request type */
01158                                    if (out.b_auto_version) { 
01159                                           XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
01160                                           if (opts) {
01161                                                  out.xmlrpc_out.version = opts->version;
01162                                           }
01163                                    }
01164                                    /* set some required request hoojum */
01165                                    XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
01166                                    XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
01167                                    XMLRPC_RequestSetData(xResponse, xAnswer);
01168                                    XMLRPC_RequestSetMethodName(xResponse, methodname);
01169 
01170                                    /* generate xml */
01171                                    outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
01172                                    if (outBuf) {
01173                                           RETVAL_STRINGL(outBuf, buf_len, 1);
01174                                           free(outBuf);
01175                                    }
01176                                    /* cleanup after ourselves.  what a sty! */
01177                                    XMLRPC_RequestFree(xResponse, 0);
01178                             }
01179                      } else { /* or as native php types? */
01180                             *return_value = *data.return_data;
01181                             zval_copy_ctor(return_value);
01182                      }
01183 
01184                      /* cleanup after ourselves.  what a sty! */
01185                      zval_ptr_dtor(&data.xmlrpc_method);
01186 
01187                      zval_dtor(data.return_data);
01188                      FREE_ZVAL(data.return_data);
01189 
01190                      if (xAnswer) {
01191                             XMLRPC_CleanupValue(xAnswer);
01192                      }
01193 
01194                      XMLRPC_RequestFree(xRequest, 1);
01195               }
01196        }
01197 }
01198 /* }}} */
01199 
01200 /* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
01201    Adds introspection documentation  */
01202 PHP_FUNCTION(xmlrpc_server_add_introspection_data)
01203 {
01204        zval *handle, *desc;
01205        int type;
01206        xmlrpc_server_data* server;
01207 
01208        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra", &handle, &desc) == FAILURE) {
01209               return;
01210        }
01211 
01212        server = zend_list_find(Z_LVAL_P(handle), &type);
01213 
01214        if (type == le_xmlrpc_server) {
01215               XMLRPC_VALUE xDesc = PHP_to_XMLRPC(desc TSRMLS_CC);
01216               if (xDesc) {
01217                      int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
01218                      XMLRPC_CleanupValue(xDesc);
01219                      RETURN_LONG(retval);
01220               }
01221        }
01222        RETURN_LONG(0);
01223 }
01224 /* }}} */
01225 
01226 /* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
01227    Decodes XML into a list of method descriptions */
01228 PHP_FUNCTION(xmlrpc_parse_method_descriptions)
01229 {
01230        zval *retval;
01231        char *arg1;
01232        int arg1_len;
01233 
01234        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg1, &arg1_len) == FAILURE) {
01235               return;
01236        }
01237 
01238        if (return_value_used) {
01239               STRUCT_XMLRPC_ERROR err = {0};
01240               XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1, &err);
01241               if (xVal) {
01242                      retval = XMLRPC_to_PHP(xVal);
01243 
01244                      if (retval) {
01245                             *return_value = *retval;
01246                             zval_copy_ctor(return_value);
01247                      }
01248                      /* dust, sweep, and mop */
01249                      XMLRPC_CleanupValue(xVal);
01250               } else {
01251                      /* could not create description */
01252                      if (err.xml_elem_error.parser_code) {
01253                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data", 
01254                                           err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
01255                      } else {
01256                             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid xml structure. Unable to create introspection data");
01257                      }
01258 
01259                      php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error. no method description created");
01260               }
01261        }
01262 }
01263 /* }}} */
01264 
01265 /************
01266 * type data *
01267 ************/
01268 
01269 #define XMLRPC_TYPE_COUNT 9
01270 #define XMLRPC_VECTOR_TYPE_COUNT 4
01271 #define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
01272 
01273 /* return a string matching a given xmlrpc type */
01274 static const char** get_type_str_mapping(void) /* {{{ */
01275 {
01276        static const char* str_mapping[TYPE_STR_MAP_SIZE];
01277        static int first = 1;
01278        if (first) {
01279               /* warning. do not add/delete without changing size define */
01280               str_mapping[xmlrpc_none]     = "none";
01281               str_mapping[xmlrpc_empty]    = "empty";
01282               str_mapping[xmlrpc_base64]   = "base64";
01283               str_mapping[xmlrpc_boolean]  = "boolean";
01284               str_mapping[xmlrpc_datetime] = "datetime";
01285               str_mapping[xmlrpc_double]   = "double";
01286               str_mapping[xmlrpc_int]      = "int";
01287               str_mapping[xmlrpc_string]   = "string";
01288               str_mapping[xmlrpc_vector]   = "vector";
01289               str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none]   = "none";
01290               str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array]  = "array";
01291               str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed]  = "mixed";
01292               str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
01293               first = 0;
01294        }
01295        return (const char**)str_mapping;
01296 }
01297 /* }}} */
01298 
01299 /* map an xmlrpc type to a string */
01300 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) /* {{{ */
01301 {
01302        const char** str_mapping = get_type_str_mapping();
01303 
01304        if (vtype == xmlrpc_vector_none) {
01305               return str_mapping[type];
01306        } else {
01307               return str_mapping[XMLRPC_TYPE_COUNT + vtype];
01308        }
01309 }
01310 /* }}} */
01311 
01312 /* map a string to an xmlrpc type */
01313 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) /* {{{ */
01314 {
01315        const char** str_mapping = get_type_str_mapping();
01316        int i;
01317 
01318        if (str) {
01319               for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
01320                      if (!strcmp(str_mapping[i], str)) {
01321                             return (XMLRPC_VALUE_TYPE) i;
01322                      }
01323               }
01324        }
01325        return xmlrpc_none;
01326 }
01327 /* }}} */
01328 
01329 /* map a string to an xmlrpc vector type */
01330 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) /* {{{ */
01331 {
01332        const char** str_mapping = get_type_str_mapping();
01333        int i;
01334 
01335        if (str) {
01336               for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
01337                      if (!strcmp(str_mapping[i], str)) {
01338                             return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
01339                      }
01340               }
01341        }
01342        return xmlrpc_none;
01343 }
01344 /* }}} */
01345 
01346 /* set a given value to a particular type. 
01347  * note: this only works on strings, and only for date and base64,
01348  *       which do not have native php types. black magic lies herein.
01349  */
01350 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype) /* {{{ */
01351 {
01352        int bSuccess = FAILURE;
01353        TSRMLS_FETCH();
01354 
01355        /* we only really care about strings because they can represent
01356         * base64 and datetime.  all other types have corresponding php types
01357         */
01358        if (Z_TYPE_P(value) == IS_STRING) {
01359               if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
01360                      const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
01361                      zval* type;
01362 
01363                      MAKE_STD_ZVAL(type);
01364 
01365                      Z_TYPE_P(type) = IS_STRING;
01366                      Z_STRVAL_P(type) = estrdup(typestr);
01367                      Z_STRLEN_P(type) = strlen(typestr);
01368 
01369                      if (newtype == xmlrpc_datetime) {
01370                             XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, value->value.str.val);
01371                             if (v) {
01372                                    time_t timestamp = (time_t) php_parse_date((char *)XMLRPC_GetValueDateTime_ISO8601(v), NULL);
01373                                    if (timestamp != -1) {
01374                                           zval* ztimestamp;
01375 
01376                                           MAKE_STD_ZVAL(ztimestamp);
01377 
01378                                           ztimestamp->type = IS_LONG;
01379                                           ztimestamp->value.lval = timestamp;
01380 
01381                                           convert_to_object(value);
01382                                           if (SUCCESS == zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL)) {
01383                                                  bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR), (void *) &ztimestamp, sizeof(zval *), NULL);
01384                                           }
01385                                    } else {
01386                                           zval_ptr_dtor(&type);
01387                                    }
01388                                    XMLRPC_CleanupValue(v);
01389                             } else {
01390                                    zval_ptr_dtor(&type);
01391                             }
01392                      } else {
01393                             convert_to_object(value);
01394                             bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL);
01395                      }
01396               }
01397        }
01398 
01399        return bSuccess;
01400 }
01401 /* }}} */
01402 
01403 /* return xmlrpc type of a php value */
01404 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval** newvalue) /* {{{ */
01405 {
01406        XMLRPC_VALUE_TYPE type = xmlrpc_none;
01407        TSRMLS_FETCH();
01408 
01409        if (value) {
01410               switch (Z_TYPE_P(value)) {
01411                      case IS_NULL:
01412                             type = xmlrpc_base64;
01413                             break;
01414 #ifndef BOOL_AS_LONG
01415 
01416                      /* Right thing to do, but it breaks some legacy code. */
01417                      case IS_BOOL:
01418                             type = xmlrpc_boolean;
01419                             break;
01420 #else
01421                      case IS_BOOL:
01422 #endif
01423                      case IS_LONG:
01424                      case IS_RESOURCE:
01425                             type = xmlrpc_int;
01426                             break;
01427                      case IS_DOUBLE:
01428                             type = xmlrpc_double;
01429                             break;
01430                      case IS_CONSTANT:
01431                             type = xmlrpc_string;
01432                             break;
01433                      case IS_STRING:
01434                             type = xmlrpc_string;
01435                             break;
01436                      case IS_ARRAY:
01437                      case IS_CONSTANT_ARRAY:
01438                             type = xmlrpc_vector;
01439                             break;
01440                      case IS_OBJECT:
01441                             {
01442                                    zval** attr;
01443                                    type = xmlrpc_vector;
01444 
01445                                    if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**) &attr) == SUCCESS) {
01446                                           if (Z_TYPE_PP(attr) == IS_STRING) {
01447                                                  type = xmlrpc_str_as_type(Z_STRVAL_PP(attr));
01448                                           }
01449                                    }
01450                                    break;
01451                             }
01452               }
01453 
01454               /* if requested, return an unmolested (magic removed) copy of the value */
01455               if (newvalue) {
01456                      zval** val;
01457 
01458                      if ((type == xmlrpc_base64 && Z_TYPE_P(value) != IS_NULL) || type == xmlrpc_datetime) {
01459                             if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), (void**) &val) == SUCCESS) {
01460                                    *newvalue = *val;
01461                             }
01462                      } else {
01463                             *newvalue = value;
01464                      }
01465               }
01466        }
01467 
01468        return type;
01469 }
01470 /* }}} */
01471 
01472 /* {{{ proto bool xmlrpc_set_type(string value, string type)
01473    Sets xmlrpc type, base64 or datetime, for a PHP string value */
01474 PHP_FUNCTION(xmlrpc_set_type)
01475 {
01476        zval **arg;
01477        char *type;
01478        int type_len;
01479        XMLRPC_VALUE_TYPE vtype;
01480 
01481        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zs", &arg, &type, &type_len) == FAILURE) {
01482               return;
01483        }
01484 
01485        vtype = xmlrpc_str_as_type(type);
01486        if (vtype != xmlrpc_none) {
01487               if (set_zval_xmlrpc_type(*arg, vtype) == SUCCESS) {
01488                      RETURN_TRUE;
01489               }
01490        } else {
01491               zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type);
01492        }
01493        RETURN_FALSE;
01494 }
01495 /* }}} */
01496 
01497 /* {{{ proto string xmlrpc_get_type(mixed value)
01498    Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */
01499 PHP_FUNCTION(xmlrpc_get_type)
01500 {
01501        zval **arg;
01502        XMLRPC_VALUE_TYPE type;
01503        XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
01504 
01505        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
01506               return;
01507        }
01508 
01509        type = get_zval_xmlrpc_type(*arg, 0);
01510        if (type == xmlrpc_vector) {
01511               vtype = determine_vector_type((Z_TYPE_PP(arg) == IS_OBJECT) ? Z_OBJPROP_PP(arg) : Z_ARRVAL_PP(arg));
01512        }
01513    
01514        RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype), 1);
01515 }
01516 /* }}} */
01517 
01518 /* {{{ proto bool xmlrpc_is_fault(array)
01519    Determines if an array value represents an XMLRPC fault. */
01520 PHP_FUNCTION(xmlrpc_is_fault)
01521 {
01522        zval *arg, **val;
01523 
01524        if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arg) == FAILURE) {
01525               return;
01526        }
01527 
01528        /* The "correct" way to do this would be to call the xmlrpc
01529         * library XMLRPC_ValueIsFault() func.  However, doing that
01530         * would require us to create an xmlrpc value from the php
01531         * array, which is rather expensive, especially if it was
01532         * a big array.  Thus, we resort to this not so clever hackery.
01533         */
01534        if (zend_hash_find(Z_ARRVAL_P(arg), FAULT_CODE, FAULT_CODE_LEN + 1, (void**) &val) == SUCCESS && 
01535               zend_hash_find(Z_ARRVAL_P(arg), FAULT_STRING, FAULT_STRING_LEN + 1, (void**) &val) == SUCCESS) {
01536               RETURN_TRUE;
01537        }
01538 
01539        RETURN_FALSE;
01540 }
01541 /* }}} */
01542 
01543 /*
01544  * Local variables:
01545  * tab-width: 4
01546  * c-basic-offset: 4
01547  * End:
01548  */
01549