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 /* ========== additional notes
97 * in the process, I've also come across the following resources that might help
98 * if/when zend_get_parameters_ex gets deprecated (only generates warnings for now)
100 * http://developers.evrsoft.com/docs/php/zend.arguments.deprecated-retrieval.shtml
101 * explains how the old (our) stuff works
103 * http://www.hospedajeydominios.com/mambo/documentacion-manual_php-pagina-zend_arguments_retrieval.html
104 * gives info on the new scheme
106 * I'm taking tha risk as the changes seem to mean major surgery ...
109 /* You should tweak config.m4 so this symbol (or some else suitable)
112 ZEND_DECLARE_MODULE_GLOBALS(xmlrpc)
114 static int le_xmlrpc_server;
116 function_entry xmlrpc_functions[] = {
117 PHP_FE(xmlrpc_encode, NULL)
118 PHP_FE(xmlrpc_decode, NULL)
119 PHP_FE(xmlrpc_decode_request, second_arg_force_ref)
120 PHP_FE(xmlrpc_encode_request, NULL)
121 PHP_FE(xmlrpc_get_type, NULL)
122 PHP_FE(xmlrpc_set_type, first_arg_force_ref)
123 PHP_FE(xmlrpc_is_fault, NULL)
124 PHP_FE(xmlrpc_server_create, NULL)
125 PHP_FE(xmlrpc_server_destroy, NULL)
126 PHP_FE(xmlrpc_server_register_method, NULL)
127 PHP_FE(xmlrpc_server_call_method, NULL)
128 PHP_FE(xmlrpc_parse_method_descriptions, NULL)
129 PHP_FE(xmlrpc_server_add_introspection_data, NULL)
130 PHP_FE(xmlrpc_server_register_introspection_callback, NULL)
134 zend_module_entry xmlrpc_module_entry = {
135 STANDARD_MODULE_HEADER,
139 PHP_MSHUTDOWN(xmlrpc),
140 PHP_RINIT(xmlrpc), /* Replace with NULL if there's nothing to do at request start */
141 PHP_RSHUTDOWN(xmlrpc), /* Replace with NULL if there's nothing to do at request end */
144 STANDARD_MODULE_PROPERTIES
147 #ifdef COMPILE_DL_XMLRPC
148 ZEND_GET_MODULE(xmlrpc)
150 # include "zend_arg_defs.c"
155 STD_PHP_INI_BOOLEAN("xmlrpc.allow_null", "0", PHP_INI_ALL, OnUpdateBool, allow_null, zend_xmlrpc_globals, xmlrpc_globals)
158 static void php_xmlrpc_init_globals(zend_xmlrpc_globals *xmlrpc_globals)
160 memset(xmlrpc_globals, 0, sizeof(zend_xmlrpc_globals));
163 /*******************************
164 * local structures and defines *
165 *******************************/
167 /* per server data */
168 typedef struct _xmlrpc_server_data {
170 zval* introspection_map;
171 XMLRPC_SERVER server_ptr;
172 } xmlrpc_server_data;
175 /* how to format output */
176 typedef struct _php_output_options {
180 STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out;
181 } php_output_options;
183 /* data passed to C callback */
184 typedef struct _xmlrpc_callback_data {
189 xmlrpc_server_data* server;
191 } xmlrpc_callback_data;
194 #define OUTPUT_TYPE_KEY "output_type"
195 #define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1)
196 #define OUTPUT_TYPE_VALUE_PHP "php"
197 #define OUTPUT_TYPE_VALUE_XML "xml"
199 #define ALLOW_NULL_KEY "allow_null"
200 #define ALLOW_NULL_KEY_LEN (sizeof(ALLOW_NULL_KEY) - 1)
202 #define VERBOSITY_KEY "verbosity"
203 #define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1)
204 #define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space"
205 #define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only"
206 #define VERBOSITY_VALUE_PRETTY "pretty"
208 #define ESCAPING_KEY "escaping"
209 #define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1)
210 #define ESCAPING_VALUE_CDATA "cdata"
211 #define ESCAPING_VALUE_NON_ASCII "non-ascii"
212 #define ESCAPING_VALUE_NON_PRINT "non-print"
213 #define ESCAPING_VALUE_MARKUP "markup"
215 #define VERSION_KEY "version"
216 #define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1)
217 #define VERSION_VALUE_SIMPLE "simple"
218 #define VERSION_VALUE_XMLRPC "xmlrpc"
219 #define VERSION_VALUE_SOAP11 "soap 1.1"
220 #define VERSION_VALUE_AUTO "auto"
222 #define ENCODING_KEY "encoding"
223 #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1)
224 #define ENCODING_DEFAULT "iso-8859-1"
227 #define OBJECT_TYPE_ATTR "xmlrpc_type"
228 #define OBJECT_VALUE_ATTR "scalar"
229 #define OBJECT_VALUE_TS_ATTR "timestamp"
232 #define FAULT_CODE "faultCode"
233 #define FAULT_CODE_LEN (sizeof(FAULT_CODE) - 1)
234 #define FAULT_STRING "faultString"
235 #define FAULT_STRING_LEN (sizeof(FAULT_STRING) - 1)
237 /***********************
238 * forward declarations *
239 ***********************/
240 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(php_output_options* out, zval* value, zval** newvalue);
241 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data);
242 int sset_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
243 zval* decode_request_worker(zval* xml_in, zval* encoding_in, zval* method_name_out);
244 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype);
245 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str);
246 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str);
247 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
249 /*********************
250 * startup / shutdown *
251 *********************/
253 static void destroy_server_data(xmlrpc_server_data *server)
256 XMLRPC_ServerDestroy(server->server_ptr);
258 zval_dtor(server->method_map);
259 FREE_ZVAL(server->method_map);
261 zval_dtor(server->introspection_map);
262 FREE_ZVAL(server->introspection_map);
268 /* called when server is being destructed. either when xmlrpc_server_destroy
269 * is called, or when request ends. */
270 static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
272 if (rsrc && rsrc->ptr) {
273 destroy_server_data((xmlrpc_server_data*) rsrc->ptr);
278 PHP_MINIT_FUNCTION(xmlrpc)
280 ZEND_INIT_MODULE_GLOBALS(xmlrpc, php_xmlrpc_init_globals, NULL);
282 REGISTER_INI_ENTRIES();
284 le_xmlrpc_server = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);
289 /* module shutdown */
290 PHP_MSHUTDOWN_FUNCTION(xmlrpc)
295 /* Remove if there's nothing to do at request start */
296 PHP_RINIT_FUNCTION(xmlrpc)
301 /* Remove if there's nothing to do at request end */
302 PHP_RSHUTDOWN_FUNCTION(xmlrpc)
307 /* display info in phpinfo() */
308 PHP_MINFO_FUNCTION(xmlrpc)
310 php_info_print_table_start();
311 php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString());
312 php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION);
313 php_info_print_table_row(2, "author", "Dan Libby");
314 php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net");
315 php_info_print_table_row(2, "open sourced by", "Epinions.com");
316 php_info_print_table_end();
323 /* Utility functions for adding data types to arrays, with or without key (assoc, non-assoc).
324 * Could easily be further generalized to work with objects.
327 static int add_long(zval* list, char* id, int num) {
328 if(id) return add_assoc_long(list, id, num);
329 else return add_next_index_long(list, num);
332 static int add_double(zval* list, char* id, double num) {
333 if(id) return add_assoc_double(list, id, num);
334 else return add_next_index_double(list, num);
337 static int add_string(zval* list, char* id, char* string, int duplicate) {
338 if(id) return add_assoc_string(list, id, string, duplicate);
339 else return add_next_index_string(list, string, duplicate);
342 static int add_stringl(zval* list, char* id, char* string, uint length, int duplicate) {
343 if(id) return add_assoc_stringl(list, id, string, length, duplicate);
344 else return add_next_index_stringl(list, string, length, duplicate);
349 static int add_zval(zval* list, const char* id, zval** val)
353 return zend_hash_update(Z_ARRVAL_P(list), (char*) id, strlen(id) + 1, (void *) val, sizeof(zval **), NULL);
355 return zend_hash_next_index_insert(Z_ARRVAL_P(list), (void *) val, sizeof(zval **), NULL);
358 /* what is the correct return on error? */
362 #define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0)
365 /*************************
366 * input / output options *
367 *************************/
369 /* parse an array (user input) into output options suitable for use by xmlrpc engine
370 * and determine whether to return data as xml or php vars */
371 static void set_output_options(php_output_options* options, zval* output_opts)
378 options->b_php_out = 0;
379 options->b_auto_version = 1;
380 options->b_allow_null = 0;
381 options->xmlrpc_out.version = xmlrpc_version_1_0;
382 options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT;
383 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
384 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping;
386 if (output_opts && Z_TYPE_P(output_opts) == IS_ARRAY) {
390 if (zend_hash_find(Z_ARRVAL_P(output_opts), ALLOW_NULL_KEY, ALLOW_NULL_KEY_LEN + 1, (void**) &val) == SUCCESS) {
391 if (Z_TYPE_PP(val) == IS_BOOL) {
392 if (Z_LVAL_PP(val)) {
393 options->b_allow_null = 1;
395 options->b_allow_null = 0;
400 /* type of output (xml/php) */
401 if (zend_hash_find(Z_ARRVAL_P(output_opts), OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**) &val) == SUCCESS) {
402 if (Z_TYPE_PP(val) == IS_STRING) {
403 if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_PHP)) {
404 options->b_php_out = 1;
405 } else if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_XML)) {
406 options->b_php_out = 0;
411 /* verbosity of generated xml */
412 if (zend_hash_find(Z_ARRVAL_P(output_opts), VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, (void**) &val) == SUCCESS) {
413 if (Z_TYPE_PP(val) == IS_STRING) {
414 if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NO_WHITE_SPACE)) {
415 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space;
416 } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NEWLINES_ONLY)) {
417 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only;
418 } else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_PRETTY)) {
419 options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
424 /* version of xml to output */
425 if (zend_hash_find(Z_ARRVAL_P(output_opts), VERSION_KEY, VERSION_KEY_LEN + 1, (void**) &val) == SUCCESS) {
426 if (Z_TYPE_PP(val) == IS_STRING) {
427 options->b_auto_version = 0;
428 if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_XMLRPC)) {
429 options->xmlrpc_out.version = xmlrpc_version_1_0;
430 } else if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_SIMPLE)) {
431 options->xmlrpc_out.version = xmlrpc_version_simple;
432 } else if (!strcmp((*val)->value.str.val, VERSION_VALUE_SOAP11)) {
433 options->xmlrpc_out.version = xmlrpc_version_soap_1_1;
434 } else { /* if(!strcmp((*val)->value.str.val, VERSION_VALUE_AUTO)) { */
435 options->b_auto_version = 1;
440 /* encoding code set */
441 if(zend_hash_find(Z_ARRVAL_P(output_opts),
442 ENCODING_KEY, ENCODING_KEY_LEN + 1,
443 (void**)&val) == SUCCESS) {
444 if(Z_TYPE_PP(val) == IS_STRING) {
445 options->xmlrpc_out.xml_elem_opts.encoding = estrdup(Z_STRVAL_PP(val));
449 /* escaping options */
450 if(zend_hash_find(Z_ARRVAL_P(output_opts),
451 ESCAPING_KEY, ESCAPING_KEY_LEN + 1,
452 (void**)&val) == SUCCESS) {
453 /* multiple values allowed. check if array */
454 if(Z_TYPE_PP(val) == IS_ARRAY) {
456 zend_hash_internal_pointer_reset(Z_ARRVAL_PP(val));
457 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping;
459 if(zend_hash_get_current_data(Z_ARRVAL_PP(val), (void**)&iter_val) == SUCCESS) {
460 if(Z_TYPE_PP(iter_val) == IS_STRING && Z_STRVAL_PP(iter_val)) {
461 if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_CDATA)) {
462 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping;
464 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_ASCII)) {
465 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping;
467 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_PRINT)) {
468 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping;
470 else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_MARKUP)) {
471 options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping;
479 zend_hash_move_forward(Z_ARRVAL_PP(val));
482 /* else, check for single value */
483 else if(Z_TYPE_PP(val) == IS_STRING) {
484 if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_CDATA)) {
485 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping;
487 else if(!strcmp(Z_STRVAL_PP(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(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(val), ESCAPING_VALUE_MARKUP)) {
494 options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping;
507 /* php arrays have no distinction between array and struct types.
508 * they even allow mixed. Thus, we determine the type by iterating
509 * through the entire array and figuring out each element.
510 * room for some optimation here if we stop after a specific # of elements.
512 static XMLRPC_VECTOR_TYPE determine_vector_type (HashTable *ht)
514 int bArray = 0, bStruct = 0, bMixed = 0;
515 unsigned long num_index;
518 zend_hash_internal_pointer_reset(ht);
520 int res = my_zend_hash_get_current_key(ht, &my_key, &num_index);
521 if(res == HASH_KEY_IS_LONG) {
528 else if(res == HASH_KEY_NON_EXISTANT) {
531 else if(res == HASH_KEY_IS_STRING) {
539 zend_hash_move_forward(ht);
541 return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array);
544 /* recursively convert php values into xmlrpc values */
545 static XMLRPC_VALUE PHP_to_XMLRPC_worker (php_output_options* out, const char* key, zval* in_val, int depth)
547 XMLRPC_VALUE xReturn = NULL;
550 XMLRPC_VALUE_TYPE type = get_zval_xmlrpc_type(out, in_val, &val);
554 xReturn = XMLRPC_CreateValueEmpty();
555 XMLRPC_SetValueID(xReturn, key, 0);
558 xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
560 case xmlrpc_datetime:
561 convert_to_string(val);
562 xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL_P(val));
565 convert_to_boolean(val);
566 xReturn = XMLRPC_CreateValueBoolean(key, Z_LVAL_P(val));
569 convert_to_long(val);
570 xReturn = XMLRPC_CreateValueInt(key, Z_LVAL_P(val));
573 convert_to_double(val);
574 xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL_P(val));
577 convert_to_string(val);
578 xReturn = XMLRPC_CreateValueString(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
582 unsigned long num_index;
586 convert_to_array(val);
588 xReturn = XMLRPC_CreateVector(key, determine_vector_type(Z_ARRVAL_P(val)));
590 zend_hash_internal_pointer_reset(Z_ARRVAL_P(val));
592 int res = my_zend_hash_get_current_key(Z_ARRVAL_P(val), &my_key, &num_index);
593 if(res == HASH_KEY_IS_LONG) {
594 if(zend_hash_get_current_data(Z_ARRVAL_P(val), (void**)&pIter) == SUCCESS) {
595 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(out, 0, *pIter, depth++));
598 else if(res == HASH_KEY_NON_EXISTANT) {
601 else if(res == HASH_KEY_IS_STRING) {
602 if(zend_hash_get_current_data(Z_ARRVAL_P(val), (void**)&pIter) == SUCCESS) {
603 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(out, my_key, *pIter, depth++));
607 zend_hash_move_forward(Z_ARRVAL_P(val));
619 static XMLRPC_VALUE PHP_to_XMLRPC(php_output_options* out, zval* root_val)
621 return PHP_to_XMLRPC_worker(out, NULL, root_val, 0);
624 /* recursively convert xmlrpc values into php values */
625 static zval* XMLRPC_to_PHP(XMLRPC_VALUE el)
631 XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
633 MAKE_STD_ZVAL(elem); /* init. very important. spent a frustrating day finding this out. */
638 Z_TYPE_P(elem) = IS_NULL;
641 pStr = XMLRPC_GetValueString(el);
643 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
644 Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
645 Z_TYPE_P(elem) = IS_STRING;
649 Z_LVAL_P(elem) = XMLRPC_GetValueInt(el);
650 Z_TYPE_P(elem) = IS_LONG;
653 Z_LVAL_P(elem) = XMLRPC_GetValueBoolean(el);
654 Z_TYPE_P(elem) = IS_BOOL;
657 Z_DVAL_P(elem) = XMLRPC_GetValueDouble(el);
658 Z_TYPE_P(elem) = IS_DOUBLE;
660 case xmlrpc_datetime:
661 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
662 Z_STRVAL_P(elem) = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), Z_STRLEN_P(elem));
663 Z_TYPE_P(elem) = IS_STRING;
666 pStr = XMLRPC_GetValueBase64(el);
668 Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
669 Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
670 Z_TYPE_P(elem) = IS_STRING;
676 XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
679 zval *val = XMLRPC_to_PHP(xIter);
681 add_zval(elem, XMLRPC_GetValueID(xIter), &val);
683 xIter = XMLRPC_VectorNext(el);
690 set_zval_xmlrpc_type(elem, type);
695 /* {{{ proto string xmlrpc_encode_request(string method, mixed params)
696 Generates XML for a method request */
697 PHP_FUNCTION(xmlrpc_encode_request)
699 XMLRPC_REQUEST xRequest = NULL;
700 zval **method, **vals, **out_opts;
702 php_output_options out;
704 if (ZEND_NUM_ARGS() < 2 || ZEND_NUM_ARGS() > 3 || (zend_get_parameters_ex(ZEND_NUM_ARGS(), &method, &vals, &out_opts) == FAILURE)) {
705 WRONG_PARAM_COUNT; /* prints/logs a warning and returns */
708 set_output_options(&out, (ZEND_NUM_ARGS() == 3) ? *out_opts : 0);
710 if(return_value_used) {
711 xRequest = XMLRPC_RequestNew();
714 XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
715 if (Z_TYPE_PP(method) == IS_NULL) {
716 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
718 XMLRPC_RequestSetMethodName(xRequest, Z_STRVAL_PP(method));
719 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
721 if (Z_TYPE_PP(vals) != IS_NULL) {
722 XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(&out, *vals));
725 outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
727 RETVAL_STRING(outBuf, 1);
730 XMLRPC_RequestFree(xRequest, 1);
736 /* {{{ proto string xmlrpc_encode(mixed value)
737 Generates XML for a PHP value */
738 PHP_FUNCTION(xmlrpc_encode)
740 XMLRPC_VALUE xOut = NULL;
744 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
748 if( return_value_used ) {
749 /* convert native php type to xmlrpc type */
750 xOut = PHP_to_XMLRPC(NULL, *arg1);
752 /* generate raw xml from xmlrpc data */
753 outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
757 RETVAL_STRING(outBuf, 1);
761 XMLRPC_CleanupValue(xOut);
768 zval* decode_request_worker (zval* xml_in, zval* encoding_in, zval* method_name_out)
771 XMLRPC_REQUEST response;
772 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}};
773 opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(Z_STRVAL_P(encoding_in)) : ENCODING_DEFAULT;
775 /* generate XMLRPC_REQUEST from raw xml */
776 response = XMLRPC_REQUEST_FromXML(Z_STRVAL_P(xml_in), Z_STRLEN_P(xml_in), &opts);
778 /* convert xmlrpc data to native php types */
779 retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response));
781 if(XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
782 if(method_name_out) {
783 convert_to_string(method_name_out);
784 Z_TYPE_P(method_name_out) = IS_STRING;
785 Z_STRVAL_P(method_name_out) = estrdup(XMLRPC_RequestGetMethodName(response));
786 Z_STRLEN_P(method_name_out) = strlen(Z_STRVAL_P(method_name_out));
790 /* dust, sweep, and mop */
791 XMLRPC_RequestFree(response, 1);
796 /* {{{ proto array xmlrpc_decode_request(string xml, string& method [, string encoding])
797 Decodes XML into native PHP types */
798 PHP_FUNCTION(xmlrpc_decode_request)
800 zval **xml, **method, **encoding = NULL;
801 int argc = ZEND_NUM_ARGS();
803 if (argc < 2 || argc > 3 || (zend_get_parameters_ex(argc, &xml, &method, &encoding) == FAILURE)) {
807 convert_to_string_ex(xml);
808 convert_to_string_ex(method);
810 convert_to_string_ex(encoding);
813 if(return_value_used) {
814 zval* retval = decode_request_worker(*xml, encoding ? *encoding : NULL, *method);
816 *return_value = *retval;
824 /* {{{ proto array xmlrpc_decode(string xml [, string encoding])
825 Decodes XML into native PHP types */
826 PHP_FUNCTION(xmlrpc_decode)
828 zval **arg1, **arg2 = NULL;
829 int argc = ZEND_NUM_ARGS();
831 if (argc < 1 || argc > 2 || (zend_get_parameters_ex(argc, &arg1, &arg2) == FAILURE)) {
835 convert_to_string_ex(arg1);
837 convert_to_string_ex(arg2);
840 if(return_value_used) {
841 zval* retval = decode_request_worker(*arg1, arg2 ? *arg2 : NULL, NULL);
843 *return_value = *retval;
851 /*************************
852 * server related methods *
853 *************************/
855 /* {{{ proto resource xmlrpc_server_create(void)
856 Creates an xmlrpc server */
857 PHP_FUNCTION(xmlrpc_server_create)
859 if(ZEND_NUM_ARGS() != 0) {
863 if(return_value_used) {
864 zval *method_map, *introspection_map;
865 xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
866 MAKE_STD_ZVAL(method_map);
867 MAKE_STD_ZVAL(introspection_map);
869 array_init(method_map);
870 array_init(introspection_map);
872 /* allocate server data. free'd in destroy_server_data() */
873 server->method_map = method_map;
874 server->introspection_map = introspection_map;
875 server->server_ptr = XMLRPC_ServerCreate();
877 XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
879 /* store for later use */
880 ZEND_REGISTER_RESOURCE(return_value,server, le_xmlrpc_server);
885 /* {{{ proto int xmlrpc_server_destroy(resource server)
886 Destroys server resources */
887 PHP_FUNCTION(xmlrpc_server_destroy)
890 int bSuccess = FAILURE;
892 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
896 if(Z_TYPE_PP(arg1) == IS_RESOURCE) {
899 xmlrpc_server_data *server = zend_list_find(Z_LVAL_PP(arg1), &type);
901 if(server && type == le_xmlrpc_server) {
902 bSuccess = zend_list_delete(Z_LVAL_PP(arg1));
904 /* called by hashtable destructor
905 * destroy_server_data(server);
909 RETVAL_LONG(bSuccess == SUCCESS);
914 /* called by xmlrpc C engine as method handler for all registered methods.
915 * it then calls the corresponding PHP function to handle the method.
917 static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data)
919 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
921 zval* callback_params[3];
924 /* convert xmlrpc to native php types */
925 xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest));
927 /* setup data hoojum */
928 callback_params[0] = pData->xmlrpc_method;
929 callback_params[1] = xmlrpc_params;
930 callback_params[2] = pData->caller_params;
932 /* Use same C function for all methods */
934 /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
935 call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params TSRMLS_CC);
937 pData->php_executed = 1;
939 zval_dtor(xmlrpc_params);
940 FREE_ZVAL(xmlrpc_params);
945 /* called by the C server when it first receives an introspection request. We pass this on to
946 * our PHP listeners, if any
948 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data)
950 zval *retval_ptr, **php_function;
951 zval* callback_params[1];
952 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
955 MAKE_STD_ZVAL(retval_ptr);
956 Z_TYPE_P(retval_ptr) = IS_NULL;
958 /* setup data hoojum */
959 callback_params[0] = pData->caller_params;
961 /* loop through and call all registered callbacks */
962 zend_hash_internal_pointer_reset(Z_ARRVAL_P(pData->server->introspection_map));
964 if(zend_hash_get_current_data(Z_ARRVAL_P(pData->server->introspection_map),
965 (void**)&php_function) == SUCCESS) {
967 /* php func prototype: function string user_func($user_params) */
968 if(call_user_function(CG(function_table), NULL, *php_function,
969 retval_ptr, 1, callback_params TSRMLS_CC) == SUCCESS) {
971 STRUCT_XMLRPC_ERROR err = {0};
973 /* return value should be a string */
974 convert_to_string(retval_ptr);
976 xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_P(retval_ptr), &err);
979 if(!XMLRPC_ServerAddIntrospectionData(server, xData)) {
980 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", Z_STRVAL_PP(php_function));
982 XMLRPC_CleanupValue(xData);
985 /* could not create description */
986 if(err.xml_elem_error.parser_code) {
987 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()",
988 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, Z_STRVAL_PP(php_function));
991 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s()",
992 Z_STRVAL_PP(php_function));
997 /* user func failed */
998 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error calling user introspection callback: %s()", Z_STRVAL_PP(php_function));
1005 zend_hash_move_forward(Z_ARRVAL_P(pData->server->introspection_map));
1008 /* so we don't call the same callbacks ever again */
1009 zend_hash_clean(Z_ARRVAL_P(pData->server->introspection_map));
1012 /* {{{ proto bool xmlrpc_server_register_method(resource server, string method_name, string function)
1013 Register a PHP function to handle method matching method_name */
1014 PHP_FUNCTION(xmlrpc_server_register_method)
1016 zval **method_key, **method_name, **handle, *method_name_save;
1018 xmlrpc_server_data* server;
1020 if (ZEND_NUM_ARGS() != 3 || (zend_get_parameters_ex(3, &handle, &method_key, &method_name) == FAILURE)) {
1024 server = zend_list_find(Z_LVAL_PP(handle), &type);
1026 if(type == le_xmlrpc_server) {
1027 /* register with C engine. every method just calls our standard callback,
1028 * and it then dispatches to php as necessary
1030 if(XMLRPC_ServerRegisterMethod(server->server_ptr, Z_STRVAL_PP(method_key), php_xmlrpc_callback)) {
1031 /* save for later use */
1032 MAKE_STD_ZVAL(method_name_save);
1033 *method_name_save = **method_name;
1034 zval_copy_ctor(method_name_save);
1036 /* register our php method */
1037 add_zval(server->method_map, Z_STRVAL_PP(method_key), &method_name_save);
1047 /* {{{ proto bool xmlrpc_server_register_introspection_callback(resource server, string function)
1048 Register a PHP function to generate documentation */
1049 PHP_FUNCTION(xmlrpc_server_register_introspection_callback)
1051 zval **method_name, **handle, *method_name_save;
1053 xmlrpc_server_data* server;
1055 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &method_name) == FAILURE)) {
1059 server = zend_list_find(Z_LVAL_PP(handle), &type);
1061 if(type == le_xmlrpc_server) {
1062 /* save for later use */
1063 MAKE_STD_ZVAL(method_name_save);
1064 *method_name_save = **method_name;
1065 zval_copy_ctor(method_name_save);
1067 /* register our php method */
1068 add_zval(server->introspection_map, NULL, &method_name_save);
1077 /* this function is itchin for a re-write */
1079 /* {{{ proto mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])
1080 Parses XML requests and call methods */
1081 PHP_FUNCTION(xmlrpc_server_call_method)
1083 xmlrpc_callback_data data = {0};
1084 XMLRPC_REQUEST xRequest;
1085 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
1086 xmlrpc_server_data* server;
1087 zval **rawxml, **caller_params, **handle, **output_opts = NULL;
1089 php_output_options out;
1090 int argc =ZEND_NUM_ARGS();
1092 if (argc < 3 || argc > 4 || (zend_get_parameters_ex(argc, &handle, &rawxml, &caller_params, &output_opts) != SUCCESS)) {
1095 /* user output options */
1097 set_output_options(&out, NULL);
1100 set_output_options(&out, *output_opts);
1103 server = zend_list_find(Z_LVAL_PP(handle), &type);
1105 if(type == le_xmlrpc_server) {
1106 /* HACK: use output encoding for now */
1107 input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
1109 /* generate an XMLRPC_REQUEST from the raw xml input */
1110 xRequest = XMLRPC_REQUEST_FromXML(Z_STRVAL_PP(rawxml), Z_STRLEN_PP(rawxml), &input_opts);
1113 const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
1114 zval **php_function;
1115 XMLRPC_VALUE xAnswer = NULL;
1116 MAKE_STD_ZVAL(data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */
1117 MAKE_STD_ZVAL(data.return_data);
1118 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 */
1119 Z_TYPE_P(data.xmlrpc_method) = IS_NULL;
1125 /* setup some data to pass to the callback function */
1126 Z_STRVAL_P(data.xmlrpc_method) = estrdup(methodname);
1127 Z_STRLEN_P(data.xmlrpc_method) = strlen(methodname);
1128 Z_TYPE_P(data.xmlrpc_method) = IS_STRING;
1129 data.caller_params = *caller_params;
1130 data.php_executed = 0;
1131 data.server = server;
1133 /* check if the called method has been previous registered */
1134 if(zend_hash_find(Z_ARRVAL_P(server->method_map),
1135 Z_STRVAL_P(data.xmlrpc_method),
1136 Z_STRLEN_P(data.xmlrpc_method) + 1,
1137 (void**)&php_function) == SUCCESS) {
1139 data.php_function = *php_function;
1142 /* We could just call the php method directly ourselves at this point, but we do this
1143 * with a C callback in case the xmlrpc library ever implements some cool usage stats,
1146 xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
1147 if(xAnswer && out.b_php_out) {
1148 zval_dtor(data.return_data);
1149 FREE_ZVAL(data.return_data);
1150 data.return_data = XMLRPC_to_PHP(xAnswer);
1151 } else if(data.php_executed && !out.b_php_out) {
1152 xAnswer = PHP_to_XMLRPC(&out, data.return_data);
1155 /* should we return data as xml? */
1156 if(!out.b_php_out) {
1157 XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
1162 /* automagically determine output serialization type from request type */
1163 if (out.b_auto_version) {
1164 XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
1166 out.xmlrpc_out.version = opts->version;
1169 /* set some required request hoojum */
1170 XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
1171 XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
1172 XMLRPC_RequestSetData(xResponse, xAnswer);
1173 XMLRPC_RequestSetMethodName(xResponse, methodname);
1176 outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
1178 RETVAL_STRINGL(outBuf, buf_len, 1);
1181 /* cleanup after ourselves. what a sty! */
1182 XMLRPC_RequestFree(xResponse, 0);
1184 } else { /* or as native php types? */
1185 *return_value = *data.return_data;
1186 zval_copy_ctor(return_value);
1189 /* cleanup after ourselves. what a sty! */
1190 zval_dtor(data.xmlrpc_method);
1191 FREE_ZVAL(data.xmlrpc_method);
1192 zval_dtor(data.return_data);
1193 FREE_ZVAL(data.return_data);
1196 XMLRPC_CleanupValue(xAnswer);
1199 XMLRPC_RequestFree(xRequest, 1);
1206 /* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
1207 Adds introspection documentation */
1208 PHP_FUNCTION(xmlrpc_server_add_introspection_data)
1210 zval **handle, **desc;
1212 xmlrpc_server_data* server;
1214 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &desc) == FAILURE)) {
1218 server = zend_list_find(Z_LVAL_PP(handle), &type);
1220 if (type == le_xmlrpc_server) {
1221 XMLRPC_VALUE xDesc = PHP_to_XMLRPC(NULL, *desc);
1223 int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
1224 XMLRPC_CleanupValue(xDesc);
1225 RETURN_LONG(retval);
1233 /* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
1234 Decodes XML into a list of method descriptions */
1235 PHP_FUNCTION(xmlrpc_parse_method_descriptions)
1237 zval **arg1, *retval;
1239 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
1243 convert_to_string_ex(arg1);
1245 if(return_value_used) {
1246 STRUCT_XMLRPC_ERROR err = {0};
1247 XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_PP(arg1), &err);
1249 retval = XMLRPC_to_PHP(xVal);
1252 *return_value = *retval;
1253 zval_copy_ctor(return_value);
1255 /* dust, sweep, and mop */
1256 XMLRPC_CleanupValue(xVal);
1258 /* could not create description */
1259 if(err.xml_elem_error.parser_code) {
1260 php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data",
1261 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
1263 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid xml structure. Unable to create introspection data");
1266 php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error. no method description created");
1277 #define XMLRPC_TYPE_COUNT 10
1278 #define XMLRPC_VECTOR_TYPE_COUNT 4
1279 #define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
1281 /* return a string matching a given xmlrpc type */
1282 static const char** get_type_str_mapping(void)
1284 static const char* str_mapping[TYPE_STR_MAP_SIZE];
1285 static int first = 1;
1287 /* warning. do not add/delete without changing size define */
1288 str_mapping[xmlrpc_none] = "none";
1289 str_mapping[xmlrpc_empty] = "empty";
1290 str_mapping[xmlrpc_nil] = "nil";
1291 str_mapping[xmlrpc_base64] = "base64";
1292 str_mapping[xmlrpc_boolean] = "boolean";
1293 str_mapping[xmlrpc_datetime] = "datetime";
1294 str_mapping[xmlrpc_double] = "double";
1295 str_mapping[xmlrpc_int] = "int";
1296 str_mapping[xmlrpc_string] = "string";
1297 str_mapping[xmlrpc_vector] = "vector";
1298 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none";
1299 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array";
1300 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed";
1301 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
1304 return (const char**)str_mapping;
1307 /* map an xmlrpc type to a string */
1308 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype)
1310 const char** str_mapping = get_type_str_mapping();
1312 if (vtype == xmlrpc_vector_none) {
1313 return str_mapping[type];
1315 return str_mapping[XMLRPC_TYPE_COUNT + vtype];
1319 /* map a string to an xmlrpc type */
1320 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str)
1322 const char** str_mapping = get_type_str_mapping();
1326 for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
1327 if (!strcmp(str_mapping[i], str)) {
1328 return (XMLRPC_VALUE_TYPE) i;
1335 /* map a string to an xmlrpc vector type */
1336 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str)
1338 const char** str_mapping = get_type_str_mapping();
1342 for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
1343 if (!strcmp(str_mapping[i], str)) {
1344 return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
1352 /* set a given value to a particular type.
1353 * note: this only works on strings, and only for date and base64,
1354 * which do not have native php types. black magic lies herein.
1356 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype)
1358 int bSuccess = FAILURE;
1361 /* we only really care about strings because they can represent
1362 * base64 and datetime. all other types have corresponding php types
1364 if (Z_TYPE_P(value) == IS_STRING) {
1365 if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
1366 const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
1369 MAKE_STD_ZVAL(type);
1371 Z_TYPE_P(type) = IS_STRING;
1372 Z_STRVAL_P(type) = estrdup(typestr);
1373 Z_STRLEN_P(type) = strlen(typestr);
1375 if(newtype == xmlrpc_datetime) {
1376 XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, value->value.str.val);
1378 time_t timestamp = XMLRPC_GetValueDateTime(v);
1382 MAKE_STD_ZVAL(ztimestamp);
1384 ztimestamp->type = IS_LONG;
1385 ztimestamp->value.lval = timestamp;
1387 convert_to_object(value);
1388 if(SUCCESS == zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL)) {
1389 bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR), (void *) &ztimestamp, sizeof(zval *), NULL);
1392 XMLRPC_CleanupValue(v);
1396 convert_to_object(value);
1397 bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL);
1405 /* return xmlrpc type of a php value */
1406 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(php_output_options* out, zval* value, zval** newvalue)
1408 XMLRPC_VALUE_TYPE type = xmlrpc_none;
1413 switch (Z_TYPE_P(value)) {
1415 if (XMLRPCG(allow_null) || (out && out->b_allow_null)) {
1418 type = xmlrpc_string;
1421 #ifndef BOOL_AS_LONG
1423 /* Right thing to do, but it breaks some legacy code. */
1425 type = xmlrpc_boolean;
1435 type = xmlrpc_double;
1438 type = xmlrpc_string;
1441 type = xmlrpc_string;
1444 case IS_CONSTANT_ARRAY:
1445 type = xmlrpc_vector;
1450 type = xmlrpc_vector;
1452 if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**) &attr) == SUCCESS) {
1453 if (Z_TYPE_PP(attr) == IS_STRING) {
1454 type = xmlrpc_str_as_type(Z_STRVAL_PP(attr));
1461 /* if requested, return an unmolested (magic removed) copy of the value */
1465 if ((type == xmlrpc_base64 && Z_TYPE_P(value) != IS_NULL) || type == xmlrpc_datetime) {
1466 if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), (void**) &val) == SUCCESS) {
1479 /* {{{ proto bool xmlrpc_set_type(string value, string type)
1480 Sets xmlrpc type, base64 or datetime or nil, for a PHP string value */
1481 PHP_FUNCTION(xmlrpc_set_type)
1484 XMLRPC_VALUE_TYPE vtype;
1486 if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &arg, &type) == FAILURE)) {
1490 convert_to_string_ex(type);
1491 vtype = xmlrpc_str_as_type(Z_STRVAL_PP(type));
1492 if (vtype != xmlrpc_none) {
1493 if (set_zval_xmlrpc_type(*arg, vtype) == SUCCESS) {
1497 zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", Z_STRVAL_PP(type));
1503 /* {{{ proto string xmlrpc_get_type(mixed value)
1504 Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */
1505 PHP_FUNCTION(xmlrpc_get_type)
1508 XMLRPC_VALUE_TYPE type;
1509 XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
1511 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1515 type = get_zval_xmlrpc_type(NULL, *arg, 0);
1516 if (type == xmlrpc_vector) {
1517 vtype = determine_vector_type(Z_ARRVAL_PP(arg));
1520 RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype), 1);
1524 /* {{{ proto bool xmlrpc_is_fault(array)
1525 Determines if an array value represents an XMLRPC fault. */
1526 PHP_FUNCTION(xmlrpc_is_fault)
1530 if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1534 if (Z_TYPE_PP(arg) != IS_ARRAY) {
1535 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array argument expected");
1537 /* The "correct" way to do this would be to call the xmlrpc
1538 * library XMLRPC_ValueIsFault() func. However, doing that
1539 * would require us to create an xmlrpc value from the php
1540 * array, which is rather expensive, especially if it was
1541 * a big array. Thus, we resort to this not so clever hackery.
1543 if (zend_hash_find(Z_ARRVAL_PP(arg), FAULT_CODE, FAULT_CODE_LEN + 1, (void**) &val) == SUCCESS &&
1544 zend_hash_find(Z_ARRVAL_PP(arg), FAULT_STRING, FAULT_STRING_LEN + 1, (void**) &val) == SUCCESS) {