2 This file is part of, or distributed with, libXMLRPC - a C library for
3 xml-encoded function calls.
5 Author: Dan Libby (dan@libby.com)
6 Epinions.com may be contacted at feedback@epinions-inc.com
10 Copyright 2001 Epinions, Inc.
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.
17 1) The above copyright notice and this permission notice shall be included
18 without modification in all copies or substantial portions of the
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.
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
34 /* auto-generated portions of this file are also subject to the php license */
37 +----------------------------------------------------------------------+
39 +----------------------------------------------------------------------+
40 | Copyright (c) 1997-2004 The PHP Group |
41 +----------------------------------------------------------------------+
42 | This source file is subject to version 3.0 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_0.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 +----------------------------------------------------------------------+
51 +----------------------------------------------------------------------+
54 /**********************************************************************
56 * - when calling a php user function, there appears to be no way to *
57 * distinguish between a return value of null, and no return value *
58 * at all. The xml serialization layer(s) will then return a value *
59 * of null, when the right thing may be no value at all. (SOAP) *
60 **********************************************************************/
67 #include "ext/standard/info.h"
69 #include "php_xmlrpc.h"
72 #define PHP_EXT_VERSION "0.51"
74 /* ====================
75 * Thierry - Dec. 17 2009
76 * patch for php - bug first triggered on f12 with php-5.3
77 * http://pecl.php.net/bugs/bug.php?id=14369
78 * http://remi.fedorapeople.org/ncurses-1.0.0-php53.patch
79 * I'm taking out the 'static' stuff as after pre-processing it appears to be part of the macro expansion already
81 #if ZEND_MODULE_API_NO >= 20071006
82 // No more defined with PHP 5.3.0
83 ZEND_BEGIN_ARG_INFO(first_arg_force_ref, 0)
87 ZEND_BEGIN_ARG_INFO(second_arg_force_ref, 0)
92 /* ==================== end patch */
94 /* ====================
95 * Thierry - Jan. 30 2013
96 * patch for php - issues first triggered on f18 with php-5.4
97 * https://bugs.php.net/bug.php?id=60016
98 * https://bugs.php.net/bug.php?id=60016
100 #if ZEND_MODULE_API_NO >= 20100525
101 // No more defined with PHP 5.4
102 #define function_entry zend_function_entry
105 /* ==================== end patch */
107 /* ====================
108 * Thierry - Jan. 2 2015
109 * patch for php - issues first triggered on f21 with php-5.6
110 * somebody somehow seems to have undefined
111 * the IS_CONSTANT_ARRAY constant
113 * however I'd rather not change the code that runs
114 * on older releases, so see for the ugly ifdef below
116 /* ==================== end patch */
119 /* ========== additional notes
120 * in the process, I've also come across the following resources that might help
121 * if/when zend_get_parameters_ex gets deprecated (only generates warnings for now)
123 * http://developers.evrsoft.com/docs/php/zend.arguments.deprecated-retrieval.shtml
124 * explains how the old (our) stuff works
126 * http://www.hospedajeydominios.com/mambo/documentacion-manual_php-pagina-zend_arguments_retrieval.html
127 * gives info on the new scheme
129 * I'm taking tha risk as the changes seem to mean major surgery ...
132 /* You should tweak config.m4 so this symbol (or some else suitable)
135 ZEND_DECLARE_MODULE_GLOBALS(xmlrpc)
137 static int le_xmlrpc_server;
139 function_entry xmlrpc_functions[] = {
140 PHP_FE(xmlrpc_encode, NULL)
141 PHP_FE(xmlrpc_decode, NULL)
142 PHP_FE(xmlrpc_decode_request, second_arg_force_ref)
143 PHP_FE(xmlrpc_encode_request, NULL)
144 PHP_FE(xmlrpc_get_type, NULL)
145 PHP_FE(xmlrpc_set_type, first_arg_force_ref)
146 PHP_FE(xmlrpc_is_fault, NULL)
147 PHP_FE(xmlrpc_server_create, NULL)
148 PHP_FE(xmlrpc_server_destroy, NULL)
149 PHP_FE(xmlrpc_server_register_method, NULL)
150 PHP_FE(xmlrpc_server_call_method, NULL)
151 PHP_FE(xmlrpc_parse_method_descriptions, NULL)
152 PHP_FE(xmlrpc_server_add_introspection_data, NULL)
153 PHP_FE(xmlrpc_server_register_introspection_callback, NULL)
157 zend_module_entry xmlrpc_module_entry = {
158 STANDARD_MODULE_HEADER,
162 PHP_MSHUTDOWN(xmlrpc),
163 PHP_RINIT(xmlrpc), /* Replace with NULL if there's nothing to do at request start */
164 PHP_RSHUTDOWN(xmlrpc), /* Replace with NULL if there's nothing to do at request end */
167 STANDARD_MODULE_PROPERTIES
170 #ifdef COMPILE_DL_XMLRPC
171 ZEND_GET_MODULE(xmlrpc)
173 # include "zend_arg_defs.c"
178 STD_PHP_INI_BOOLEAN("xmlrpc.allow_null", "0", PHP_INI_ALL, OnUpdateBool, allow_null, zend_xmlrpc_globals, xmlrpc_globals)
181 static void php_xmlrpc_init_globals(zend_xmlrpc_globals *xmlrpc_globals)
183 memset(xmlrpc_globals, 0, sizeof(zend_xmlrpc_globals));
186 /*******************************
187 * local structures and defines *
188 *******************************/
190 /* per server data */
191 typedef struct _xmlrpc_server_data {
193 zval* introspection_map;
194 XMLRPC_SERVER server_ptr;
195 } xmlrpc_server_data;
198 /* how to format output */
199 typedef struct _php_output_options {
203 STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out;
204 } php_output_options;
206 /* data passed to C callback */
207 typedef struct _xmlrpc_callback_data {
212 xmlrpc_server_data* server;
214 } xmlrpc_callback_data;
217 #define OUTPUT_TYPE_KEY "output_type"
218 #define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1)
219 #define OUTPUT_TYPE_VALUE_PHP "php"
220 #define OUTPUT_TYPE_VALUE_XML "xml"
222 #define ALLOW_NULL_KEY "allow_null"
223 #define ALLOW_NULL_KEY_LEN (sizeof(ALLOW_NULL_KEY) - 1)
225 #define VERBOSITY_KEY "verbosity"
226 #define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1)
227 #define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space"
228 #define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only"
229 #define VERBOSITY_VALUE_PRETTY "pretty"
231 #define ESCAPING_KEY "escaping"
232 #define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1)
233 #define ESCAPING_VALUE_CDATA "cdata"
234 #define ESCAPING_VALUE_NON_ASCII "non-ascii"
235 #define ESCAPING_VALUE_NON_PRINT "non-print"
236 #define ESCAPING_VALUE_MARKUP "markup"
238 #define VERSION_KEY "version"
239 #define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1)
240 #define VERSION_VALUE_SIMPLE "simple"
241 #define VERSION_VALUE_XMLRPC "xmlrpc"
242 #define VERSION_VALUE_SOAP11 "soap 1.1"
243 #define VERSION_VALUE_AUTO "auto"
245 #define ENCODING_KEY "encoding"
246 #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1)
247 #define ENCODING_DEFAULT "iso-8859-1"
250 #define OBJECT_TYPE_ATTR "xmlrpc_type"
251 #define OBJECT_VALUE_ATTR "scalar"
252 #define OBJECT_VALUE_TS_ATTR "timestamp"
255 #define FAULT_CODE "faultCode"
256 #define FAULT_CODE_LEN (sizeof(FAULT_CODE) - 1)
257 #define FAULT_STRING "faultString"
258 #define FAULT_STRING_LEN (sizeof(FAULT_STRING) - 1)
260 /***********************
261 * forward declarations *
262 ***********************/
263 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(php_output_options* out, zval* value, zval** newvalue);
264 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data);
265 int sset_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
266 zval* decode_request_worker(zval* xml_in, zval* encoding_in, zval* method_name_out);
267 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype);
268 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str);
269 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str);
270 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
272 /*********************
273 * startup / shutdown *
274 *********************/
276 static void destroy_server_data(xmlrpc_server_data *server)
279 XMLRPC_ServerDestroy(server->server_ptr);
281 zval_dtor(server->method_map);
282 FREE_ZVAL(server->method_map);
284 zval_dtor(server->introspection_map);
285 FREE_ZVAL(server->introspection_map);
291 /* called when server is being destructed. either when xmlrpc_server_destroy
292 * is called, or when request ends. */
293 static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
295 if (rsrc && rsrc->ptr) {
296 destroy_server_data((xmlrpc_server_data*) rsrc->ptr);
301 PHP_MINIT_FUNCTION(xmlrpc)
303 ZEND_INIT_MODULE_GLOBALS(xmlrpc, php_xmlrpc_init_globals, NULL);
305 REGISTER_INI_ENTRIES();
307 le_xmlrpc_server = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);
312 /* module shutdown */
313 PHP_MSHUTDOWN_FUNCTION(xmlrpc)
318 /* Remove if there's nothing to do at request start */
319 PHP_RINIT_FUNCTION(xmlrpc)
324 /* Remove if there's nothing to do at request end */
325 PHP_RSHUTDOWN_FUNCTION(xmlrpc)
330 /* display info in phpinfo() */
331 PHP_MINFO_FUNCTION(xmlrpc)
333 php_info_print_table_start();
334 php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString());
335 php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION);
336 php_info_print_table_row(2, "author", "Dan Libby");
337 php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net");
338 php_info_print_table_row(2, "open sourced by", "Epinions.com");
339 php_info_print_table_end();
346 /* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc).
347 * Could easily be further generalized to work with objects.
350 static int add_long(zval* list, char* id, int num) {
351 if(id) return add_assoc_long(list, id, num);
352 else return add_next_index_long(list, num);
355 static int add_double(zval* list, char* id, double num) {
356 if(id) return add_assoc_double(list, id, num);
357 else return add_next_index_double(list, num);
360 static int add_string(zval* list, char* id, char* string, int duplicate) {
361 if(id) return add_assoc_string(list, id, string, duplicate);
362 else return add_next_index_string(list, string, duplicate);
365 static int add_stringl(zval* list, char* id, char* string, uint length, int duplicate) {
366 if(id) return add_assoc_stringl(list, id, string, length, duplicate);
367 else return add_next_index_stringl(list, string, length, duplicate);
372 static int add_zval(zval* list, const char* id, zval** val)
376 return zend_hash_update(Z_ARRVAL_P(list), (char*) id, strlen(id) + 1, (void *) val, sizeof(zval **), NULL);
378 return zend_hash_next_index_insert(Z_ARRVAL_P(list), (void *) val, sizeof(zval **), NULL);
381 /* what is the correct return on error? */
385 #define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0)
388 /*************************
389 * input / output options *
390 *************************/
392 /* parse an array (user input) into output options suitable for use by xmlrpc engine
393 * and determine whether to return data as xml or php vars */
394 static void set_output_options(php_output_options* options, zval* output_opts)
401 options->b_php_out = 0;
402 options->b_auto_version = 1;
403 options->b_allow_null = 0;
404 options->xmlrpc_out.version = xmlrpc_version_1_0;
405 options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT;
406 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
407 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping;
409 if (output_opts && Z_TYPE_P(output_opts) == IS_ARRAY) {
413 if (zend_hash_find(Z_ARRVAL_P(output_opts), ALLOW_NULL_KEY, ALLOW_NULL_KEY_LEN + 1, (void**) &val) == SUCCESS) {
414 if (Z_TYPE_PP(val) == IS_BOOL) {
415 if (Z_LVAL_PP(val)) {
416 options->b_allow_null = 1;
418 options->b_allow_null = 0;
423 /* type of output (xml/php) */
424 if (zend_hash_find(Z_ARRVAL_P(output_opts), OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**) &val) == SUCCESS) {
425 if (Z_TYPE_PP(val) == IS_STRING) {
426 if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_PHP)) {
427 options->b_php_out = 1;
428 } else if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_XML)) {
429 options->b_php_out = 0;
434 /* verbosity of generated xml */
435 if (zend_hash_find(Z_ARRVAL_P(output_opts), VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, (void**) &val) == SUCCESS) {
436 if (Z_TYPE_PP(val) == IS_STRING) {
437 if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NO_WHITE_SPACE)) {
438 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space;
439 } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NEWLINES_ONLY)) {
440 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only;
441 } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_PRETTY)) {
442 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
447 /* version of xml to output */
448 if (zend_hash_find(Z_ARRVAL_P(output_opts), VERSION_KEY, VERSION_KEY_LEN + 1, (void**) &val) == SUCCESS) {
449 if (Z_TYPE_PP(val) == IS_STRING) {
450 options->b_auto_version = 0;
451 if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_XMLRPC)) {
452 options->xmlrpc_out.version = xmlrpc_version_1_0;
453 } else if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_SIMPLE)) {
454 options->xmlrpc_out.version = xmlrpc_version_simple;
455 } else if (!strcmp((*val)->value.str.val, VERSION_VALUE_SOAP11)) {
456 options->xmlrpc_out.version = xmlrpc_version_soap_1_1;
457 } else { /* if(!strcmp((*val)->value.str.val, VERSION_VALUE_AUTO)) { */
458 options->b_auto_version = 1;
463 /* encoding code set */
464 if(zend_hash_find(Z_ARRVAL_P(output_opts),
465 ENCODING_KEY, ENCODING_KEY_LEN + 1,
466 (void**)&val) == SUCCESS) {
467 if(Z_TYPE_PP(val) == IS_STRING) {
468 options->xmlrpc_out.xml_elem_opts.encoding = estrdup(Z_STRVAL_PP(val));
472 /* escaping options */
473 if(zend_hash_find(Z_ARRVAL_P(output_opts),
474 ESCAPING_KEY, ESCAPING_KEY_LEN + 1,
475 (void**)&val) == SUCCESS) {
476 /* multiple values allowed. check if array */
477 if(Z_TYPE_PP(val) == IS_ARRAY) {
479 zend_hash_internal_pointer_reset(Z_ARRVAL_PP(val));
480 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping;
482 if(zend_hash_get_current_data(Z_ARRVAL_PP(val), (void**)&iter_val) == SUCCESS) {
483 if(Z_TYPE_PP(iter_val) == IS_STRING && Z_STRVAL_PP(iter_val)) {
484 if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_CDATA)) {
485 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping;
487 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_ASCII)) {
488 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping;
490 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_PRINT)) {
491 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping;
493 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_MARKUP)) {
494 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping;
502 zend_hash_move_forward(Z_ARRVAL_PP(val));
505 /* else, check for single value */
506 else if(Z_TYPE_PP(val) == IS_STRING) {
507 if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_CDATA)) {
508 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping;
510 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_ASCII)) {
511 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping;
513 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_PRINT)) {
514 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping;
516 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_MARKUP)) {
517 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping;
530 /* php arrays have no distinction between array and struct types.
531 * they even allow mixed. Thus, we determine the type by iterating
532 * through the entire array and figuring out each element.
533 * room for some optimation here if we stop after a specific # of elements.
535 static XMLRPC_VECTOR_TYPE determine_vector_type (HashTable *ht)
537 int bArray = 0, bStruct = 0, bMixed = 0;
538 unsigned long num_index;
541 zend_hash_internal_pointer_reset(ht);
543 int res = my_zend_hash_get_current_key(ht, &my_key, &num_index);
544 if(res == HASH_KEY_IS_LONG) {
551 else if(res == HASH_KEY_NON_EXISTANT) {
554 else if(res == HASH_KEY_IS_STRING) {
562 zend_hash_move_forward(ht);
564 return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array);
567 /* recursively convert php values into xmlrpc values */
568 static XMLRPC_VALUE PHP_to_XMLRPC_worker (php_output_options* out, const char* key, zval* in_val, int depth)
570 XMLRPC_VALUE xReturn = NULL;
573 XMLRPC_VALUE_TYPE type = get_zval_xmlrpc_type(out, in_val, &val);
577 xReturn = XMLRPC_CreateValueEmpty();
578 XMLRPC_SetValueID(xReturn, key, 0);
581 xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
583 case xmlrpc_datetime:
584 convert_to_string(val);
585 xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL_P(val));
588 convert_to_boolean(val);
589 xReturn = XMLRPC_CreateValueBoolean(key, Z_LVAL_P(val));
592 convert_to_long(val);
593 xReturn = XMLRPC_CreateValueInt(key, Z_LVAL_P(val));
596 convert_to_double(val);
597 xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL_P(val));
600 convert_to_string(val);
601 xReturn = XMLRPC_CreateValueString(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
605 unsigned long num_index;
609 convert_to_array(val);
611 xReturn = XMLRPC_CreateVector(key, determine_vector_type(Z_ARRVAL_P(val)));
613 zend_hash_internal_pointer_reset(Z_ARRVAL_P(val));
615 int res = my_zend_hash_get_current_key(Z_ARRVAL_P(val), &my_key, &num_index);
616 if(res == HASH_KEY_IS_LONG) {
617 if(zend_hash_get_current_data(Z_ARRVAL_P(val), (void**)&pIter) == SUCCESS) {
618 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(out, 0, *pIter, depth++));
621 else if(res == HASH_KEY_NON_EXISTANT) {
624 else if(res == HASH_KEY_IS_STRING) {
625 if(zend_hash_get_current_data(Z_ARRVAL_P(val), (void**)&pIter) == SUCCESS) {
626 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(out, my_key, *pIter, depth++));
630 zend_hash_move_forward(Z_ARRVAL_P(val));
642 static XMLRPC_VALUE PHP_to_XMLRPC(php_output_options* out, zval* root_val)
644 return PHP_to_XMLRPC_worker(out, NULL, root_val, 0);
647 /* recursively convert xmlrpc values into php values */
648 static zval* XMLRPC_to_PHP(XMLRPC_VALUE el)
654 XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
656 MAKE_STD_ZVAL(elem); /* init. very important. spent a frustrating day finding this out. */
661 Z_TYPE_P(elem) = IS_NULL;
664 pStr = XMLRPC_GetValueString(el);
666 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
667 Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
668 Z_TYPE_P(elem) = IS_STRING;
672 Z_LVAL_P(elem) = XMLRPC_GetValueInt(el);
673 Z_TYPE_P(elem) = IS_LONG;
676 Z_LVAL_P(elem) = XMLRPC_GetValueBoolean(el);
677 Z_TYPE_P(elem) = IS_BOOL;
680 Z_DVAL_P(elem) = XMLRPC_GetValueDouble(el);
681 Z_TYPE_P(elem) = IS_DOUBLE;
683 case xmlrpc_datetime:
684 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
685 Z_STRVAL_P(elem) = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), Z_STRLEN_P(elem));
686 Z_TYPE_P(elem) = IS_STRING;
689 pStr = XMLRPC_GetValueBase64(el);
691 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
692 Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
693 Z_TYPE_P(elem) = IS_STRING;
699 XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
702 zval *val = XMLRPC_to_PHP(xIter);
704 add_zval(elem, XMLRPC_GetValueID(xIter), &val);
706 xIter = XMLRPC_VectorNext(el);
713 set_zval_xmlrpc_type(elem, type);
718 /* {{{ proto string xmlrpc_encode_request(string method, mixed params)
719 Generates XML for a method request */
720 PHP_FUNCTION(xmlrpc_encode_request)
722 XMLRPC_REQUEST xRequest = NULL;
723 zval **method, **vals, **out_opts;
725 php_output_options out;
727 if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 3 || (zend_get_parameters_ex(ZEND_NUM_ARGS(), &method, &vals, &out_opts) == FAILURE)) {
728 WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
731 set_output_options(&out, (ZEND_NUM_ARGS() == 3) ? *out_opts : 0);
733 if(return_value_used) {
734 xRequest = XMLRPC_RequestNew();
737 XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
738 if (Z_TYPE_PP(method) == IS_NULL) {
739 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
741 XMLRPC_RequestSetMethodName(xRequest, Z_STRVAL_PP(method));
742 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
744 if (Z_TYPE_PP(vals) != IS_NULL) {
745 XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(&out, *vals));
748 outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
750 RETVAL_STRING(outBuf, 1);
753 XMLRPC_RequestFree(xRequest, 1);
759 /* {{{ proto string xmlrpc_encode(mixed value)
760 Generates XML for a PHP value */
761 PHP_FUNCTION(xmlrpc_encode)
763 XMLRPC_VALUE xOut = NULL;
767 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
771 if( return_value_used ) {
772 /* convert native php type to xmlrpc type */
773 xOut = PHP_to_XMLRPC(NULL, *arg1);
775 /* generate raw xml from xmlrpc data */
776 outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
780 RETVAL_STRING(outBuf, 1);
784 XMLRPC_CleanupValue(xOut);
791 zval* decode_request_worker (zval* xml_in, zval* encoding_in, zval* method_name_out)
794 XMLRPC_REQUEST response;
795 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}};
796 opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(Z_STRVAL_P(encoding_in)) : ENCODING_DEFAULT;
798 /* generate XMLRPC_REQUEST from raw xml */
799 response = XMLRPC_REQUEST_FromXML(Z_STRVAL_P(xml_in), Z_STRLEN_P(xml_in), &opts);
801 /* convert xmlrpc data to native php types */
802 retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response));
804 if(XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
805 if(method_name_out) {
806 convert_to_string(method_name_out);
807 Z_TYPE_P(method_name_out) = IS_STRING;
808 Z_STRVAL_P(method_name_out) = estrdup(XMLRPC_RequestGetMethodName(response));
809 Z_STRLEN_P(method_name_out) = strlen(Z_STRVAL_P(method_name_out));
813 /* dust, sweep, and mop */
814 XMLRPC_RequestFree(response, 1);
819 /* {{{ proto array xmlrpc_decode_request(string xml, string& method [, string encoding])
820 Decodes XML into native PHP types */
821 PHP_FUNCTION(xmlrpc_decode_request)
823 zval **xml, **method, **encoding = NULL;
824 int argc = ZEND_NUM_ARGS();
826 if (argc < 2 || argc > 3 || (zend_get_parameters_ex(argc, &xml, &method, &encoding) == FAILURE)) {
830 convert_to_string_ex(xml);
831 convert_to_string_ex(method);
833 convert_to_string_ex(encoding);
836 if(return_value_used) {
837 zval* retval = decode_request_worker(*xml, encoding ? *encoding : NULL, *method);
839 *return_value = *retval;
847 /* {{{ proto array xmlrpc_decode(string xml [, string encoding])
848 Decodes XML into native PHP types */
849 PHP_FUNCTION(xmlrpc_decode)
851 zval **arg1, **arg2 = NULL;
852 int argc = ZEND_NUM_ARGS();
854 if (argc < 1 || argc > 2 || (zend_get_parameters_ex(argc, &arg1, &arg2) == FAILURE)) {
858 convert_to_string_ex(arg1);
860 convert_to_string_ex(arg2);
863 if(return_value_used) {
864 zval* retval = decode_request_worker(*arg1, arg2 ? *arg2 : NULL, NULL);
866 *return_value = *retval;
874 /*************************
875 * server related methods *
876 *************************/
878 /* {{{ proto resource xmlrpc_server_create(void)
879 Creates an xmlrpc server */
880 PHP_FUNCTION(xmlrpc_server_create)
882 if(ZEND_NUM_ARGS() != 0) {
886 if(return_value_used) {
887 zval *method_map, *introspection_map;
888 xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
889 MAKE_STD_ZVAL(method_map);
890 MAKE_STD_ZVAL(introspection_map);
892 array_init(method_map);
893 array_init(introspection_map);
895 /* allocate server data. free'd in destroy_server_data() */
896 server->method_map = method_map;
897 server->introspection_map = introspection_map;
898 server->server_ptr = XMLRPC_ServerCreate();
900 XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
902 /* store for later use */
903 ZEND_REGISTER_RESOURCE(return_value,server, le_xmlrpc_server);
908 /* {{{ proto int xmlrpc_server_destroy(resource server)
909 Destroys server resources */
910 PHP_FUNCTION(xmlrpc_server_destroy)
913 int bSuccess = FAILURE;
915 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
919 if(Z_TYPE_PP(arg1) == IS_RESOURCE) {
922 xmlrpc_server_data *server = zend_list_find(Z_LVAL_PP(arg1), &type);
924 if(server && type == le_xmlrpc_server) {
925 bSuccess = zend_list_delete(Z_LVAL_PP(arg1));
927 /* called by hashtable destructor
928 * destroy_server_data(server);
932 RETVAL_LONG(bSuccess == SUCCESS);
937 /* called by xmlrpc C engine as method handler for all registered methods.
938 * it then calls the corresponding PHP function to handle the method.
940 static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data)
942 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
944 zval* callback_params[3];
947 /* convert xmlrpc to native php types */
948 xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest));
950 /* setup data hoojum */
951 callback_params[0] = pData->xmlrpc_method;
952 callback_params[1] = xmlrpc_params;
953 callback_params[2] = pData->caller_params;
955 /* Use same C function for all methods */
957 /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
958 call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params TSRMLS_CC);
960 pData->php_executed = 1;
962 zval_dtor(xmlrpc_params);
963 FREE_ZVAL(xmlrpc_params);
968 /* called by the C server when it first receives an introspection request. We pass this on to
969 * our PHP listeners, if any
971 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data)
973 zval *retval_ptr, **php_function;
974 zval* callback_params[1];
975 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
978 MAKE_STD_ZVAL(retval_ptr);
979 Z_TYPE_P(retval_ptr) = IS_NULL;
981 /* setup data hoojum */
982 callback_params[0] = pData->caller_params;
984 /* loop through and call all registered callbacks */
985 zend_hash_internal_pointer_reset(Z_ARRVAL_P(pData->server->introspection_map));
987 if(zend_hash_get_current_data(Z_ARRVAL_P(pData->server->introspection_map),
988 (void**)&php_function) == SUCCESS) {
990 /* php func prototype: function string user_func($user_params) */
991 if(call_user_function(CG(function_table), NULL, *php_function,
992 retval_ptr, 1, callback_params TSRMLS_CC) == SUCCESS) {
994 STRUCT_XMLRPC_ERROR err = {0};
996 /* return value should be a string */
997 convert_to_string(retval_ptr);
999 xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_P(retval_ptr), &err);
1002 if(!XMLRPC_ServerAddIntrospectionData(server, xData)) {
1003 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", Z_STRVAL_PP(php_function));
1005 XMLRPC_CleanupValue(xData);
1008 /* could not create description */
1009 if(err.xml_elem_error.parser_code) {
1010 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()",
1011 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, Z_STRVAL_PP(php_function));
1014 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s()",
1015 Z_STRVAL_PP(php_function));
1020 /* user func failed */
1021 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error calling user introspection callback: %s()", Z_STRVAL_PP(php_function));
1028 zend_hash_move_forward(Z_ARRVAL_P(pData->server->introspection_map));
1031 /* so we don't call the same callbacks ever again */
1032 zend_hash_clean(Z_ARRVAL_P(pData->server->introspection_map));
1035 /* {{{ proto bool xmlrpc_server_register_method(resource server, string method_name, string function)
1036 Register a PHP function to handle method matching method_name */
1037 PHP_FUNCTION(xmlrpc_server_register_method)
1039 zval **method_key, **method_name, **handle, *method_name_save;
1041 xmlrpc_server_data* server;
1043 if (ZEND_NUM_ARGS() != 3 || (zend_get_parameters_ex(3, &handle, &method_key, &method_name) == FAILURE)) {
1047 server = zend_list_find(Z_LVAL_PP(handle), &type);
1049 if(type == le_xmlrpc_server) {
1050 /* register with C engine. every method just calls our standard callback,
1051 * and it then dispatches to php as necessary
1053 if(XMLRPC_ServerRegisterMethod(server->server_ptr, Z_STRVAL_PP(method_key), php_xmlrpc_callback)) {
1054 /* save for later use */
1055 MAKE_STD_ZVAL(method_name_save);
1056 *method_name_save = **method_name;
1057 zval_copy_ctor(method_name_save);
1059 /* register our php method */
1060 add_zval(server->method_map, Z_STRVAL_PP(method_key), &method_name_save);
1070 /* {{{ proto bool xmlrpc_server_register_introspection_callback(resource server, string function)
1071 Register a PHP function to generate documentation */
1072 PHP_FUNCTION(xmlrpc_server_register_introspection_callback)
1074 zval **method_name, **handle, *method_name_save;
1076 xmlrpc_server_data* server;
1078 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &method_name) == FAILURE)) {
1082 server = zend_list_find(Z_LVAL_PP(handle), &type);
1084 if(type == le_xmlrpc_server) {
1085 /* save for later use */
1086 MAKE_STD_ZVAL(method_name_save);
1087 *method_name_save = **method_name;
1088 zval_copy_ctor(method_name_save);
1090 /* register our php method */
1091 add_zval(server->introspection_map, NULL, &method_name_save);
1100 /* this function is itchin for a re-write */
1102 /* {{{ proto mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])
1103 Parses XML requests and call methods */
1104 PHP_FUNCTION(xmlrpc_server_call_method)
1106 xmlrpc_callback_data data = {0};
1107 XMLRPC_REQUEST xRequest;
1108 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
1109 xmlrpc_server_data* server;
1110 zval **rawxml, **caller_params, **handle, **output_opts = NULL;
1112 php_output_options out;
1113 int argc =ZEND_NUM_ARGS();
1115 if (argc < 3 || argc > 4 || (zend_get_parameters_ex(argc, &handle, &rawxml, &caller_params, &output_opts) != SUCCESS)) {
1118 /* user output options */
1120 set_output_options(&out, NULL);
1123 set_output_options(&out, *output_opts);
1126 server = zend_list_find(Z_LVAL_PP(handle), &type);
1128 if(type == le_xmlrpc_server) {
1129 /* HACK: use output encoding for now */
1130 input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
1132 /* generate an XMLRPC_REQUEST from the raw xml input */
1133 xRequest = XMLRPC_REQUEST_FromXML(Z_STRVAL_PP(rawxml), Z_STRLEN_PP(rawxml), &input_opts);
1136 const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
1137 zval **php_function;
1138 XMLRPC_VALUE xAnswer = NULL;
1139 MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */
1140 MAKE_STD_ZVAL(data.return_data);
1141 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 */
1142 Z_TYPE_P(data.xmlrpc_method) = IS_NULL;
1148 /* setup some data to pass to the callback function */
1149 Z_STRVAL_P(data.xmlrpc_method) = estrdup(methodname);
1150 Z_STRLEN_P(data.xmlrpc_method) = strlen(methodname);
1151 Z_TYPE_P(data.xmlrpc_method) = IS_STRING;
1152 data.caller_params = *caller_params;
1153 data.php_executed = 0;
1154 data.server = server;
1156 /* check if the called method has been previous registered */
1157 if(zend_hash_find(Z_ARRVAL_P(server->method_map),
1158 Z_STRVAL_P(data.xmlrpc_method),
1159 Z_STRLEN_P(data.xmlrpc_method) + 1,
1160 (void**)&php_function) == SUCCESS) {
1162 data.php_function = *php_function;
1165 /* We could just call the php method directly ourselves at this point, but we do this
1166 * with a C callback in case the xmlrpc library ever implements some cool usage stats,
1169 xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
1170 if(xAnswer && out.b_php_out) {
1171 zval_dtor(data.return_data);
1172 FREE_ZVAL(data.return_data);
1173 data.return_data = XMLRPC_to_PHP(xAnswer);
1174 } else if(data.php_executed && !out.b_php_out) {
1175 xAnswer = PHP_to_XMLRPC(&out, data.return_data);
1178 /* should we return data as xml? */
1179 if(!out.b_php_out) {
1180 XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
1185 /* automagically determine output serialization type from request type */
1186 if (out.b_auto_version) {
1187 XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
1189 out.xmlrpc_out.version = opts->version;
1192 /* set some required request hoojum */
1193 XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
1194 XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
1195 XMLRPC_RequestSetData(xResponse, xAnswer);
1196 XMLRPC_RequestSetMethodName(xResponse, methodname);
1199 outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
1201 RETVAL_STRINGL(outBuf, buf_len, 1);
1204 /* cleanup after ourselves. what a sty! */
1205 XMLRPC_RequestFree(xResponse, 0);
1207 } else { /* or as native php types? */
1208 *return_value = *data.return_data;
1209 zval_copy_ctor(return_value);
1212 /* cleanup after ourselves. what a sty! */
1213 zval_dtor(data.xmlrpc_method);
1214 FREE_ZVAL(data.xmlrpc_method);
1215 zval_dtor(data.return_data);
1216 FREE_ZVAL(data.return_data);
1219 XMLRPC_CleanupValue(xAnswer);
1222 XMLRPC_RequestFree(xRequest, 1);
1229 /* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
1230 Adds introspection documentation */
1231 PHP_FUNCTION(xmlrpc_server_add_introspection_data)
1233 zval **handle, **desc;
1235 xmlrpc_server_data* server;
1237 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &desc) == FAILURE)) {
1241 server = zend_list_find(Z_LVAL_PP(handle), &type);
1243 if (type == le_xmlrpc_server) {
1244 XMLRPC_VALUE xDesc = PHP_to_XMLRPC(NULL, *desc);
1246 int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
1247 XMLRPC_CleanupValue(xDesc);
1248 RETURN_LONG(retval);
1256 /* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
1257 Decodes XML into a list of method descriptions */
1258 PHP_FUNCTION(xmlrpc_parse_method_descriptions)
1260 zval **arg1, *retval;
1262 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
1266 convert_to_string_ex(arg1);
1268 if(return_value_used) {
1269 STRUCT_XMLRPC_ERROR err = {0};
1270 XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_PP(arg1), &err);
1272 retval = XMLRPC_to_PHP(xVal);
1275 *return_value = *retval;
1276 zval_copy_ctor(return_value);
1278 /* dust, sweep, and mop */
1279 XMLRPC_CleanupValue(xVal);
1281 /* could not create description */
1282 if(err.xml_elem_error.parser_code) {
1283 php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data",
1284 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
1286 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid xml structure. Unable to create introspection data");
1289 php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error. no method description created");
1300 #define XMLRPC_TYPE_COUNT 10
1301 #define XMLRPC_VECTOR_TYPE_COUNT 4
1302 #define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
1304 /* return a string matching a given xmlrpc type */
1305 static const char** get_type_str_mapping(void)
1307 static const char* str_mapping[TYPE_STR_MAP_SIZE];
1308 static int first = 1;
1310 /* warning. do not add/delete without changing size define */
1311 str_mapping[xmlrpc_none] = "none";
1312 str_mapping[xmlrpc_empty] = "empty";
1313 str_mapping[xmlrpc_nil] = "nil";
1314 str_mapping[xmlrpc_base64] = "base64";
1315 str_mapping[xmlrpc_boolean] = "boolean";
1316 str_mapping[xmlrpc_datetime] = "datetime";
1317 str_mapping[xmlrpc_double] = "double";
1318 str_mapping[xmlrpc_int] = "int";
1319 str_mapping[xmlrpc_string] = "string";
1320 str_mapping[xmlrpc_vector] = "vector";
1321 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none";
1322 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array";
1323 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed";
1324 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
1327 return (const char**)str_mapping;
1330 /* map an xmlrpc type to a string */
1331 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype)
1333 const char** str_mapping = get_type_str_mapping();
1335 if (vtype == xmlrpc_vector_none) {
1336 return str_mapping[type];
1338 return str_mapping[XMLRPC_TYPE_COUNT + vtype];
1342 /* map a string to an xmlrpc type */
1343 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str)
1345 const char** str_mapping = get_type_str_mapping();
1349 for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
1350 if (!strcmp(str_mapping[i], str)) {
1351 return (XMLRPC_VALUE_TYPE) i;
1358 /* map a string to an xmlrpc vector type */
1359 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str)
1361 const char** str_mapping = get_type_str_mapping();
1365 for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
1366 if (!strcmp(str_mapping[i], str)) {
1367 return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
1375 /* set a given value to a particular type.
1376 * note: this only works on strings, and only for date and base64,
1377 * which do not have native php types. black magic lies herein.
1379 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype)
1381 int bSuccess = FAILURE;
1384 /* we only really care about strings because they can represent
1385 * base64 and datetime. all other types have corresponding php types
1387 if (Z_TYPE_P(value) == IS_STRING) {
1388 if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
1389 const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
1392 MAKE_STD_ZVAL(type);
1394 Z_TYPE_P(type) = IS_STRING;
1395 Z_STRVAL_P(type) = estrdup(typestr);
1396 Z_STRLEN_P(type) = strlen(typestr);
1398 if(newtype == xmlrpc_datetime) {
1399 XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, value->value.str.val);
1401 time_t timestamp = XMLRPC_GetValueDateTime(v);
1405 MAKE_STD_ZVAL(ztimestamp);
1407 ztimestamp->type = IS_LONG;
1408 ztimestamp->value.lval = timestamp;
1410 convert_to_object(value);
1411 if(SUCCESS == zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL)) {
1412 bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR), (void *) &ztimestamp, sizeof(zval *), NULL);
1415 XMLRPC_CleanupValue(v);
1419 convert_to_object(value);
1420 bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL);
1428 /* return xmlrpc type of a php value */
1429 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(php_output_options* out, zval* value, zval** newvalue)
1431 XMLRPC_VALUE_TYPE type = xmlrpc_none;
1436 switch (Z_TYPE_P(value)) {
1438 if (XMLRPCG(allow_null) || (out && out->b_allow_null)) {
1441 type = xmlrpc_string;
1444 #ifndef BOOL_AS_LONG
1446 /* Right thing to do, but it breaks some legacy code. */
1448 type = xmlrpc_boolean;
1458 type = xmlrpc_double;
1461 type = xmlrpc_string;
1464 type = xmlrpc_string;
1467 /* jan. 2 2015 - see note in file header */
1468 #ifdef IS_CONSTANT_ARRAY
1469 case IS_CONSTANT_ARRAY:
1471 type = xmlrpc_vector;
1476 type = xmlrpc_vector;
1478 if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**) &attr) == SUCCESS) {
1479 if (Z_TYPE_PP(attr) == IS_STRING) {
1480 type = xmlrpc_str_as_type(Z_STRVAL_PP(attr));
1487 /* if requested, return an unmolested (magic removed) copy of the value */
1491 if ((type == xmlrpc_base64 && Z_TYPE_P(value) != IS_NULL) || type == xmlrpc_datetime) {
1492 if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), (void**) &val) == SUCCESS) {
1505 /* {{{ proto bool xmlrpc_set_type(string value, string type)
1506 Sets xmlrpc type, base64 or datetime or nil, for a PHP string value */
1507 PHP_FUNCTION(xmlrpc_set_type)
1510 XMLRPC_VALUE_TYPE vtype;
1512 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &arg, &type) == FAILURE)) {
1516 convert_to_string_ex(type);
1517 vtype = xmlrpc_str_as_type(Z_STRVAL_PP(type));
1518 if (vtype != xmlrpc_none) {
1519 if (set_zval_xmlrpc_type(*arg, vtype) == SUCCESS) {
1523 zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", Z_STRVAL_PP(type));
1529 /* {{{ proto string xmlrpc_get_type(mixed value)
1530 Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */
1531 PHP_FUNCTION(xmlrpc_get_type)
1534 XMLRPC_VALUE_TYPE type;
1535 XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
1537 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1541 type = get_zval_xmlrpc_type(NULL, *arg, 0);
1542 if (type == xmlrpc_vector) {
1543 vtype = determine_vector_type(Z_ARRVAL_PP(arg));
1546 RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype), 1);
1550 /* {{{ proto bool xmlrpc_is_fault(array)
1551 Determines if an array value represents an XMLRPC fault. */
1552 PHP_FUNCTION(xmlrpc_is_fault)
1556 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1560 if (Z_TYPE_PP(arg) != IS_ARRAY) {
1561 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array argument expected");
1563 /* The "correct" way to do this would be to call the xmlrpc
1564 * library XMLRPC_ValueIsFault() func. However, doing that
1565 * would require us to create an xmlrpc value from the php
1566 * array, which is rather expensive, especially if it was
1567 * a big array. Thus, we resort to this not so clever hackery.
1569 if (zend_hash_find(Z_ARRVAL_PP(arg), FAULT_CODE, FAULT_CODE_LEN + 1, (void**) &val) == SUCCESS &&
1570 zend_hash_find(Z_ARRVAL_PP(arg), FAULT_STRING, FAULT_STRING_LEN + 1, (void**) &val) == SUCCESS) {