root/ext/xmlrpc/xmlrpc-epi-php.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ZEND_GET_MODULE
  2. destroy_server_data
  3. xmlrpc_server_destructor
  4. PHP_MINIT_FUNCTION
  5. PHP_MINFO_FUNCTION
  6. add_long
  7. add_double
  8. add_string
  9. add_stringl
  10. add_zval
  11. set_output_options
  12. determine_vector_type
  13. PHP_to_XMLRPC_worker
  14. PHP_to_XMLRPC
  15. XMLRPC_to_PHP
  16. PHP_FUNCTION
  17. PHP_FUNCTION
  18. decode_request_worker
  19. PHP_FUNCTION
  20. PHP_FUNCTION
  21. PHP_FUNCTION
  22. PHP_FUNCTION
  23. php_xmlrpc_callback
  24. php_xmlrpc_introspection_callback
  25. PHP_FUNCTION
  26. PHP_FUNCTION
  27. PHP_FUNCTION
  28. PHP_FUNCTION
  29. PHP_FUNCTION
  30. get_type_str_mapping
  31. xmlrpc_type_as_str
  32. xmlrpc_str_as_type
  33. xmlrpc_str_as_vector_type
  34. set_zval_xmlrpc_type
  35. get_zval_xmlrpc_type
  36. PHP_FUNCTION
  37. PHP_FUNCTION
  38. PHP_FUNCTION

   1 /*
   2   This file is part of, or distributed with, libXMLRPC - a C library for
   3   xml-encoded function calls.
   4 
   5   Author: Dan Libby (dan@libby.com)
   6   Epinions.com may be contacted at feedback@epinions-inc.com
   7 */
   8 
   9 /*
  10   Copyright 2001 Epinions, Inc.
  11 
  12   Subject to the following 3 conditions, Epinions, Inc.  permits you, free
  13   of charge, to (a) use, copy, distribute, modify, perform and display this
  14   software and associated documentation files (the "Software"), and (b)
  15   permit others to whom the Software is furnished to do so as well.
  16 
  17   1) The above copyright notice and this permission notice shall be included
  18   without modification in all copies or substantial portions of the
  19   Software.
  20 
  21   2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF
  22   ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY
  23   IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR
  24   PURPOSE OR NONINFRINGEMENT.
  25 
  26   3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
  27   SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
  28   OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING
  29   NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH
  30   DAMAGES.
  31 
  32 */
  33 
  34 /* auto-generated portions of this file are also subject to the php license */
  35 
  36 /*
  37    +----------------------------------------------------------------------+
  38    | PHP Version 7                                                        |
  39    +----------------------------------------------------------------------+
  40    | Copyright (c) 1997-2016 The PHP Group                                |
  41    +----------------------------------------------------------------------+
  42    | This source file is subject to version 3.01 of the PHP license,      |
  43    | that is bundled with this package in the file LICENSE, and is        |
  44    | available through the world-wide-web at the following url:           |
  45    | http://www.php.net/license/3_01.txt                                  |
  46    | If you did not receive a copy of the PHP license and are unable to   |
  47    | obtain it through the world-wide-web, please send a note to          |
  48    | license@php.net so we can mail you a copy immediately.               |
  49    +----------------------------------------------------------------------+
  50    | Author: Dan Libby                                                    |
  51    +----------------------------------------------------------------------+
  52  */
  53 
  54 /* $Id$ */
  55 
  56 /**********************************************************************
  57 * BUGS:                                                               *
  58 *  - when calling a php user function, there appears to be no way to  *
  59 *    distinguish between a return value of null, and no return value  *
  60 *    at all.  The xml serialization layer(s) will then return a value *
  61 *    of null, when the right thing may be no value at all. (SOAP)     *
  62 **********************************************************************/
  63 
  64 #ifdef HAVE_CONFIG_H
  65 #include "config.h"
  66 #endif
  67 
  68 #include "php.h"
  69 #include "ext/standard/info.h"
  70 #include "ext/standard/php_string.h"
  71 #include "ext/date/php_date.h"
  72 #include "php_ini.h"
  73 #include "php_xmlrpc.h"
  74 #include "xmlrpc.h"
  75 
  76 static int le_xmlrpc_server;
  77 
  78 /* {{{ arginfo */
  79 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_encode, 0, 0, 1)
  80         ZEND_ARG_INFO(0, value)
  81 ZEND_END_ARG_INFO()
  82 
  83 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_decode, 0, 0, 1)
  84         ZEND_ARG_INFO(0, value)
  85         ZEND_ARG_INFO(0, encoding)
  86 ZEND_END_ARG_INFO()
  87 
  88 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_decode_request, 0, 0, 2)
  89         ZEND_ARG_INFO(0, xml)
  90         ZEND_ARG_INFO(1, method)
  91         ZEND_ARG_INFO(0, encoding)
  92 ZEND_END_ARG_INFO()
  93 
  94 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_encode_request, 0, 0, 2)
  95         ZEND_ARG_INFO(0, method)
  96         ZEND_ARG_INFO(0, params)
  97         ZEND_ARG_INFO(0, output_options)
  98 ZEND_END_ARG_INFO()
  99 
 100 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_set_type, 0, 0, 2)
 101         ZEND_ARG_INFO(1, value)
 102         ZEND_ARG_INFO(0, type)
 103 ZEND_END_ARG_INFO()
 104 
 105 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_is_fault, 0, 0, 1)
 106         ZEND_ARG_INFO(0, arg)
 107 ZEND_END_ARG_INFO()
 108 
 109 ZEND_BEGIN_ARG_INFO(arginfo_xmlrpc_server_create, 0)
 110 ZEND_END_ARG_INFO()
 111 
 112 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_destroy, 0, 0, 1)
 113         ZEND_ARG_INFO(0, server)
 114 ZEND_END_ARG_INFO()
 115 
 116 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_register_method, 0, 0, 3)
 117         ZEND_ARG_INFO(0, server)
 118         ZEND_ARG_INFO(0, method_name)
 119         ZEND_ARG_INFO(0, function)
 120 ZEND_END_ARG_INFO()
 121 
 122 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_call_method, 0, 0, 3)
 123         ZEND_ARG_INFO(0, server)
 124         ZEND_ARG_INFO(0, xml)
 125         ZEND_ARG_INFO(0, user_data)
 126         ZEND_ARG_INFO(0, output_options)
 127 ZEND_END_ARG_INFO()
 128 
 129 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_parse_method_descriptions, 0, 0, 1)
 130         ZEND_ARG_INFO(0, xml)
 131 ZEND_END_ARG_INFO()
 132 
 133 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_add_introspection_data, 0, 0, 2)
 134         ZEND_ARG_INFO(0, server)
 135         ZEND_ARG_INFO(0, desc)
 136 ZEND_END_ARG_INFO()
 137 
 138 ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_register_introspection_callback, 0, 0, 2)
 139         ZEND_ARG_INFO(0, server)
 140         ZEND_ARG_INFO(0, function)
 141 ZEND_END_ARG_INFO()
 142 /* }}} */
 143 
 144 const zend_function_entry xmlrpc_functions[] = {
 145         PHP_FE(xmlrpc_encode,                                                                   arginfo_xmlrpc_encode)
 146         PHP_FE(xmlrpc_decode,                                                                   arginfo_xmlrpc_decode)
 147         PHP_FE(xmlrpc_decode_request,                                                   arginfo_xmlrpc_decode_request)
 148         PHP_FE(xmlrpc_encode_request,                                                   arginfo_xmlrpc_encode_request)
 149         PHP_FE(xmlrpc_get_type,                                                                 arginfo_xmlrpc_encode)
 150         PHP_FE(xmlrpc_set_type,                                                                 arginfo_xmlrpc_set_type)
 151         PHP_FE(xmlrpc_is_fault,                                                                 arginfo_xmlrpc_is_fault)
 152         PHP_FE(xmlrpc_server_create,                                                    arginfo_xmlrpc_server_create)
 153         PHP_FE(xmlrpc_server_destroy,                                                   arginfo_xmlrpc_server_destroy)
 154         PHP_FE(xmlrpc_server_register_method,                                   arginfo_xmlrpc_server_register_method)
 155         PHP_FE(xmlrpc_server_call_method,                                               arginfo_xmlrpc_server_call_method)
 156         PHP_FE(xmlrpc_parse_method_descriptions,                                arginfo_xmlrpc_parse_method_descriptions)
 157         PHP_FE(xmlrpc_server_add_introspection_data,                    arginfo_xmlrpc_server_add_introspection_data)
 158         PHP_FE(xmlrpc_server_register_introspection_callback,   arginfo_xmlrpc_server_register_introspection_callback)
 159         PHP_FE_END
 160 };
 161 
 162 zend_module_entry xmlrpc_module_entry = {
 163         STANDARD_MODULE_HEADER,
 164         "xmlrpc",
 165         xmlrpc_functions,
 166         PHP_MINIT(xmlrpc),
 167         NULL,
 168         NULL,
 169         NULL,
 170         PHP_MINFO(xmlrpc),
 171         PHP_XMLRPC_VERSION,
 172         STANDARD_MODULE_PROPERTIES
 173 };
 174 
 175 #ifdef COMPILE_DL_XMLRPC
 176 ZEND_GET_MODULE(xmlrpc)
 177 #endif
 178 
 179 /*******************************
 180 * local structures and defines *
 181 *******************************/
 182 
 183 /* per server data */
 184 typedef struct _xmlrpc_server_data {
 185         zval method_map;
 186         zval introspection_map;
 187         XMLRPC_SERVER server_ptr;
 188 } xmlrpc_server_data;
 189 
 190 
 191 /* how to format output */
 192 typedef struct _php_output_options {
 193         int b_php_out;
 194         int b_auto_version;
 195         STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out;
 196 } php_output_options;
 197 
 198 /* data passed to C callback */
 199 typedef struct _xmlrpc_callback_data {
 200         zval xmlrpc_method;
 201         zval php_function;
 202         zval caller_params;
 203         zval return_data;
 204         xmlrpc_server_data* server;
 205         char php_executed;
 206 } xmlrpc_callback_data;
 207 
 208 /* output options */
 209 #define OUTPUT_TYPE_KEY       "output_type"
 210 #define OUTPUT_TYPE_KEY_LEN   (sizeof(OUTPUT_TYPE_KEY) - 1)
 211 #define OUTPUT_TYPE_VALUE_PHP "php"
 212 #define OUTPUT_TYPE_VALUE_XML "xml"
 213 
 214 #define VERBOSITY_KEY                  "verbosity"
 215 #define VERBOSITY_KEY_LEN              (sizeof(VERBOSITY_KEY) - 1)
 216 #define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space"
 217 #define VERBOSITY_VALUE_NEWLINES_ONLY  "newlines_only"
 218 #define VERBOSITY_VALUE_PRETTY         "pretty"
 219 
 220 #define ESCAPING_KEY             "escaping"
 221 #define ESCAPING_KEY_LEN         (sizeof(ESCAPING_KEY) - 1)
 222 #define ESCAPING_VALUE_CDATA     "cdata"
 223 #define ESCAPING_VALUE_NON_ASCII "non-ascii"
 224 #define ESCAPING_VALUE_NON_PRINT "non-print"
 225 #define ESCAPING_VALUE_MARKUP    "markup"
 226 
 227 #define VERSION_KEY          "version"
 228 #define VERSION_KEY_LEN      (sizeof(VERSION_KEY) - 1)
 229 #define VERSION_VALUE_SIMPLE "simple"
 230 #define VERSION_VALUE_XMLRPC "xmlrpc"
 231 #define VERSION_VALUE_SOAP11 "soap 1.1"
 232 #define VERSION_VALUE_AUTO   "auto"
 233 
 234 #define ENCODING_KEY     "encoding"
 235 #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1)
 236 #define ENCODING_DEFAULT "iso-8859-1"
 237 
 238 /* value types */
 239 #define OBJECT_TYPE_ATTR  "xmlrpc_type"
 240 #define OBJECT_VALUE_ATTR "scalar"
 241 #define OBJECT_VALUE_TS_ATTR "timestamp"
 242 
 243 /* faults */
 244 #define FAULT_CODE       "faultCode"
 245 #define FAULT_CODE_LEN   (sizeof(FAULT_CODE) - 1)
 246 #define FAULT_STRING     "faultString"
 247 #define FAULT_STRING_LEN (sizeof(FAULT_STRING) - 1)
 248 
 249 /***********************
 250 * forward declarations *
 251 ***********************/
 252 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval* newvalue);
 253 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data);
 254 int sset_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
 255 void decode_request_worker(char *xml_in, int xml_in_len, char *encoding_in, zval* method_name_out, zval *retval);
 256 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype);
 257 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str);
 258 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str);
 259 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
 260 
 261 /*********************
 262 * startup / shutdown *
 263 *********************/
 264 
 265 static void destroy_server_data(xmlrpc_server_data *server)
 266 {
 267         if (server) {
 268                 XMLRPC_ServerDestroy(server->server_ptr);
 269 
 270                 zval_ptr_dtor(&server->method_map);
 271                 zval_ptr_dtor(&server->introspection_map);
 272 
 273                 efree(server);
 274         }
 275 }
 276 
 277 /* called when server is being destructed. either when xmlrpc_server_destroy
 278  * is called, or when request ends.  */
 279 static void xmlrpc_server_destructor(zend_resource *rsrc)
 280 {
 281         if (rsrc && rsrc->ptr) {
 282                 rsrc->gc.refcount++;
 283                 destroy_server_data((xmlrpc_server_data*) rsrc->ptr);
 284                 rsrc->gc.refcount--;
 285         }
 286 }
 287 
 288 /* module init */
 289 PHP_MINIT_FUNCTION(xmlrpc)
 290 {
 291         le_xmlrpc_server = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);
 292 
 293         return SUCCESS;
 294 }
 295 
 296 /* display info in phpinfo() */
 297 PHP_MINFO_FUNCTION(xmlrpc)
 298 {
 299         php_info_print_table_start();
 300         php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString());
 301         php_info_print_table_row(2, "php extension version", PHP_XMLRPC_VERSION);
 302         php_info_print_table_row(2, "author", "Dan Libby");
 303         php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net");
 304         php_info_print_table_row(2, "open sourced by", "Epinions.com");
 305         php_info_print_table_end();
 306 }
 307 
 308 /*******************
 309 * random utilities *
 310 *******************/
 311 
 312 /* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc).
 313  * Could easily be further generalized to work with objects.
 314  */
 315 #if 0
 316 static int add_long(zval* list, char* id, int num) {
 317         if(id) return add_assoc_long(list, id, num);
 318         else   return add_next_index_long(list, num);
 319 }
 320 
 321 static int add_double(zval* list, char* id, double num) {
 322         if(id) return add_assoc_double(list, id, num);
 323         else   return add_next_index_double(list, num);
 324 }
 325 
 326 static int add_string(zval* list, char* id, char* string) {
 327         if(id) return add_assoc_string(list, id, string);
 328         else   return add_next_index_string(list, string);
 329 }
 330 
 331 static int add_stringl(zval* list, char* id, char* string, uint length) {
 332         if(id) return add_assoc_stringl(list, id, string, length);
 333         else   return add_next_index_stringl(list, string, length);
 334 }
 335 
 336 #endif
 337 
 338 static void add_zval(zval* list, const char* id, zval* val)
 339 {
 340         if (list && val) {
 341                 if (id) {
 342                         int id_len = strlen(id);
 343                         if (!(id_len > 1 && id[0] == '0') && is_numeric_string((char *)id, id_len, NULL, NULL, 0) == IS_LONG) {
 344                                 long index = strtol(id, NULL, 0);
 345                                 zend_hash_index_update(Z_ARRVAL_P(list), index, val);
 346                         } else {
 347                                 zend_hash_str_update(Z_ARRVAL_P(list), (char*)id, strlen(id), val);
 348                         }
 349                 } else {
 350                         zend_hash_next_index_insert(Z_ARRVAL_P(list), val);
 351                 }
 352         }
 353 }
 354 
 355 /*************************
 356 * input / output options *
 357 *************************/
 358 
 359 /* parse an array (user input) into output options suitable for use by xmlrpc engine
 360  * and determine whether to return data as xml or php vars */
 361 static void set_output_options(php_output_options* options, zval* output_opts)
 362 {
 363         if (options) {
 364                 /* defaults */
 365                 options->b_php_out = 0;
 366                 options->b_auto_version = 1;
 367                 options->xmlrpc_out.version = xmlrpc_version_1_0;
 368                 options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT;
 369                 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
 370                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping;
 371 
 372                 if (output_opts && Z_TYPE_P(output_opts) == IS_ARRAY) {
 373                         zval* val;
 374 
 375                         /* type of output (xml/php) */
 376                         if ((val = zend_hash_str_find(Z_ARRVAL_P(output_opts), OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN)) != NULL) {
 377                                 if (Z_TYPE_P(val) == IS_STRING) {
 378                                         if (!strcmp(Z_STRVAL_P(val), OUTPUT_TYPE_VALUE_PHP)) {
 379                                                 options->b_php_out = 1;
 380                                         } else if (!strcmp(Z_STRVAL_P(val), OUTPUT_TYPE_VALUE_XML)) {
 381                                                 options->b_php_out = 0;
 382                                         }
 383                                 }
 384                         }
 385 
 386                         /* verbosity of generated xml */
 387                         if ((val = zend_hash_str_find(Z_ARRVAL_P(output_opts), VERBOSITY_KEY, VERBOSITY_KEY_LEN)) != NULL) {
 388                                 if (Z_TYPE_P(val) == IS_STRING) {
 389                                         if (!strcmp(Z_STRVAL_P(val), VERBOSITY_VALUE_NO_WHITE_SPACE)) {
 390                                                 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space;
 391                                         } else if (!strcmp(Z_STRVAL_P(val), VERBOSITY_VALUE_NEWLINES_ONLY)) {
 392                                                 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only;
 393                                         } else if (!strcmp(Z_STRVAL_P(val), VERBOSITY_VALUE_PRETTY)) {
 394                                                 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
 395                                         }
 396                                 }
 397                         }
 398 
 399                         /* version of xml to output */
 400                         if ((val = zend_hash_str_find(Z_ARRVAL_P(output_opts), VERSION_KEY, VERSION_KEY_LEN)) != NULL) {
 401                                 if (Z_TYPE_P(val) == IS_STRING) {
 402                                         options->b_auto_version = 0;
 403                                         if (!strcmp(Z_STRVAL_P(val), VERSION_VALUE_XMLRPC)) {
 404                                                 options->xmlrpc_out.version = xmlrpc_version_1_0;
 405                                         } else if (!strcmp(Z_STRVAL_P(val), VERSION_VALUE_SIMPLE)) {
 406                                                 options->xmlrpc_out.version = xmlrpc_version_simple;
 407                                         } else if (!strcmp(Z_STRVAL_P(val), VERSION_VALUE_SOAP11)) {
 408                                                 options->xmlrpc_out.version = xmlrpc_version_soap_1_1;
 409                                         } else { /* if(!strcmp(Z_STRVAL_P(val), VERSION_VALUE_AUTO)) { */
 410                                                 options->b_auto_version = 1;
 411                                         }
 412                                 }
 413 
 414                         }
 415 
 416                         /* encoding code set */
 417                         if ((val = zend_hash_str_find(Z_ARRVAL_P(output_opts), ENCODING_KEY, ENCODING_KEY_LEN)) != NULL) {
 418                                 if (Z_TYPE_P(val) == IS_STRING) {
 419                                         options->xmlrpc_out.xml_elem_opts.encoding = estrdup(Z_STRVAL_P(val));
 420                                 }
 421                         }
 422 
 423                         /* escaping options */
 424                         if ((val = zend_hash_str_find(Z_ARRVAL_P(output_opts), ESCAPING_KEY, ESCAPING_KEY_LEN)) != NULL) {
 425                                 /* multiple values allowed.  check if array */
 426                                 if (Z_TYPE_P(val) == IS_ARRAY) {
 427                                         zval* iter_val;
 428 
 429                                         options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping;
 430 
 431                                         ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(val), iter_val) {
 432                                                 if (Z_TYPE_P(iter_val) == IS_STRING && Z_STRVAL_P(iter_val)) {
 433                                                         if (!strcmp(Z_STRVAL_P(iter_val), ESCAPING_VALUE_CDATA)) {
 434                                                                 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping;
 435                                                         } else if (!strcmp(Z_STRVAL_P(iter_val), ESCAPING_VALUE_NON_ASCII)) {
 436                                                                 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping;
 437                                                         } else if (!strcmp(Z_STRVAL_P(iter_val), ESCAPING_VALUE_NON_PRINT)) {
 438                                                                 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping;
 439                                                         } else if (!strcmp(Z_STRVAL_P(iter_val), ESCAPING_VALUE_MARKUP)) {
 440                                                                 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping;
 441                                                         }
 442                                                 }
 443                                         } ZEND_HASH_FOREACH_END();
 444                                         /* else, check for single value */
 445                                 } else if (Z_TYPE_P(val) == IS_STRING) {
 446                                         if (!strcmp(Z_STRVAL_P(val), ESCAPING_VALUE_CDATA)) {
 447                                                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping;
 448                                         } else if (!strcmp(Z_STRVAL_P(val), ESCAPING_VALUE_NON_ASCII)) {
 449                                                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping;
 450                                         } else if (!strcmp(Z_STRVAL_P(val), ESCAPING_VALUE_NON_PRINT)) {
 451                                                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping;
 452                                         } else if (!strcmp(Z_STRVAL_P(val), ESCAPING_VALUE_MARKUP)) {
 453                                                 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping;
 454                                         }
 455                                 }
 456                         }
 457                 }
 458         }
 459 }
 460 
 461 
 462 /******************
 463 * encode / decode *
 464 ******************/
 465 
 466 /* php arrays have no distinction between array and struct types.
 467  * they even allow mixed.  Thus, we determine the type by iterating
 468  * through the entire array and figuring out each element.
 469  * room for some optimation here if we stop after a specific # of elements.
 470  */
 471 static XMLRPC_VECTOR_TYPE determine_vector_type (HashTable *ht)
 472 {
 473         int bArray = 0, bStruct = 0, bMixed = 0;
 474         zend_ulong num_index, last_num = 0;
 475         zend_string* my_key;
 476 
 477         ZEND_HASH_FOREACH_KEY(ht, num_index, my_key) {
 478                 if (my_key == NULL) {
 479                         if (bStruct) {
 480                                 bMixed = 1;
 481                                 break;
 482                         } else if (last_num > 0 && last_num != num_index-1) {
 483                                 bStruct = 1;
 484                                 break;
 485                         }
 486                         bArray = 1;
 487                         last_num = num_index;
 488                 } else {
 489                         if (bArray) {
 490                                 bMixed = 1;
 491                                 break;
 492                         }
 493                         bStruct = 1;
 494                 }
 495         } ZEND_HASH_FOREACH_END();
 496         return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array);
 497 }
 498 
 499 /* recursively convert php values into xmlrpc values */
 500 static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int depth)
 501 {
 502         XMLRPC_VALUE xReturn = NULL;
 503 
 504         if (in_val) {
 505                 zval val;
 506                 XMLRPC_VALUE_TYPE type;
 507 
 508                 ZVAL_UNDEF(&val);
 509                 type = get_zval_xmlrpc_type(in_val, &val);
 510 
 511                 if (!Z_ISUNDEF(val)) {
 512                         switch (type) {
 513                                 case xmlrpc_base64:
 514                                         if (Z_TYPE(val) == IS_NULL) {
 515                                                 xReturn = XMLRPC_CreateValueEmpty();
 516                                                 XMLRPC_SetValueID(xReturn, key, 0);
 517                                         } else {
 518                                                 if (Z_TYPE(val) != IS_STRING) {
 519                                                         zval newvalue;
 520                                                         ZVAL_DUP(&newvalue, &val);
 521                                                         convert_to_string(&newvalue);
 522                                                         xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL(newvalue), Z_STRLEN(newvalue));
 523                                                         zval_dtor(&newvalue);
 524                                                 } else {
 525                                                         xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL(val), Z_STRLEN(val));
 526                                                 }
 527                                         }
 528                                         break;
 529                                 case xmlrpc_datetime:
 530                                         convert_to_string(&val);
 531                                         xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL(val));
 532                                         break;
 533                                 case xmlrpc_boolean:
 534                                         convert_to_boolean(&val);
 535                                         xReturn = XMLRPC_CreateValueBoolean(key, Z_TYPE(val) == IS_TRUE);
 536                                         break;
 537                                 case xmlrpc_int:
 538                                         convert_to_long(&val);
 539                                         xReturn = XMLRPC_CreateValueInt(key, Z_LVAL(val));
 540                                         break;
 541                                 case xmlrpc_double:
 542                                         convert_to_double(&val);
 543                                         xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL(val));
 544                                         break;
 545                                 case xmlrpc_string:
 546                                         convert_to_string(&val);
 547                                         xReturn = XMLRPC_CreateValueString(key, Z_STRVAL(val), Z_STRLEN(val));
 548                                         break;
 549                                 case xmlrpc_vector:
 550                                         {
 551                                                 zend_ulong num_index;
 552                                                 zval* pIter;
 553                                                 zend_string* my_key;
 554                                                 HashTable *ht = NULL;
 555                                                 zval val_arr;
 556                                                 XMLRPC_VECTOR_TYPE vtype;
 557 
 558                                                 ht = HASH_OF(&val);
 559                                                 if (ht && ht->u.v.nApplyCount > 1) {
 560                                                         php_error_docref(NULL, E_ERROR, "XML-RPC doesn't support circular references");
 561                                                         return NULL;
 562                                                 }
 563 
 564                                                 ZVAL_COPY(&val_arr, &val);
 565                                                 convert_to_array(&val_arr);
 566 
 567                                                 vtype = determine_vector_type(Z_ARRVAL(val_arr));
 568                                                 xReturn = XMLRPC_CreateVector(key, vtype);
 569 
 570                                                 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(val_arr), num_index, my_key, pIter) {
 571                                                         ht = HASH_OF(pIter);
 572                                                         if (ht) {
 573                                                                 ht->u.v.nApplyCount++;
 574                                                         }
 575                                                         if (my_key == NULL) {
 576                                                                 char *num_str = NULL;
 577 
 578                                                                 if (vtype != xmlrpc_vector_array) {
 579                                                                         spprintf(&num_str, 0, "%ld", num_index);
 580                                                                 }
 581 
 582                                                                 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(num_str, pIter, depth++));
 583                                                                 if (num_str) {
 584                                                                         efree(num_str);
 585                                                                 }
 586                                                         } else {
 587                                                                 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(ZSTR_VAL(my_key), pIter, depth++));
 588                                                         }
 589                                                         if (ht) {
 590                                                                 ht->u.v.nApplyCount--;
 591                                                         }
 592                                                 } ZEND_HASH_FOREACH_END();
 593                                                 zval_ptr_dtor(&val_arr);
 594                                         }
 595                                         break;
 596                                 default:
 597                                         break;
 598                         }
 599                 }
 600         }
 601         return xReturn;
 602 }
 603 
 604 static XMLRPC_VALUE PHP_to_XMLRPC(zval* root_val)
 605 {
 606         return PHP_to_XMLRPC_worker(NULL, root_val, 0);
 607 }
 608 
 609 /* recursively convert xmlrpc values into php values */
 610 static void XMLRPC_to_PHP(XMLRPC_VALUE el, zval *elem)
 611 {
 612         const char* pStr;
 613 
 614         if (el) {
 615                 XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
 616 
 617                 switch (type) {
 618                         case xmlrpc_empty:
 619                                 ZVAL_NULL(elem);
 620                                 break;
 621                         case xmlrpc_string:
 622                                 pStr = XMLRPC_GetValueString(el);
 623                                 if (pStr) {
 624                                         ZVAL_STRINGL(elem, pStr, XMLRPC_GetValueStringLen(el));
 625                                 }
 626                                 break;
 627                         case xmlrpc_int:
 628                                 ZVAL_LONG(elem, XMLRPC_GetValueInt(el));
 629                                 break;
 630                         case xmlrpc_boolean:
 631                                 ZVAL_BOOL(elem, XMLRPC_GetValueBoolean(el));
 632                                 break;
 633                         case xmlrpc_double:
 634                                 ZVAL_DOUBLE(elem, XMLRPC_GetValueDouble(el));
 635                                 break;
 636                         case xmlrpc_datetime:
 637                                 ZVAL_STRINGL(elem, XMLRPC_GetValueDateTime_ISO8601(el), XMLRPC_GetValueStringLen(el));
 638                                 break;
 639                         case xmlrpc_base64:
 640                                 pStr = XMLRPC_GetValueBase64(el);
 641                                 if (pStr) {
 642                                         ZVAL_STRINGL(elem, pStr, XMLRPC_GetValueStringLen(el));
 643                                 }
 644                                 break;
 645                         case xmlrpc_vector:
 646                                 array_init(elem);
 647                                 {
 648                                         XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
 649 
 650                                         while( xIter ) {
 651                                                 zval val;
 652                                                 ZVAL_UNDEF(&val);
 653                                                 XMLRPC_to_PHP(xIter, &val);
 654                                                 if (!Z_ISUNDEF(val)) {
 655                                                         add_zval(elem, XMLRPC_GetValueID(xIter), &val);
 656                                                 }
 657                                                 xIter = XMLRPC_VectorNext(el);
 658                                         }
 659                                 }
 660                                 break;
 661                         default:
 662                                 break;
 663                 }
 664                 set_zval_xmlrpc_type(elem, type);
 665         }
 666 }
 667 
 668 /* {{{ proto string xmlrpc_encode_request(string method, mixed params [, array output_options])
 669    Generates XML for a method request */
 670 PHP_FUNCTION(xmlrpc_encode_request)
 671 {
 672         XMLRPC_REQUEST xRequest = NULL;
 673         char *outBuf;
 674         zval *vals, *out_opts = NULL;
 675         char *method = NULL;
 676         size_t method_len;
 677         php_output_options out;
 678 
 679         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!z|a", &method, &method_len, &vals, &out_opts) == FAILURE) {
 680                 return;
 681         }
 682 
 683         set_output_options(&out, out_opts ? out_opts : 0);
 684 
 685         if (USED_RET()) {
 686                 xRequest = XMLRPC_RequestNew();
 687 
 688                 if (xRequest) {
 689                         XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
 690                         if (method == NULL) {
 691                                 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
 692                         } else {
 693                                 XMLRPC_RequestSetMethodName(xRequest, method);
 694                                 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
 695                         }
 696                         if (Z_TYPE_P(vals) != IS_NULL) {
 697                                 XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(vals));
 698                         }
 699 
 700                         outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
 701                         if (outBuf) {
 702                                 RETVAL_STRING(outBuf);
 703                                 free(outBuf);
 704                         }
 705                         XMLRPC_RequestFree(xRequest, 1);
 706                 }
 707         }
 708 
 709         if (strcmp(out.xmlrpc_out.xml_elem_opts.encoding, ENCODING_DEFAULT) != 0) {
 710                 efree((char *)out.xmlrpc_out.xml_elem_opts.encoding);
 711         }
 712 }
 713 /* }}} */
 714 
 715 /* {{{ proto string xmlrpc_encode(mixed value)
 716    Generates XML for a PHP value */
 717 PHP_FUNCTION(xmlrpc_encode)
 718 {
 719         XMLRPC_VALUE xOut = NULL;
 720         zval *arg1;
 721         char *outBuf;
 722 
 723         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg1) == FAILURE) {
 724                 return;
 725         }
 726 
 727         if (USED_RET()) {
 728                 /* convert native php type to xmlrpc type */
 729                 xOut = PHP_to_XMLRPC(arg1);
 730 
 731                 /* generate raw xml from xmlrpc data */
 732                 outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
 733 
 734                 if (xOut) {
 735                         if (outBuf) {
 736                                 RETVAL_STRING(outBuf);
 737                                 free(outBuf);
 738                         }
 739                         /* cleanup */
 740                         XMLRPC_CleanupValue(xOut);
 741                 }
 742         }
 743 }
 744 /* }}} */
 745 
 746 void decode_request_worker(char *xml_in, int xml_in_len, char *encoding_in, zval* method_name_out, zval *retval) /* {{{ */
 747 {
 748         XMLRPC_REQUEST response;
 749         STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}};
 750         const char *method_name;
 751         opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in) : ENCODING_DEFAULT;
 752 
 753         /* generate XMLRPC_REQUEST from raw xml */
 754         response = XMLRPC_REQUEST_FromXML(xml_in, xml_in_len, &opts);
 755         if (response) {
 756                 ZVAL_NULL(retval);
 757                 /* convert xmlrpc data to native php types */
 758                 XMLRPC_to_PHP(XMLRPC_RequestGetData(response), retval);
 759 
 760                 if (XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
 761                         if (method_name_out) {
 762                                 method_name = XMLRPC_RequestGetMethodName(response);
 763                                 if (method_name) {
 764                                         zval_ptr_dtor(method_name_out);
 765                                         ZVAL_STRING(method_name_out, method_name);
 766                                 } else {
 767                                         zval_ptr_dtor(retval);
 768                                         ZVAL_NULL(retval);
 769                                 }
 770                         }
 771                 }
 772 
 773                 /* dust, sweep, and mop */
 774                 XMLRPC_RequestFree(response, 1);
 775         }
 776 }
 777 /* }}} */
 778 
 779 /* {{{ proto array xmlrpc_decode_request(string xml, string& method [, string encoding])
 780    Decodes XML into native PHP types */
 781 PHP_FUNCTION(xmlrpc_decode_request)
 782 {
 783         char *xml, *encoding = NULL;
 784         zval *method;
 785         size_t xml_len, encoding_len = 0;
 786 
 787         if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/|s", &xml, &xml_len, &method, &encoding, &encoding_len) == FAILURE) {
 788                 return;
 789         }
 790 
 791         if (USED_RET()) {
 792                 decode_request_worker(xml, xml_len, encoding_len ? encoding : NULL, method, return_value);
 793         }
 794 }
 795 /* }}} */
 796 
 797 /* {{{ proto array xmlrpc_decode(string xml [, string encoding])
 798    Decodes XML into native PHP types */
 799 PHP_FUNCTION(xmlrpc_decode)
 800 {
 801         char *arg1, *arg2 = NULL;
 802         size_t arg1_len, arg2_len = 0;
 803 
 804         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &arg1, &arg1_len, &arg2, &arg2_len) == FAILURE) {
 805                 return;
 806         }
 807 
 808         if (USED_RET()) {
 809                 decode_request_worker(arg1, arg1_len, arg2_len ? arg2 : NULL, NULL, return_value);
 810         }
 811 }
 812 /* }}} */
 813 
 814 /*************************
 815 * server related methods *
 816 *************************/
 817 
 818 /* {{{ proto resource xmlrpc_server_create(void)
 819    Creates an xmlrpc server */
 820 PHP_FUNCTION(xmlrpc_server_create)
 821 {
 822         if (zend_parse_parameters_none() == FAILURE) {
 823                 return;
 824         }
 825 
 826         if (USED_RET()) {
 827                 xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
 828 
 829                 /* allocate server data.  free'd in destroy_server_data() */
 830                 array_init(&server->method_map);
 831                 array_init(&server->introspection_map);
 832                 server->server_ptr = XMLRPC_ServerCreate();
 833 
 834                 XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
 835 
 836                 /* store for later use */
 837                 RETURN_RES(zend_register_resource(server, le_xmlrpc_server));
 838         }
 839 }
 840 /* }}} */
 841 
 842 /* {{{ proto int xmlrpc_server_destroy(resource server)
 843    Destroys server resources */
 844 PHP_FUNCTION(xmlrpc_server_destroy)
 845 {
 846         zval *arg1;
 847         int bSuccess = FAILURE;
 848         xmlrpc_server_data *server;
 849 
 850         if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg1) == FAILURE) {
 851                 return;
 852         }
 853 
 854         if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(arg1), "xmlrpc server", le_xmlrpc_server)) == NULL) {
 855                 RETURN_FALSE;
 856         }
 857 
 858         bSuccess = zend_list_close(Z_RES_P(arg1));
 859         /* called by hashtable destructor
 860          * destroy_server_data(server);
 861          */
 862         RETURN_BOOL(bSuccess == SUCCESS);
 863 }
 864 /* }}} */
 865 
 866 /* called by xmlrpc C engine as method handler for all registered methods.
 867  * it then calls the corresponding PHP function to handle the method.
 868  */
 869 static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data) /* {{{ */
 870 {
 871         xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
 872         zval* php_function;
 873         zval xmlrpc_params;
 874         zval callback_params[3];
 875 
 876         zval_ptr_dtor(&pData->xmlrpc_method);
 877         zval_ptr_dtor(&pData->return_data);
 878 
 879         /* convert xmlrpc to native php types */
 880         ZVAL_STRING(&pData->xmlrpc_method, XMLRPC_RequestGetMethodName(xRequest));
 881         XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest), &xmlrpc_params);
 882 
 883         /* check if the called method has been previous registered */
 884         if ((php_function = zend_hash_find(Z_ARRVAL(pData->server->method_map), Z_STR(pData->xmlrpc_method))) != NULL) {
 885                 ZVAL_COPY_VALUE(&pData->php_function, php_function);
 886         }
 887 
 888         /* setup data hoojum */
 889         ZVAL_COPY_VALUE(&callback_params[0], &pData->xmlrpc_method);
 890         ZVAL_COPY_VALUE(&callback_params[1], &xmlrpc_params);
 891         ZVAL_COPY_VALUE(&callback_params[2], &pData->caller_params);
 892 
 893         /* Use same C function for all methods */
 894 
 895         /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
 896         call_user_function(CG(function_table), NULL, &pData->php_function, &pData->return_data, 3, callback_params);
 897 
 898         pData->php_executed = 1;
 899 
 900         zval_ptr_dtor(&xmlrpc_params);
 901 
 902         return PHP_to_XMLRPC(&pData->return_data);
 903 }
 904 /* }}} */
 905 
 906 /* called by the C server when it first receives an introspection request.  We pass this on to
 907  * our PHP listeners, if any
 908  */
 909 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) /* {{{ */
 910 {
 911         zval retval, *php_function;
 912         zval callback_params[1];
 913         zend_string *php_function_name;
 914         xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
 915 
 916         /* setup data hoojum */
 917         ZVAL_COPY_VALUE(&callback_params[0], &pData->caller_params);
 918 
 919         ZEND_HASH_FOREACH_VAL(Z_ARRVAL(pData->server->introspection_map), php_function) {
 920                 if (zend_is_callable(php_function, 0, &php_function_name)) {
 921                         /* php func prototype: function string user_func($user_params) */
 922                         if (call_user_function(CG(function_table), NULL, php_function, &retval, 1, callback_params) == SUCCESS) {
 923                                 XMLRPC_VALUE xData;
 924                                 STRUCT_XMLRPC_ERROR err = {0};
 925 
 926                                 /* return value should be a string */
 927                                 convert_to_string(&retval);
 928 
 929                                 xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL(retval), &err);
 930 
 931                                 if (xData) {
 932                                         if (!XMLRPC_ServerAddIntrospectionData(server, xData)) {
 933                                                 php_error_docref(NULL, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", ZSTR_VAL(php_function_name));
 934                                         }
 935                                         XMLRPC_CleanupValue(xData);
 936                                 } else {
 937                                         /* could not create description */
 938                                         if (err.xml_elem_error.parser_code) {
 939                                                 php_error_docref(NULL, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to add introspection data returned from %s()",
 940                                                                 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, ZSTR_VAL(php_function_name));
 941                                         } else {
 942                                                 php_error_docref(NULL, E_WARNING, "Unable to add introspection data returned from %s()", ZSTR_VAL(php_function_name));
 943                                         }
 944                                 }
 945                                 zval_ptr_dtor(&retval);
 946                         } else {
 947                                 /* user func failed */
 948                                 php_error_docref(NULL, E_WARNING, "Error calling user introspection callback: %s()", ZSTR_VAL(php_function_name));
 949                         }
 950                 } else {
 951                         php_error_docref(NULL, E_WARNING, "Invalid callback '%s' passed", ZSTR_VAL(php_function_name));
 952                 }
 953                 zend_string_release(php_function_name);
 954         } ZEND_HASH_FOREACH_END();
 955 
 956         /* so we don't call the same callbacks ever again */
 957         zend_hash_clean(Z_ARRVAL(pData->server->introspection_map));
 958 }
 959 /* }}} */
 960 
 961 /* {{{ proto bool xmlrpc_server_register_method(resource server, string method_name, string function)
 962    Register a PHP function to handle method matching method_name */
 963 PHP_FUNCTION(xmlrpc_server_register_method)
 964 {
 965         char *method_key;
 966         size_t method_key_len;
 967         zval *handle, *method_name;
 968         xmlrpc_server_data* server;
 969 
 970         if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsz", &handle, &method_key, &method_key_len, &method_name) == FAILURE) {
 971                 return;
 972         }
 973 
 974         if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
 975                 RETURN_FALSE;
 976         }
 977 
 978         /* register with C engine. every method just calls our standard callback,
 979          * and it then dispatches to php as necessary
 980          */
 981         if (XMLRPC_ServerRegisterMethod(server->server_ptr, method_key, php_xmlrpc_callback)) {
 982                 /* save for later use */
 983 
 984                 if (Z_REFCOUNTED_P(method_name)) {
 985                         Z_ADDREF_P(method_name);
 986                 }
 987                 /* register our php method */
 988                 add_zval(&server->method_map, method_key, method_name);
 989 
 990                 RETURN_TRUE;
 991         }
 992 }
 993 /* }}} */
 994 
 995 /* {{{ proto bool xmlrpc_server_register_introspection_callback(resource server, string function)
 996    Register a PHP function to generate documentation */
 997 PHP_FUNCTION(xmlrpc_server_register_introspection_callback)
 998 {
 999         zval *method_name, *handle;
1000         xmlrpc_server_data* server;
1001 
1002         if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz", &handle, &method_name) == FAILURE) {
1003                 return;
1004         }
1005 
1006         if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
1007                 RETURN_FALSE;
1008         }
1009 
1010         if (Z_REFCOUNTED_P(method_name)) {
1011                 Z_ADDREF_P(method_name);
1012         }
1013         /* register our php method */
1014         add_zval(&server->introspection_map, NULL, method_name);
1015 
1016         RETURN_TRUE;
1017 }
1018 /* }}} */
1019 
1020 /* this function is itchin for a re-write */
1021 
1022 /* {{{ proto mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])
1023    Parses XML requests and call methods */
1024 PHP_FUNCTION(xmlrpc_server_call_method)
1025 {
1026         XMLRPC_REQUEST xRequest;
1027         xmlrpc_callback_data data;
1028         STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
1029         xmlrpc_server_data* server;
1030         zval *caller_params, *handle, *output_opts = NULL;
1031         char *rawxml;
1032         size_t rawxml_len;
1033         php_output_options out;
1034         int argc = ZEND_NUM_ARGS();
1035 
1036         if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsz|a", &handle, &rawxml, &rawxml_len, &caller_params, &output_opts) != SUCCESS) {
1037                 return;
1038         }
1039         /* user output options */
1040         if (argc == 3) {
1041                 set_output_options(&out, NULL);
1042         } else {
1043                 set_output_options(&out, output_opts);
1044         }
1045 
1046         if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
1047                 RETURN_FALSE;
1048         }
1049 
1050         /* HACK: use output encoding for now */
1051         input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
1052 
1053         /* generate an XMLRPC_REQUEST from the raw xml input */
1054         xRequest = XMLRPC_REQUEST_FromXML(rawxml, rawxml_len, &input_opts);
1055 
1056         if (xRequest) {
1057                 const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
1058                 XMLRPC_VALUE xAnswer = NULL;
1059                 ZVAL_NULL(&data.xmlrpc_method); /* init. very important.  spent a frustrating day finding this out. */
1060                 ZVAL_NULL(&data.return_data);
1061                 ZVAL_NULL(&data.return_data);  /* in case value is never init'd, we don't dtor to think it is a string or something */
1062                 ZVAL_NULL(&data.xmlrpc_method);
1063 
1064                 /* setup some data to pass to the callback function */
1065                 ZVAL_COPY_VALUE(&data.caller_params, caller_params);
1066                 data.php_executed = 0;
1067                 data.server = server;
1068 
1069                 /* We could just call the php method directly ourselves at this point, but we do this
1070                  * with a C callback in case the xmlrpc library ever implements some cool usage stats,
1071                  * or somesuch.
1072                  */
1073                 xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
1074                 if (xAnswer && out.b_php_out) {
1075                         XMLRPC_to_PHP(xAnswer, &data.return_data);
1076                 } else if (data.php_executed && !out.b_php_out && !xAnswer) {
1077                         xAnswer = PHP_to_XMLRPC(&data.return_data);
1078                 }
1079 
1080                 /* should we return data as xml? */
1081                 if (!out.b_php_out) {
1082                         XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
1083                         if (xResponse) {
1084                                 char *outBuf = 0;
1085                                 int buf_len = 0;
1086 
1087                                 /* automagically determine output serialization type from request type */
1088                                 if (out.b_auto_version) {
1089                                         XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
1090                                         if (opts) {
1091                                                 out.xmlrpc_out.version = opts->version;
1092                                         }
1093                                 }
1094                                 /* set some required request hoojum */
1095                                 XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
1096                                 XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
1097                                 XMLRPC_RequestSetData(xResponse, xAnswer);
1098                                 XMLRPC_RequestSetMethodName(xResponse, methodname);
1099 
1100                                 /* generate xml */
1101                                 outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
1102                                 if (outBuf) {
1103                                         RETVAL_STRINGL(outBuf, buf_len);
1104                                         free(outBuf);
1105                                 }
1106                                 /* cleanup after ourselves.  what a sty! */
1107                                 XMLRPC_RequestFree(xResponse, 0);
1108                         }
1109                 } else { /* or as native php types? */
1110                         ZVAL_COPY(return_value, &data.return_data);
1111                 }
1112 
1113                 /* cleanup after ourselves.  what a sty! */
1114                 zval_ptr_dtor(&data.xmlrpc_method);
1115                 zval_ptr_dtor(&data.return_data);
1116 
1117                 if (xAnswer) {
1118                         XMLRPC_CleanupValue(xAnswer);
1119                 }
1120 
1121                 XMLRPC_RequestFree(xRequest, 1);
1122         }
1123 }
1124 /* }}} */
1125 
1126 /* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
1127    Adds introspection documentation  */
1128 PHP_FUNCTION(xmlrpc_server_add_introspection_data)
1129 {
1130         zval *handle, *desc;
1131         xmlrpc_server_data* server;
1132         XMLRPC_VALUE xDesc;
1133 
1134         if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra", &handle, &desc) == FAILURE) {
1135                 return;
1136         }
1137 
1138         if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
1139                 RETURN_FALSE;
1140         }
1141 
1142         xDesc = PHP_to_XMLRPC(desc);
1143         if (xDesc) {
1144                 int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
1145                 XMLRPC_CleanupValue(xDesc);
1146                 RETURN_LONG(retval);
1147         }
1148         RETURN_LONG(0);
1149 }
1150 /* }}} */
1151 
1152 /* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
1153    Decodes XML into a list of method descriptions */
1154 PHP_FUNCTION(xmlrpc_parse_method_descriptions)
1155 {
1156         char *arg1;
1157         size_t arg1_len;
1158 
1159         if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg1, &arg1_len) == FAILURE) {
1160                 return;
1161         }
1162 
1163         if (USED_RET()) {
1164                 STRUCT_XMLRPC_ERROR err = {0};
1165                 XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1, &err);
1166                 if (xVal) {
1167                         XMLRPC_to_PHP(xVal, return_value);
1168                         /* dust, sweep, and mop */
1169                         XMLRPC_CleanupValue(xVal);
1170                 } else {
1171                         /* could not create description */
1172                         if (err.xml_elem_error.parser_code) {
1173                                 php_error_docref(NULL, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data",
1174                                                 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
1175                         } else {
1176                                 php_error_docref(NULL, E_WARNING, "Invalid xml structure. Unable to create introspection data");
1177                         }
1178 
1179                         php_error_docref(NULL, E_WARNING, "xml parse error. no method description created");
1180                 }
1181         }
1182 }
1183 /* }}} */
1184 
1185 /************
1186 * type data *
1187 ************/
1188 
1189 #define XMLRPC_TYPE_COUNT 9
1190 #define XMLRPC_VECTOR_TYPE_COUNT 4
1191 #define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
1192 
1193 /* return a string matching a given xmlrpc type */
1194 static const char** get_type_str_mapping(void) /* {{{ */
1195 {
1196         static const char* str_mapping[TYPE_STR_MAP_SIZE];
1197         static int first = 1;
1198         if (first) {
1199                 /* warning. do not add/delete without changing size define */
1200                 str_mapping[xmlrpc_none]     = "none";
1201                 str_mapping[xmlrpc_empty]    = "empty";
1202                 str_mapping[xmlrpc_base64]   = "base64";
1203                 str_mapping[xmlrpc_boolean]  = "boolean";
1204                 str_mapping[xmlrpc_datetime] = "datetime";
1205                 str_mapping[xmlrpc_double]   = "double";
1206                 str_mapping[xmlrpc_int]      = "int";
1207                 str_mapping[xmlrpc_string]   = "string";
1208                 str_mapping[xmlrpc_vector]   = "vector";
1209                 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none]   = "none";
1210                 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array]  = "array";
1211                 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed]  = "mixed";
1212                 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
1213                 first = 0;
1214         }
1215         return (const char**)str_mapping;
1216 }
1217 /* }}} */
1218 
1219 /* map an xmlrpc type to a string */
1220 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) /* {{{ */
1221 {
1222         const char** str_mapping = get_type_str_mapping();
1223 
1224         if (vtype == xmlrpc_vector_none) {
1225                 return str_mapping[type];
1226         } else {
1227                 return str_mapping[XMLRPC_TYPE_COUNT + vtype];
1228         }
1229 }
1230 /* }}} */
1231 
1232 /* map a string to an xmlrpc type */
1233 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) /* {{{ */
1234 {
1235         const char** str_mapping = get_type_str_mapping();
1236         int i;
1237 
1238         if (str) {
1239                 for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
1240                         if (!strcmp(str_mapping[i], str)) {
1241                                 return (XMLRPC_VALUE_TYPE) i;
1242                         }
1243                 }
1244         }
1245         return xmlrpc_none;
1246 }
1247 /* }}} */
1248 
1249 /* map a string to an xmlrpc vector type */
1250 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) /* {{{ */
1251 {
1252         const char** str_mapping = get_type_str_mapping();
1253         int i;
1254 
1255         if (str) {
1256                 for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
1257                         if (!strcmp(str_mapping[i], str)) {
1258                                 return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
1259                         }
1260                 }
1261         }
1262         return xmlrpc_none;
1263 }
1264 /* }}} */
1265 
1266 /* set a given value to a particular type.
1267  * note: this only works on strings, and only for date and base64,
1268  *       which do not have native php types. black magic lies herein.
1269  */
1270 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype) /* {{{ */
1271 {
1272         int bSuccess = FAILURE;
1273 
1274         /* we only really care about strings because they can represent
1275          * base64 and datetime.  all other types have corresponding php types
1276          */
1277         if (Z_TYPE_P(value) == IS_STRING) {
1278                 if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
1279                         const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
1280                         zval type;
1281 
1282                         ZVAL_STRING(&type, typestr);
1283 
1284                         if (newtype == xmlrpc_datetime) {
1285                                 XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, Z_STRVAL_P(value));
1286                                 if (v) {
1287                                         time_t timestamp = (time_t) php_parse_date((char *)XMLRPC_GetValueDateTime_ISO8601(v), NULL);
1288                                         if (timestamp != -1) {
1289                                                 zval ztimestamp;
1290 
1291                                                 ZVAL_LONG(&ztimestamp, timestamp);
1292 
1293                                                 convert_to_object(value);
1294                                                 if (zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1, &type)) {
1295                                                         bSuccess = (zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR) - 1, &ztimestamp) != NULL)? SUCCESS : FAILURE;
1296                                                 }
1297                                         } else {
1298                                                 zval_ptr_dtor(&type);
1299                                         }
1300                                         XMLRPC_CleanupValue(v);
1301                                 } else {
1302                                         zval_ptr_dtor(&type);
1303                                 }
1304                         } else {
1305                                 convert_to_object(value);
1306                                 bSuccess = (zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1, &type) != NULL)? SUCCESS : FAILURE;
1307                         }
1308                 }
1309         }
1310 
1311         return bSuccess;
1312 }
1313 /* }}} */
1314 
1315 /* return xmlrpc type of a php value */
1316 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval* newvalue) /* {{{ */
1317 {
1318         XMLRPC_VALUE_TYPE type = xmlrpc_none;
1319 
1320         if (value) {
1321                 switch (Z_TYPE_P(value)) {
1322                         case IS_NULL:
1323                                 type = xmlrpc_base64;
1324                                 break;
1325 #ifndef BOOL_AS_LONG
1326 
1327                         /* Right thing to do, but it breaks some legacy code. */
1328                         case IS_TRUE:
1329                         case IS_FALSE:
1330                                 type = xmlrpc_boolean;
1331                                 break;
1332 #else
1333                         case IS_BOOL:
1334 #endif
1335                         case IS_LONG:
1336                         case IS_RESOURCE:
1337                                 type = xmlrpc_int;
1338                                 break;
1339                         case IS_DOUBLE:
1340                                 type = xmlrpc_double;
1341                                 break;
1342                         case IS_CONSTANT:
1343                                 type = xmlrpc_string;
1344                                 break;
1345                         case IS_STRING:
1346                                 type = xmlrpc_string;
1347                                 break;
1348                         case IS_ARRAY:
1349                                 type = xmlrpc_vector;
1350                                 break;
1351                         case IS_OBJECT:
1352                                 {
1353                                         zval* attr;
1354                                         type = xmlrpc_vector;
1355 
1356                                         if ((attr = zend_hash_str_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1)) != NULL) {
1357                                                 if (Z_TYPE_P(attr) == IS_STRING) {
1358                                                         type = xmlrpc_str_as_type(Z_STRVAL_P(attr));
1359                                                 }
1360                                         }
1361                                         break;
1362                                 }
1363                 }
1364 
1365                 /* if requested, return an unmolested (magic removed) copy of the value */
1366                 if (newvalue) {
1367                         zval* val;
1368 
1369                         if ((type == xmlrpc_base64 && Z_TYPE_P(value) == IS_OBJECT) || type == xmlrpc_datetime) {
1370                                 if ((val = zend_hash_str_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR) - 1)) != NULL) {
1371                                         ZVAL_COPY_VALUE(newvalue, val);
1372                                 }
1373                         } else {
1374                                 ZVAL_COPY_VALUE(newvalue, value);
1375                         }
1376                 }
1377         }
1378 
1379         return type;
1380 }
1381 /* }}} */
1382 
1383 /* {{{ proto bool xmlrpc_set_type(string value, string type)
1384    Sets xmlrpc type, base64 or datetime, for a PHP string value */
1385 PHP_FUNCTION(xmlrpc_set_type)
1386 {
1387         zval *arg;
1388         char *type;
1389         size_t type_len;
1390         XMLRPC_VALUE_TYPE vtype;
1391 
1392         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/s", &arg, &type, &type_len) == FAILURE) {
1393                 return;
1394         }
1395 
1396         vtype = xmlrpc_str_as_type(type);
1397         if (vtype != xmlrpc_none) {
1398                 if (set_zval_xmlrpc_type(arg, vtype) == SUCCESS) {
1399                         RETURN_TRUE;
1400                 }
1401         } else {
1402                 zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type);
1403         }
1404         RETURN_FALSE;
1405 }
1406 /* }}} */
1407 
1408 /* {{{ proto string xmlrpc_get_type(mixed value)
1409    Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */
1410 PHP_FUNCTION(xmlrpc_get_type)
1411 {
1412         zval *arg;
1413         XMLRPC_VALUE_TYPE type;
1414         XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
1415 
1416         if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1417                 return;
1418         }
1419 
1420         type = get_zval_xmlrpc_type(arg, 0);
1421         if (type == xmlrpc_vector) {
1422                 vtype = determine_vector_type((Z_TYPE_P(arg) == IS_OBJECT) ? Z_OBJPROP_P(arg) : Z_ARRVAL_P(arg));
1423         }
1424 
1425         RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype));
1426 }
1427 /* }}} */
1428 
1429 /* {{{ proto bool xmlrpc_is_fault(array arg)
1430    Determines if an array value represents an XMLRPC fault. */
1431 PHP_FUNCTION(xmlrpc_is_fault)
1432 {
1433         zval *arg;
1434 
1435         if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &arg) == FAILURE) {
1436                 return;
1437         }
1438 
1439         /* The "correct" way to do this would be to call the xmlrpc
1440          * library XMLRPC_ValueIsFault() func.  However, doing that
1441          * would require us to create an xmlrpc value from the php
1442          * array, which is rather expensive, especially if it was
1443          * a big array.  Thus, we resort to this not so clever hackery.
1444          */
1445         if (zend_hash_str_exists(Z_ARRVAL_P(arg), FAULT_CODE, FAULT_CODE_LEN) &&
1446                         zend_hash_str_exists(Z_ARRVAL_P(arg), FAULT_STRING, FAULT_STRING_LEN)) {
1447                 RETURN_TRUE;
1448         }
1449 
1450         RETURN_FALSE;
1451 }
1452 /* }}} */
1453 
1454 /*
1455  * Local variables:
1456  * tab-width: 4
1457  * c-basic-offset: 4
1458  * End:
1459  */
1460 

/* [<][>][^][v][top][bottom][index][help] */