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