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 +----------------------------------------------------------------------+
56 /**********************************************************************
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 **********************************************************************/
69 #include "ext/standard/info.h"
71 #include "php_xmlrpc.h"
74 #define PHP_EXT_VERSION "0.51"
76 /* ====================
77 * Thierry - Dec. 17 2009
78 * patch for php - bug first triggered on f12 with php-5.3
79 * http://pecl.php.net/bugs/bug.php?id=14369
80 * http://remi.fedorapeople.org/ncurses-1.0.0-php53.patch
81 * I'm taking out the 'static' stuff as after pre-processing it appears to be part of the macro expansion already
83 #if ZEND_MODULE_API_NO >= 20071006
84 // No more defined with PHP 5.3.0
85 ZEND_BEGIN_ARG_INFO(first_arg_force_ref, 0)
89 ZEND_BEGIN_ARG_INFO(second_arg_force_ref, 0)
94 /* ==================== end patch */
96 /* ====================
97 * Thierry - Jan. 30 2013
98 * patch for php - issues first triggered on f18 with php-5.4
99 * https://bugs.php.net/bug.php?id=60016
100 * https://bugs.php.net/bug.php?id=60016
102 #if ZEND_MODULE_API_NO >= 20100525
103 // No more defined with PHP 5.4
104 #define function_entry zend_function_entry
107 /* ==================== end patch */
109 /* ========== additional notes
110 * in the process, I've also come across the following resources that might help
111 * if/when zend_get_parameters_ex gets deprecated (only generates warnings for now)
113 * http://developers.evrsoft.com/docs/php/zend.arguments.deprecated-retrieval.shtml
114 * explains how the old (our) stuff works
116 * http://www.hospedajeydominios.com/mambo/documentacion-manual_php-pagina-zend_arguments_retrieval.html
117 * gives info on the new scheme
119 * I'm taking tha risk as the changes seem to mean major surgery ...
122 /* You should tweak config.m4 so this symbol (or some else suitable)
125 ZEND_DECLARE_MODULE_GLOBALS(xmlrpc)
127 static int le_xmlrpc_server;
129 function_entry xmlrpc_functions[] = {
130 PHP_FE(xmlrpc_encode, NULL)
131 PHP_FE(xmlrpc_decode, NULL)
132 PHP_FE(xmlrpc_decode_request, second_arg_force_ref)
133 PHP_FE(xmlrpc_encode_request, NULL)
134 PHP_FE(xmlrpc_get_type, NULL)
135 PHP_FE(xmlrpc_set_type, first_arg_force_ref)
136 PHP_FE(xmlrpc_is_fault, NULL)
137 PHP_FE(xmlrpc_server_create, NULL)
138 PHP_FE(xmlrpc_server_destroy, NULL)
139 PHP_FE(xmlrpc_server_register_method, NULL)
140 PHP_FE(xmlrpc_server_call_method, NULL)
141 PHP_FE(xmlrpc_parse_method_descriptions, NULL)
142 PHP_FE(xmlrpc_server_add_introspection_data, NULL)
143 PHP_FE(xmlrpc_server_register_introspection_callback, NULL)
147 zend_module_entry xmlrpc_module_entry = {
148 STANDARD_MODULE_HEADER,
152 PHP_MSHUTDOWN(xmlrpc),
153 PHP_RINIT(xmlrpc), /* Replace with NULL if there's nothing to do at request start */
154 PHP_RSHUTDOWN(xmlrpc), /* Replace with NULL if there's nothing to do at request end */
157 STANDARD_MODULE_PROPERTIES
160 #ifdef COMPILE_DL_XMLRPC
161 ZEND_GET_MODULE(xmlrpc)
163 # include "zend_arg_defs.c"
168 STD_PHP_INI_BOOLEAN("xmlrpc.allow_null", "0", PHP_INI_ALL, OnUpdateBool, allow_null, zend_xmlrpc_globals, xmlrpc_globals)
171 static void php_xmlrpc_init_globals(zend_xmlrpc_globals *xmlrpc_globals)
173 memset(xmlrpc_globals, 0, sizeof(zend_xmlrpc_globals));
176 /*******************************
177 * local structures and defines *
178 *******************************/
180 /* per server data */
181 typedef struct _xmlrpc_server_data {
183 zval* introspection_map;
184 XMLRPC_SERVER server_ptr;
185 } xmlrpc_server_data;
188 /* how to format output */
189 typedef struct _php_output_options {
193 STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out;
194 } php_output_options;
196 /* data passed to C callback */
197 typedef struct _xmlrpc_callback_data {
202 xmlrpc_server_data* server;
204 } xmlrpc_callback_data;
207 #define OUTPUT_TYPE_KEY "output_type"
208 #define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1)
209 #define OUTPUT_TYPE_VALUE_PHP "php"
210 #define OUTPUT_TYPE_VALUE_XML "xml"
212 #define ALLOW_NULL_KEY "allow_null"
213 #define ALLOW_NULL_KEY_LEN (sizeof(ALLOW_NULL_KEY) - 1)
215 #define VERBOSITY_KEY "verbosity"
216 #define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1)
217 #define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space"
218 #define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only"
219 #define VERBOSITY_VALUE_PRETTY "pretty"
221 #define ESCAPING_KEY "escaping"
222 #define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1)
223 #define ESCAPING_VALUE_CDATA "cdata"
224 #define ESCAPING_VALUE_NON_ASCII "non-ascii"
225 #define ESCAPING_VALUE_NON_PRINT "non-print"
226 #define ESCAPING_VALUE_MARKUP "markup"
228 #define VERSION_KEY "version"
229 #define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1)
230 #define VERSION_VALUE_SIMPLE "simple"
231 #define VERSION_VALUE_XMLRPC "xmlrpc"
232 #define VERSION_VALUE_SOAP11 "soap 1.1"
233 #define VERSION_VALUE_AUTO "auto"
235 #define ENCODING_KEY "encoding"
236 #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1)
237 #define ENCODING_DEFAULT "iso-8859-1"
240 #define OBJECT_TYPE_ATTR "xmlrpc_type"
241 #define OBJECT_VALUE_ATTR "scalar"
242 #define OBJECT_VALUE_TS_ATTR "timestamp"
245 #define FAULT_CODE "faultCode"
246 #define FAULT_CODE_LEN (sizeof(FAULT_CODE) - 1)
247 #define FAULT_STRING "faultString"
248 #define FAULT_STRING_LEN (sizeof(FAULT_STRING) - 1)
250 /***********************
251 * forward declarations *
252 ***********************/
253 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(php_output_options* out, zval* value, zval** newvalue);
254 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data);
255 int sset_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
256 zval* decode_request_worker(zval* xml_in, zval* encoding_in, zval* method_name_out);
257 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype);
258 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str);
259 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str);
260 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
262 /*********************
263 * startup / shutdown *
264 *********************/
266 static void destroy_server_data(xmlrpc_server_data *server)
269 XMLRPC_ServerDestroy(server->server_ptr);
271 zval_dtor(server->method_map);
272 FREE_ZVAL(server->method_map);
274 zval_dtor(server->introspection_map);
275 FREE_ZVAL(server->introspection_map);
281 /* called when server is being destructed. either when xmlrpc_server_destroy
282 * is called, or when request ends. */
283 static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
285 if (rsrc && rsrc->ptr) {
286 destroy_server_data((xmlrpc_server_data*) rsrc->ptr);
291 PHP_MINIT_FUNCTION(xmlrpc)
293 ZEND_INIT_MODULE_GLOBALS(xmlrpc, php_xmlrpc_init_globals, NULL);
295 REGISTER_INI_ENTRIES();
297 le_xmlrpc_server = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);
302 /* module shutdown */
303 PHP_MSHUTDOWN_FUNCTION(xmlrpc)
308 /* Remove if there's nothing to do at request start */
309 PHP_RINIT_FUNCTION(xmlrpc)
314 /* Remove if there's nothing to do at request end */
315 PHP_RSHUTDOWN_FUNCTION(xmlrpc)
320 /* display info in phpinfo() */
321 PHP_MINFO_FUNCTION(xmlrpc)
323 php_info_print_table_start();
324 php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString());
325 php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION);
326 php_info_print_table_row(2, "author", "Dan Libby");
327 php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net");
328 php_info_print_table_row(2, "open sourced by", "Epinions.com");
329 php_info_print_table_end();
336 /* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc).
337 * Could easily be further generalized to work with objects.
340 static int add_long(zval* list, char* id, int num) {
341 if(id) return add_assoc_long(list, id, num);
342 else return add_next_index_long(list, num);
345 static int add_double(zval* list, char* id, double num) {
346 if(id) return add_assoc_double(list, id, num);
347 else return add_next_index_double(list, num);
350 static int add_string(zval* list, char* id, char* string, int duplicate) {
351 if(id) return add_assoc_string(list, id, string, duplicate);
352 else return add_next_index_string(list, string, duplicate);
355 static int add_stringl(zval* list, char* id, char* string, uint length, int duplicate) {
356 if(id) return add_assoc_stringl(list, id, string, length, duplicate);
357 else return add_next_index_stringl(list, string, length, duplicate);
362 static int add_zval(zval* list, const char* id, zval** val)
366 return zend_hash_update(Z_ARRVAL_P(list), (char*) id, strlen(id) + 1, (void *) val, sizeof(zval **), NULL);
368 return zend_hash_next_index_insert(Z_ARRVAL_P(list), (void *) val, sizeof(zval **), NULL);
371 /* what is the correct return on error? */
375 #define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0)
378 /*************************
379 * input / output options *
380 *************************/
382 /* parse an array (user input) into output options suitable for use by xmlrpc engine
383 * and determine whether to return data as xml or php vars */
384 static void set_output_options(php_output_options* options, zval* output_opts)
391 options->b_php_out = 0;
392 options->b_auto_version = 1;
393 options->b_allow_null = 0;
394 options->xmlrpc_out.version = xmlrpc_version_1_0;
395 options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT;
396 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
397 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping;
399 if (output_opts && Z_TYPE_P(output_opts) == IS_ARRAY) {
403 if (zend_hash_find(Z_ARRVAL_P(output_opts), ALLOW_NULL_KEY, ALLOW_NULL_KEY_LEN + 1, (void**) &val) == SUCCESS) {
404 if (Z_TYPE_PP(val) == IS_BOOL) {
405 if (Z_LVAL_PP(val)) {
406 options->b_allow_null = 1;
408 options->b_allow_null = 0;
413 /* type of output (xml/php) */
414 if (zend_hash_find(Z_ARRVAL_P(output_opts), OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**) &val) == SUCCESS) {
415 if (Z_TYPE_PP(val) == IS_STRING) {
416 if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_PHP)) {
417 options->b_php_out = 1;
418 } else if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_XML)) {
419 options->b_php_out = 0;
424 /* verbosity of generated xml */
425 if (zend_hash_find(Z_ARRVAL_P(output_opts), VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, (void**) &val) == SUCCESS) {
426 if (Z_TYPE_PP(val) == IS_STRING) {
427 if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NO_WHITE_SPACE)) {
428 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space;
429 } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NEWLINES_ONLY)) {
430 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only;
431 } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_PRETTY)) {
432 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
437 /* version of xml to output */
438 if (zend_hash_find(Z_ARRVAL_P(output_opts), VERSION_KEY, VERSION_KEY_LEN + 1, (void**) &val) == SUCCESS) {
439 if (Z_TYPE_PP(val) == IS_STRING) {
440 options->b_auto_version = 0;
441 if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_XMLRPC)) {
442 options->xmlrpc_out.version = xmlrpc_version_1_0;
443 } else if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_SIMPLE)) {
444 options->xmlrpc_out.version = xmlrpc_version_simple;
445 } else if (!strcmp((*val)->value.str.val, VERSION_VALUE_SOAP11)) {
446 options->xmlrpc_out.version = xmlrpc_version_soap_1_1;
447 } else { /* if(!strcmp((*val)->value.str.val, VERSION_VALUE_AUTO)) { */
448 options->b_auto_version = 1;
453 /* encoding code set */
454 if(zend_hash_find(Z_ARRVAL_P(output_opts),
455 ENCODING_KEY, ENCODING_KEY_LEN + 1,
456 (void**)&val) == SUCCESS) {
457 if(Z_TYPE_PP(val) == IS_STRING) {
458 options->xmlrpc_out.xml_elem_opts.encoding = estrdup(Z_STRVAL_PP(val));
462 /* escaping options */
463 if(zend_hash_find(Z_ARRVAL_P(output_opts),
464 ESCAPING_KEY, ESCAPING_KEY_LEN + 1,
465 (void**)&val) == SUCCESS) {
466 /* multiple values allowed. check if array */
467 if(Z_TYPE_PP(val) == IS_ARRAY) {
469 zend_hash_internal_pointer_reset(Z_ARRVAL_PP(val));
470 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping;
472 if(zend_hash_get_current_data(Z_ARRVAL_PP(val), (void**)&iter_val) == SUCCESS) {
473 if(Z_TYPE_PP(iter_val) == IS_STRING && Z_STRVAL_PP(iter_val)) {
474 if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_CDATA)) {
475 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping;
477 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_ASCII)) {
478 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping;
480 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_PRINT)) {
481 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping;
483 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_MARKUP)) {
484 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping;
492 zend_hash_move_forward(Z_ARRVAL_PP(val));
495 /* else, check for single value */
496 else if(Z_TYPE_PP(val) == IS_STRING) {
497 if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_CDATA)) {
498 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping;
500 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_ASCII)) {
501 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping;
503 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_PRINT)) {
504 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping;
506 else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_MARKUP)) {
507 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping;
520 /* php arrays have no distinction between array and struct types.
521 * they even allow mixed. Thus, we determine the type by iterating
522 * through the entire array and figuring out each element.
523 * room for some optimation here if we stop after a specific # of elements.
525 static XMLRPC_VECTOR_TYPE determine_vector_type (HashTable *ht)
527 int bArray = 0, bStruct = 0, bMixed = 0;
528 unsigned long num_index;
531 zend_hash_internal_pointer_reset(ht);
533 int res = my_zend_hash_get_current_key(ht, &my_key, &num_index);
534 if(res == HASH_KEY_IS_LONG) {
541 else if(res == HASH_KEY_NON_EXISTANT) {
544 else if(res == HASH_KEY_IS_STRING) {
552 zend_hash_move_forward(ht);
554 return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array);
557 /* recursively convert php values into xmlrpc values */
558 static XMLRPC_VALUE PHP_to_XMLRPC_worker (php_output_options* out, const char* key, zval* in_val, int depth)
560 XMLRPC_VALUE xReturn = NULL;
563 XMLRPC_VALUE_TYPE type = get_zval_xmlrpc_type(out, in_val, &val);
567 xReturn = XMLRPC_CreateValueEmpty();
568 XMLRPC_SetValueID(xReturn, key, 0);
571 xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
573 case xmlrpc_datetime:
574 convert_to_string(val);
575 xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL_P(val));
578 convert_to_boolean(val);
579 xReturn = XMLRPC_CreateValueBoolean(key, Z_LVAL_P(val));
582 convert_to_long(val);
583 xReturn = XMLRPC_CreateValueInt(key, Z_LVAL_P(val));
586 convert_to_double(val);
587 xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL_P(val));
590 convert_to_string(val);
591 xReturn = XMLRPC_CreateValueString(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
595 unsigned long num_index;
599 convert_to_array(val);
601 xReturn = XMLRPC_CreateVector(key, determine_vector_type(Z_ARRVAL_P(val)));
603 zend_hash_internal_pointer_reset(Z_ARRVAL_P(val));
605 int res = my_zend_hash_get_current_key(Z_ARRVAL_P(val), &my_key, &num_index);
606 if(res == HASH_KEY_IS_LONG) {
607 if(zend_hash_get_current_data(Z_ARRVAL_P(val), (void**)&pIter) == SUCCESS) {
608 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(out, 0, *pIter, depth++));
611 else if(res == HASH_KEY_NON_EXISTANT) {
614 else if(res == HASH_KEY_IS_STRING) {
615 if(zend_hash_get_current_data(Z_ARRVAL_P(val), (void**)&pIter) == SUCCESS) {
616 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(out, my_key, *pIter, depth++));
620 zend_hash_move_forward(Z_ARRVAL_P(val));
632 static XMLRPC_VALUE PHP_to_XMLRPC(php_output_options* out, zval* root_val)
634 return PHP_to_XMLRPC_worker(out, NULL, root_val, 0);
637 /* recursively convert xmlrpc values into php values */
638 static zval* XMLRPC_to_PHP(XMLRPC_VALUE el)
644 XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
646 MAKE_STD_ZVAL(elem); /* init. very important. spent a frustrating day finding this out. */
651 Z_TYPE_P(elem) = IS_NULL;
654 pStr = XMLRPC_GetValueString(el);
656 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
657 Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
658 Z_TYPE_P(elem) = IS_STRING;
662 Z_LVAL_P(elem) = XMLRPC_GetValueInt(el);
663 Z_TYPE_P(elem) = IS_LONG;
666 Z_LVAL_P(elem) = XMLRPC_GetValueBoolean(el);
667 Z_TYPE_P(elem) = IS_BOOL;
670 Z_DVAL_P(elem) = XMLRPC_GetValueDouble(el);
671 Z_TYPE_P(elem) = IS_DOUBLE;
673 case xmlrpc_datetime:
674 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
675 Z_STRVAL_P(elem) = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), Z_STRLEN_P(elem));
676 Z_TYPE_P(elem) = IS_STRING;
679 pStr = XMLRPC_GetValueBase64(el);
681 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
682 Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
683 Z_TYPE_P(elem) = IS_STRING;
689 XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
692 zval *val = XMLRPC_to_PHP(xIter);
694 add_zval(elem, XMLRPC_GetValueID(xIter), &val);
696 xIter = XMLRPC_VectorNext(el);
703 set_zval_xmlrpc_type(elem, type);
708 /* {{{ proto string xmlrpc_encode_request(string method, mixed params)
709 Generates XML for a method request */
710 PHP_FUNCTION(xmlrpc_encode_request)
712 XMLRPC_REQUEST xRequest = NULL;
713 zval **method, **vals, **out_opts;
715 php_output_options out;
717 if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 3 || (zend_get_parameters_ex(ZEND_NUM_ARGS(), &method, &vals, &out_opts) == FAILURE)) {
718 WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
721 set_output_options(&out, (ZEND_NUM_ARGS() == 3) ? *out_opts : 0);
723 if(return_value_used) {
724 xRequest = XMLRPC_RequestNew();
727 XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
728 if (Z_TYPE_PP(method) == IS_NULL) {
729 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
731 XMLRPC_RequestSetMethodName(xRequest, Z_STRVAL_PP(method));
732 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
734 if (Z_TYPE_PP(vals) != IS_NULL) {
735 XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(&out, *vals));
738 outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
740 RETVAL_STRING(outBuf, 1);
743 XMLRPC_RequestFree(xRequest, 1);
749 /* {{{ proto string xmlrpc_encode(mixed value)
750 Generates XML for a PHP value */
751 PHP_FUNCTION(xmlrpc_encode)
753 XMLRPC_VALUE xOut = NULL;
757 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
761 if( return_value_used ) {
762 /* convert native php type to xmlrpc type */
763 xOut = PHP_to_XMLRPC(NULL, *arg1);
765 /* generate raw xml from xmlrpc data */
766 outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
770 RETVAL_STRING(outBuf, 1);
774 XMLRPC_CleanupValue(xOut);
781 zval* decode_request_worker (zval* xml_in, zval* encoding_in, zval* method_name_out)
784 XMLRPC_REQUEST response;
785 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}};
786 opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(Z_STRVAL_P(encoding_in)) : ENCODING_DEFAULT;
788 /* generate XMLRPC_REQUEST from raw xml */
789 response = XMLRPC_REQUEST_FromXML(Z_STRVAL_P(xml_in), Z_STRLEN_P(xml_in), &opts);
791 /* convert xmlrpc data to native php types */
792 retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response));
794 if(XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
795 if(method_name_out) {
796 convert_to_string(method_name_out);
797 Z_TYPE_P(method_name_out) = IS_STRING;
798 Z_STRVAL_P(method_name_out) = estrdup(XMLRPC_RequestGetMethodName(response));
799 Z_STRLEN_P(method_name_out) = strlen(Z_STRVAL_P(method_name_out));
803 /* dust, sweep, and mop */
804 XMLRPC_RequestFree(response, 1);
809 /* {{{ proto array xmlrpc_decode_request(string xml, string& method [, string encoding])
810 Decodes XML into native PHP types */
811 PHP_FUNCTION(xmlrpc_decode_request)
813 zval **xml, **method, **encoding = NULL;
814 int argc = ZEND_NUM_ARGS();
816 if (argc < 2 || argc > 3 || (zend_get_parameters_ex(argc, &xml, &method, &encoding) == FAILURE)) {
820 convert_to_string_ex(xml);
821 convert_to_string_ex(method);
823 convert_to_string_ex(encoding);
826 if(return_value_used) {
827 zval* retval = decode_request_worker(*xml, encoding ? *encoding : NULL, *method);
829 *return_value = *retval;
837 /* {{{ proto array xmlrpc_decode(string xml [, string encoding])
838 Decodes XML into native PHP types */
839 PHP_FUNCTION(xmlrpc_decode)
841 zval **arg1, **arg2 = NULL;
842 int argc = ZEND_NUM_ARGS();
844 if (argc < 1 || argc > 2 || (zend_get_parameters_ex(argc, &arg1, &arg2) == FAILURE)) {
848 convert_to_string_ex(arg1);
850 convert_to_string_ex(arg2);
853 if(return_value_used) {
854 zval* retval = decode_request_worker(*arg1, arg2 ? *arg2 : NULL, NULL);
856 *return_value = *retval;
864 /*************************
865 * server related methods *
866 *************************/
868 /* {{{ proto resource xmlrpc_server_create(void)
869 Creates an xmlrpc server */
870 PHP_FUNCTION(xmlrpc_server_create)
872 if(ZEND_NUM_ARGS() != 0) {
876 if(return_value_used) {
877 zval *method_map, *introspection_map;
878 xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
879 MAKE_STD_ZVAL(method_map);
880 MAKE_STD_ZVAL(introspection_map);
882 array_init(method_map);
883 array_init(introspection_map);
885 /* allocate server data. free'd in destroy_server_data() */
886 server->method_map = method_map;
887 server->introspection_map = introspection_map;
888 server->server_ptr = XMLRPC_ServerCreate();
890 XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
892 /* store for later use */
893 ZEND_REGISTER_RESOURCE(return_value,server, le_xmlrpc_server);
898 /* {{{ proto int xmlrpc_server_destroy(resource server)
899 Destroys server resources */
900 PHP_FUNCTION(xmlrpc_server_destroy)
903 int bSuccess = FAILURE;
905 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
909 if(Z_TYPE_PP(arg1) == IS_RESOURCE) {
912 xmlrpc_server_data *server = zend_list_find(Z_LVAL_PP(arg1), &type);
914 if(server && type == le_xmlrpc_server) {
915 bSuccess = zend_list_delete(Z_LVAL_PP(arg1));
917 /* called by hashtable destructor
918 * destroy_server_data(server);
922 RETVAL_LONG(bSuccess == SUCCESS);
927 /* called by xmlrpc C engine as method handler for all registered methods.
928 * it then calls the corresponding PHP function to handle the method.
930 static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data)
932 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
934 zval* callback_params[3];
937 /* convert xmlrpc to native php types */
938 xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest));
940 /* setup data hoojum */
941 callback_params[0] = pData->xmlrpc_method;
942 callback_params[1] = xmlrpc_params;
943 callback_params[2] = pData->caller_params;
945 /* Use same C function for all methods */
947 /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
948 call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params TSRMLS_CC);
950 pData->php_executed = 1;
952 zval_dtor(xmlrpc_params);
953 FREE_ZVAL(xmlrpc_params);
958 /* called by the C server when it first receives an introspection request. We pass this on to
959 * our PHP listeners, if any
961 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data)
963 zval *retval_ptr, **php_function;
964 zval* callback_params[1];
965 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
968 MAKE_STD_ZVAL(retval_ptr);
969 Z_TYPE_P(retval_ptr) = IS_NULL;
971 /* setup data hoojum */
972 callback_params[0] = pData->caller_params;
974 /* loop through and call all registered callbacks */
975 zend_hash_internal_pointer_reset(Z_ARRVAL_P(pData->server->introspection_map));
977 if(zend_hash_get_current_data(Z_ARRVAL_P(pData->server->introspection_map),
978 (void**)&php_function) == SUCCESS) {
980 /* php func prototype: function string user_func($user_params) */
981 if(call_user_function(CG(function_table), NULL, *php_function,
982 retval_ptr, 1, callback_params TSRMLS_CC) == SUCCESS) {
984 STRUCT_XMLRPC_ERROR err = {0};
986 /* return value should be a string */
987 convert_to_string(retval_ptr);
989 xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_P(retval_ptr), &err);
992 if(!XMLRPC_ServerAddIntrospectionData(server, xData)) {
993 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", Z_STRVAL_PP(php_function));
995 XMLRPC_CleanupValue(xData);
998 /* could not create description */
999 if(err.xml_elem_error.parser_code) {
1000 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()",
1001 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, Z_STRVAL_PP(php_function));
1004 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s()",
1005 Z_STRVAL_PP(php_function));
1010 /* user func failed */
1011 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error calling user introspection callback: %s()", Z_STRVAL_PP(php_function));
1018 zend_hash_move_forward(Z_ARRVAL_P(pData->server->introspection_map));
1021 /* so we don't call the same callbacks ever again */
1022 zend_hash_clean(Z_ARRVAL_P(pData->server->introspection_map));
1025 /* {{{ proto bool xmlrpc_server_register_method(resource server, string method_name, string function)
1026 Register a PHP function to handle method matching method_name */
1027 PHP_FUNCTION(xmlrpc_server_register_method)
1029 zval **method_key, **method_name, **handle, *method_name_save;
1031 xmlrpc_server_data* server;
1033 if (ZEND_NUM_ARGS() != 3 || (zend_get_parameters_ex(3, &handle, &method_key, &method_name) == FAILURE)) {
1037 server = zend_list_find(Z_LVAL_PP(handle), &type);
1039 if(type == le_xmlrpc_server) {
1040 /* register with C engine. every method just calls our standard callback,
1041 * and it then dispatches to php as necessary
1043 if(XMLRPC_ServerRegisterMethod(server->server_ptr, Z_STRVAL_PP(method_key), php_xmlrpc_callback)) {
1044 /* save for later use */
1045 MAKE_STD_ZVAL(method_name_save);
1046 *method_name_save = **method_name;
1047 zval_copy_ctor(method_name_save);
1049 /* register our php method */
1050 add_zval(server->method_map, Z_STRVAL_PP(method_key), &method_name_save);
1060 /* {{{ proto bool xmlrpc_server_register_introspection_callback(resource server, string function)
1061 Register a PHP function to generate documentation */
1062 PHP_FUNCTION(xmlrpc_server_register_introspection_callback)
1064 zval **method_name, **handle, *method_name_save;
1066 xmlrpc_server_data* server;
1068 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &method_name) == FAILURE)) {
1072 server = zend_list_find(Z_LVAL_PP(handle), &type);
1074 if(type == le_xmlrpc_server) {
1075 /* save for later use */
1076 MAKE_STD_ZVAL(method_name_save);
1077 *method_name_save = **method_name;
1078 zval_copy_ctor(method_name_save);
1080 /* register our php method */
1081 add_zval(server->introspection_map, NULL, &method_name_save);
1090 /* this function is itchin for a re-write */
1092 /* {{{ proto mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])
1093 Parses XML requests and call methods */
1094 PHP_FUNCTION(xmlrpc_server_call_method)
1096 xmlrpc_callback_data data = {0};
1097 XMLRPC_REQUEST xRequest;
1098 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
1099 xmlrpc_server_data* server;
1100 zval **rawxml, **caller_params, **handle, **output_opts = NULL;
1102 php_output_options out;
1103 int argc =ZEND_NUM_ARGS();
1105 if (argc < 3 || argc > 4 || (zend_get_parameters_ex(argc, &handle, &rawxml, &caller_params, &output_opts) != SUCCESS)) {
1108 /* user output options */
1110 set_output_options(&out, NULL);
1113 set_output_options(&out, *output_opts);
1116 server = zend_list_find(Z_LVAL_PP(handle), &type);
1118 if(type == le_xmlrpc_server) {
1119 /* HACK: use output encoding for now */
1120 input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
1122 /* generate an XMLRPC_REQUEST from the raw xml input */
1123 xRequest = XMLRPC_REQUEST_FromXML(Z_STRVAL_PP(rawxml), Z_STRLEN_PP(rawxml), &input_opts);
1126 const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
1127 zval **php_function;
1128 XMLRPC_VALUE xAnswer = NULL;
1129 MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */
1130 MAKE_STD_ZVAL(data.return_data);
1131 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 */
1132 Z_TYPE_P(data.xmlrpc_method) = IS_NULL;
1138 /* setup some data to pass to the callback function */
1139 Z_STRVAL_P(data.xmlrpc_method) = estrdup(methodname);
1140 Z_STRLEN_P(data.xmlrpc_method) = strlen(methodname);
1141 Z_TYPE_P(data.xmlrpc_method) = IS_STRING;
1142 data.caller_params = *caller_params;
1143 data.php_executed = 0;
1144 data.server = server;
1146 /* check if the called method has been previous registered */
1147 if(zend_hash_find(Z_ARRVAL_P(server->method_map),
1148 Z_STRVAL_P(data.xmlrpc_method),
1149 Z_STRLEN_P(data.xmlrpc_method) + 1,
1150 (void**)&php_function) == SUCCESS) {
1152 data.php_function = *php_function;
1155 /* We could just call the php method directly ourselves at this point, but we do this
1156 * with a C callback in case the xmlrpc library ever implements some cool usage stats,
1159 xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
1160 if(xAnswer && out.b_php_out) {
1161 zval_dtor(data.return_data);
1162 FREE_ZVAL(data.return_data);
1163 data.return_data = XMLRPC_to_PHP(xAnswer);
1164 } else if(data.php_executed && !out.b_php_out) {
1165 xAnswer = PHP_to_XMLRPC(&out, data.return_data);
1168 /* should we return data as xml? */
1169 if(!out.b_php_out) {
1170 XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
1175 /* automagically determine output serialization type from request type */
1176 if (out.b_auto_version) {
1177 XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
1179 out.xmlrpc_out.version = opts->version;
1182 /* set some required request hoojum */
1183 XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
1184 XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
1185 XMLRPC_RequestSetData(xResponse, xAnswer);
1186 XMLRPC_RequestSetMethodName(xResponse, methodname);
1189 outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
1191 RETVAL_STRINGL(outBuf, buf_len, 1);
1194 /* cleanup after ourselves. what a sty! */
1195 XMLRPC_RequestFree(xResponse, 0);
1197 } else { /* or as native php types? */
1198 *return_value = *data.return_data;
1199 zval_copy_ctor(return_value);
1202 /* cleanup after ourselves. what a sty! */
1203 zval_dtor(data.xmlrpc_method);
1204 FREE_ZVAL(data.xmlrpc_method);
1205 zval_dtor(data.return_data);
1206 FREE_ZVAL(data.return_data);
1209 XMLRPC_CleanupValue(xAnswer);
1212 XMLRPC_RequestFree(xRequest, 1);
1219 /* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
1220 Adds introspection documentation */
1221 PHP_FUNCTION(xmlrpc_server_add_introspection_data)
1223 zval **handle, **desc;
1225 xmlrpc_server_data* server;
1227 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &desc) == FAILURE)) {
1231 server = zend_list_find(Z_LVAL_PP(handle), &type);
1233 if (type == le_xmlrpc_server) {
1234 XMLRPC_VALUE xDesc = PHP_to_XMLRPC(NULL, *desc);
1236 int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
1237 XMLRPC_CleanupValue(xDesc);
1238 RETURN_LONG(retval);
1246 /* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
1247 Decodes XML into a list of method descriptions */
1248 PHP_FUNCTION(xmlrpc_parse_method_descriptions)
1250 zval **arg1, *retval;
1252 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
1256 convert_to_string_ex(arg1);
1258 if(return_value_used) {
1259 STRUCT_XMLRPC_ERROR err = {0};
1260 XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_PP(arg1), &err);
1262 retval = XMLRPC_to_PHP(xVal);
1265 *return_value = *retval;
1266 zval_copy_ctor(return_value);
1268 /* dust, sweep, and mop */
1269 XMLRPC_CleanupValue(xVal);
1271 /* could not create description */
1272 if(err.xml_elem_error.parser_code) {
1273 php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data",
1274 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
1276 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid xml structure. Unable to create introspection data");
1279 php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error. no method description created");
1290 #define XMLRPC_TYPE_COUNT 10
1291 #define XMLRPC_VECTOR_TYPE_COUNT 4
1292 #define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
1294 /* return a string matching a given xmlrpc type */
1295 static const char** get_type_str_mapping(void)
1297 static const char* str_mapping[TYPE_STR_MAP_SIZE];
1298 static int first = 1;
1300 /* warning. do not add/delete without changing size define */
1301 str_mapping[xmlrpc_none] = "none";
1302 str_mapping[xmlrpc_empty] = "empty";
1303 str_mapping[xmlrpc_nil] = "nil";
1304 str_mapping[xmlrpc_base64] = "base64";
1305 str_mapping[xmlrpc_boolean] = "boolean";
1306 str_mapping[xmlrpc_datetime] = "datetime";
1307 str_mapping[xmlrpc_double] = "double";
1308 str_mapping[xmlrpc_int] = "int";
1309 str_mapping[xmlrpc_string] = "string";
1310 str_mapping[xmlrpc_vector] = "vector";
1311 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none";
1312 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array";
1313 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed";
1314 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
1317 return (const char**)str_mapping;
1320 /* map an xmlrpc type to a string */
1321 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype)
1323 const char** str_mapping = get_type_str_mapping();
1325 if (vtype == xmlrpc_vector_none) {
1326 return str_mapping[type];
1328 return str_mapping[XMLRPC_TYPE_COUNT + vtype];
1332 /* map a string to an xmlrpc type */
1333 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str)
1335 const char** str_mapping = get_type_str_mapping();
1339 for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
1340 if (!strcmp(str_mapping[i], str)) {
1341 return (XMLRPC_VALUE_TYPE) i;
1348 /* map a string to an xmlrpc vector type */
1349 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str)
1351 const char** str_mapping = get_type_str_mapping();
1355 for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
1356 if (!strcmp(str_mapping[i], str)) {
1357 return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
1365 /* set a given value to a particular type.
1366 * note: this only works on strings, and only for date and base64,
1367 * which do not have native php types. black magic lies herein.
1369 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype)
1371 int bSuccess = FAILURE;
1374 /* we only really care about strings because they can represent
1375 * base64 and datetime. all other types have corresponding php types
1377 if (Z_TYPE_P(value) == IS_STRING) {
1378 if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
1379 const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
1382 MAKE_STD_ZVAL(type);
1384 Z_TYPE_P(type) = IS_STRING;
1385 Z_STRVAL_P(type) = estrdup(typestr);
1386 Z_STRLEN_P(type) = strlen(typestr);
1388 if(newtype == xmlrpc_datetime) {
1389 XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, value->value.str.val);
1391 time_t timestamp = XMLRPC_GetValueDateTime(v);
1395 MAKE_STD_ZVAL(ztimestamp);
1397 ztimestamp->type = IS_LONG;
1398 ztimestamp->value.lval = timestamp;
1400 convert_to_object(value);
1401 if(SUCCESS == zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL)) {
1402 bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR), (void *) &ztimestamp, sizeof(zval *), NULL);
1405 XMLRPC_CleanupValue(v);
1409 convert_to_object(value);
1410 bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL);
1418 /* return xmlrpc type of a php value */
1419 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(php_output_options* out, zval* value, zval** newvalue)
1421 XMLRPC_VALUE_TYPE type = xmlrpc_none;
1426 switch (Z_TYPE_P(value)) {
1428 if (XMLRPCG(allow_null) || (out && out->b_allow_null)) {
1431 type = xmlrpc_string;
1434 #ifndef BOOL_AS_LONG
1436 /* Right thing to do, but it breaks some legacy code. */
1438 type = xmlrpc_boolean;
1448 type = xmlrpc_double;
1451 type = xmlrpc_string;
1454 type = xmlrpc_string;
1457 case IS_CONSTANT_ARRAY:
1458 type = xmlrpc_vector;
1463 type = xmlrpc_vector;
1465 if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**) &attr) == SUCCESS) {
1466 if (Z_TYPE_PP(attr) == IS_STRING) {
1467 type = xmlrpc_str_as_type(Z_STRVAL_PP(attr));
1474 /* if requested, return an unmolested (magic removed) copy of the value */
1478 if ((type == xmlrpc_base64 && Z_TYPE_P(value) != IS_NULL) || type == xmlrpc_datetime) {
1479 if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), (void**) &val) == SUCCESS) {
1492 /* {{{ proto bool xmlrpc_set_type(string value, string type)
1493 Sets xmlrpc type, base64 or datetime or nil, for a PHP string value */
1494 PHP_FUNCTION(xmlrpc_set_type)
1497 XMLRPC_VALUE_TYPE vtype;
1499 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &arg, &type) == FAILURE)) {
1503 convert_to_string_ex(type);
1504 vtype = xmlrpc_str_as_type(Z_STRVAL_PP(type));
1505 if (vtype != xmlrpc_none) {
1506 if (set_zval_xmlrpc_type(*arg, vtype) == SUCCESS) {
1510 zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", Z_STRVAL_PP(type));
1516 /* {{{ proto string xmlrpc_get_type(mixed value)
1517 Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */
1518 PHP_FUNCTION(xmlrpc_get_type)
1521 XMLRPC_VALUE_TYPE type;
1522 XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
1524 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1528 type = get_zval_xmlrpc_type(NULL, *arg, 0);
1529 if (type == xmlrpc_vector) {
1530 vtype = determine_vector_type(Z_ARRVAL_PP(arg));
1533 RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype), 1);
1537 /* {{{ proto bool xmlrpc_is_fault(array)
1538 Determines if an array value represents an XMLRPC fault. */
1539 PHP_FUNCTION(xmlrpc_is_fault)
1543 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1547 if (Z_TYPE_PP(arg) != IS_ARRAY) {
1548 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array argument expected");
1550 /* The "correct" way to do this would be to call the xmlrpc
1551 * library XMLRPC_ValueIsFault() func. However, doing that
1552 * would require us to create an xmlrpc value from the php
1553 * array, which is rather expensive, especially if it was
1554 * a big array. Thus, we resort to this not so clever hackery.
1556 if (zend_hash_find(Z_ARRVAL_PP(arg), FAULT_CODE, FAULT_CODE_LEN + 1, (void**) &val) == SUCCESS &&
1557 zend_hash_find(Z_ARRVAL_PP(arg), FAULT_STRING, FAULT_STRING_LEN + 1, (void**) &val) == SUCCESS) {