patch for php-5.3 (the one in f12)
[plcapi.git] / php / xmlrpc / xmlrpc-epi-php.c
1 /*
2   This file is part of, or distributed with, libXMLRPC - a C library for 
3   xml-encoded function calls.
4
5   Author: Dan Libby (dan@libby.com)
6   Epinions.com may be contacted at feedback@epinions-inc.com
7 */
8
9 /*  
10   Copyright 2001 Epinions, Inc. 
11
12   Subject to the following 3 conditions, Epinions, Inc.  permits you, free 
13   of charge, to (a) use, copy, distribute, modify, perform and display this 
14   software and associated documentation files (the "Software"), and (b) 
15   permit others to whom the Software is furnished to do so as well.  
16
17   1) The above copyright notice and this permission notice shall be included 
18   without modification in all copies or substantial portions of the 
19   Software.  
20
21   2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF 
22   ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY 
23   IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR 
24   PURPOSE OR NONINFRINGEMENT.  
25
26   3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, 
27   SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT 
28   OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING 
29   NEGLIGENCE), EVEN IF EPINIONS, INC.  IS AWARE OF THE POSSIBILITY OF SUCH 
30   DAMAGES.    
31
32 */
33
34 /* auto-generated portions of this file are also subject to the php license */
35
36 /*
37    +----------------------------------------------------------------------+
38    | PHP Version 5                                                        |
39    +----------------------------------------------------------------------+
40    | Copyright (c) 1997-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    +----------------------------------------------------------------------+
50    | Author: Dan Libby                                                    |
51    +----------------------------------------------------------------------+
52  */
53
54 /* $Id$ */
55
56 /**********************************************************************
57 * BUGS:                                                               *
58 *  - when calling a php user function, there appears to be no way to  *
59 *    distinguish between a return value of null, and no return value  *
60 *    at all.  The xml serialization layer(s) will then return a value *
61 *    of null, when the right thing may be no value at all. (SOAP)     *
62 **********************************************************************/
63
64 #ifdef HAVE_CONFIG_H
65 #include "config.h"
66 #endif
67
68 #include "php.h"
69 #include "ext/standard/info.h"
70 #include "php_ini.h"
71 #include "php_xmlrpc.h"
72 #include "xmlrpc.h"
73
74 #define PHP_EXT_VERSION "0.51"
75
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
82  */
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)
86                 ZEND_ARG_PASS_INFO(1)
87         ZEND_END_ARG_INFO();
88
89         ZEND_BEGIN_ARG_INFO(second_arg_force_ref, 0)
90                 ZEND_ARG_PASS_INFO(0)
91                 ZEND_ARG_PASS_INFO(1)
92         ZEND_END_ARG_INFO();
93 #endif
94 /* ==================== end patch */
95
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)
99  *
100  * http://developers.evrsoft.com/docs/php/zend.arguments.deprecated-retrieval.shtml
101  * explains how the old (our) stuff works
102  *
103  * http://www.hospedajeydominios.com/mambo/documentacion-manual_php-pagina-zend_arguments_retrieval.html
104  * gives info on the new scheme
105  *
106  * I'm taking tha risk as the changes seem to mean major surgery ...
107  */
108
109 /* You should tweak config.m4 so this symbol (or some else suitable)
110         gets defined.  */
111
112 ZEND_DECLARE_MODULE_GLOBALS(xmlrpc)
113
114 static int le_xmlrpc_server;
115
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)
131         {NULL, NULL, NULL}
132 };
133
134 zend_module_entry xmlrpc_module_entry = {
135         STANDARD_MODULE_HEADER,
136         "xmlrpc",
137         xmlrpc_functions,
138         PHP_MINIT(xmlrpc),
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 */
142         PHP_MINFO(xmlrpc),
143         PHP_EXT_VERSION,
144         STANDARD_MODULE_PROPERTIES
145 };
146
147 #ifdef COMPILE_DL_XMLRPC
148 ZEND_GET_MODULE(xmlrpc)
149 # ifdef PHP_WIN32
150 # include "zend_arg_defs.c"
151 # endif
152 #endif
153
154 PHP_INI_BEGIN()
155 STD_PHP_INI_BOOLEAN("xmlrpc.allow_null", "0", PHP_INI_ALL, OnUpdateBool, allow_null, zend_xmlrpc_globals, xmlrpc_globals)
156 PHP_INI_END()
157
158 static void php_xmlrpc_init_globals(zend_xmlrpc_globals *xmlrpc_globals)
159 {
160         memset(xmlrpc_globals, 0, sizeof(zend_xmlrpc_globals));
161 }
162
163 /*******************************
164 * local structures and defines *
165 *******************************/
166
167 /* per server data */
168 typedef struct _xmlrpc_server_data {
169         zval* method_map;
170         zval* introspection_map;
171         XMLRPC_SERVER server_ptr;
172 } xmlrpc_server_data;
173
174
175 /* how to format output */
176 typedef struct _php_output_options {
177         int b_php_out;
178         int b_auto_version;
179         int b_allow_null;
180         STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out;
181 } php_output_options;
182
183 /* data passed to C callback */
184 typedef struct _xmlrpc_callback_data {
185         zval* xmlrpc_method;
186         zval* php_function;
187         zval* caller_params;
188         zval* return_data;
189         xmlrpc_server_data* server;
190         char php_executed;
191 } xmlrpc_callback_data;
192
193 /* output options */
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"
198
199 #define ALLOW_NULL_KEY     "allow_null"
200 #define ALLOW_NULL_KEY_LEN (sizeof(ALLOW_NULL_KEY) - 1)
201
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"
207
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"
214
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"
221
222 #define ENCODING_KEY     "encoding"
223 #define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1)
224 #define ENCODING_DEFAULT "iso-8859-1"
225
226 /* value types */
227 #define OBJECT_TYPE_ATTR  "xmlrpc_type"
228 #define OBJECT_VALUE_ATTR "scalar"
229 #define OBJECT_VALUE_TS_ATTR "timestamp"
230
231 /* faults */
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)
236
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);
248
249 /*********************
250 * startup / shutdown *
251 *********************/
252
253 static void destroy_server_data(xmlrpc_server_data *server)
254 {
255         if (server) {
256                 XMLRPC_ServerDestroy(server->server_ptr);
257
258                 zval_dtor(server->method_map);
259                 FREE_ZVAL(server->method_map);
260
261                 zval_dtor(server->introspection_map);
262                 FREE_ZVAL(server->introspection_map);
263
264                 efree(server);
265         }
266 }
267
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)
271 {
272         if (rsrc && rsrc->ptr) {
273                 destroy_server_data((xmlrpc_server_data*) rsrc->ptr);
274         }
275 }
276
277 /* module init */
278 PHP_MINIT_FUNCTION(xmlrpc)
279 {
280         ZEND_INIT_MODULE_GLOBALS(xmlrpc, php_xmlrpc_init_globals, NULL);
281
282         REGISTER_INI_ENTRIES();
283
284         le_xmlrpc_server = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);
285
286         return SUCCESS;
287 }
288
289 /* module shutdown */
290 PHP_MSHUTDOWN_FUNCTION(xmlrpc)
291 {
292         return SUCCESS;
293 }
294
295 /* Remove if there's nothing to do at request start */
296 PHP_RINIT_FUNCTION(xmlrpc)
297 {
298         return SUCCESS;
299 }
300
301 /* Remove if there's nothing to do at request end */
302 PHP_RSHUTDOWN_FUNCTION(xmlrpc)
303 {
304         return SUCCESS;
305 }
306
307 /* display info in phpinfo() */
308 PHP_MINFO_FUNCTION(xmlrpc)
309 {
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();
317 }
318
319 /*******************
320 * random utilities *
321 *******************/
322
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.
325  */
326 #if 0
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);
330 }
331
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);
335 }
336
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);
340 }
341
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);
345 }
346
347 #endif
348
349 static int add_zval(zval* list, const char* id, zval** val)
350 {
351         if (list && val) {
352                 if (id) {
353                         return zend_hash_update(Z_ARRVAL_P(list), (char*) id, strlen(id) + 1, (void *) val, sizeof(zval **), NULL);
354                 } else {
355                         return zend_hash_next_index_insert(Z_ARRVAL_P(list), (void *) val, sizeof(zval **), NULL); 
356                 }
357         }
358         /* what is the correct return on error? */
359         return 0;
360 }
361
362 #define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0)
363
364
365 /*************************
366 * input / output options *
367 *************************/
368
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)
372 {
373         XMLRPCLS_FETCH();
374
375         if (options) {
376
377                 /* defaults */
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;
385
386                 if (output_opts && Z_TYPE_P(output_opts) == IS_ARRAY) {
387                         zval** val;
388
389                         /* marshal NULL */
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;
394                                         } else {
395                                                 options->b_allow_null = 0;
396                                         }
397                                 }
398                         }
399
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;
407                                         }
408                                 }
409                         }
410
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;
420                                         }
421                                 }
422                         }
423
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;
436                                         }
437                                 }
438                         }
439
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));
446                      }
447                   }
448
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) {
455                         zval** iter_val;
456                         zend_hash_internal_pointer_reset(Z_ARRVAL_PP(val));
457                         options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping;
458                         while(1) {
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;
463                                  }
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;
466                                  }
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;
469                                  }
470                                  else if(!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_MARKUP)) {
471                                     options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping;
472                                  }
473                               }
474                            }
475                            else {
476                               break;
477                            }
478
479                            zend_hash_move_forward(Z_ARRVAL_PP(val));
480                         }
481                      }
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;
486                         }
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;
489                         }
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;
492                         }
493                         else if(!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_MARKUP)) {
494                            options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping;
495                         }
496                      }
497                   }
498           }
499         }
500 }
501
502
503 /******************
504 * encode / decode *
505 ******************/
506
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.
511  */
512 static XMLRPC_VECTOR_TYPE determine_vector_type (HashTable *ht)
513 {
514     int bArray = 0, bStruct = 0, bMixed = 0;
515     unsigned long num_index;
516     char* my_key;
517
518     zend_hash_internal_pointer_reset(ht);
519     while(1) {
520        int res = my_zend_hash_get_current_key(ht, &my_key, &num_index);
521        if(res == HASH_KEY_IS_LONG) {
522            if(bStruct) {
523                bMixed = 1;
524                break;
525            }
526            bArray = 1;
527        }
528        else if(res == HASH_KEY_NON_EXISTANT) {
529           break;
530        }
531        else if(res == HASH_KEY_IS_STRING) {
532            if(bArray) {
533                bMixed = 1;
534                break;
535            }
536            bStruct = 1;
537        }
538
539        zend_hash_move_forward(ht);
540     }
541     return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array);
542 }
543
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)
546 {
547    XMLRPC_VALUE xReturn = NULL;
548    if(in_val) {
549       zval* val = NULL;
550       XMLRPC_VALUE_TYPE type = get_zval_xmlrpc_type(out, in_val, &val);
551       if(val) {
552          switch(type) {
553             case xmlrpc_nil:
554                xReturn = XMLRPC_CreateValueEmpty();
555                XMLRPC_SetValueID(xReturn, key, 0);
556                break;
557             case xmlrpc_base64:
558                xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
559                break;
560             case xmlrpc_datetime:
561                convert_to_string(val);
562                xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL_P(val));
563                break;
564             case xmlrpc_boolean:
565                convert_to_boolean(val);
566                xReturn = XMLRPC_CreateValueBoolean(key, Z_LVAL_P(val));
567                break;
568             case xmlrpc_int:
569                convert_to_long(val);
570                xReturn = XMLRPC_CreateValueInt(key, Z_LVAL_P(val));
571                break;
572             case xmlrpc_double:
573                convert_to_double(val);
574                xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL_P(val));
575                break;
576             case xmlrpc_string:
577                convert_to_string(val);
578                xReturn = XMLRPC_CreateValueString(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
579                break;
580             case xmlrpc_vector:
581                {
582                   unsigned long num_index;
583                   zval** pIter;
584                   char* my_key;
585
586                   convert_to_array(val);
587
588                   xReturn = XMLRPC_CreateVector(key, determine_vector_type(Z_ARRVAL_P(val)));
589
590                   zend_hash_internal_pointer_reset(Z_ARRVAL_P(val));
591                   while(1) {
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++));
596                         }
597                      }
598                      else if(res == HASH_KEY_NON_EXISTANT) {
599                         break;
600                      }
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++));
604                         }
605                      }
606
607                      zend_hash_move_forward(Z_ARRVAL_P(val));
608                   }
609                }
610                break;
611             default:
612                break;
613          }
614       }
615    }
616    return xReturn;
617 }
618
619 static XMLRPC_VALUE PHP_to_XMLRPC(php_output_options* out, zval* root_val)
620 {
621    return PHP_to_XMLRPC_worker(out, NULL, root_val, 0);
622 }
623
624 /* recursively convert xmlrpc values into php values */
625 static zval* XMLRPC_to_PHP(XMLRPC_VALUE el)
626 {
627    zval* elem = NULL;
628    const char* pStr;
629
630    if(el) {
631       XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
632
633       MAKE_STD_ZVAL(elem); /* init. very important.  spent a frustrating day finding this out. */
634
635       switch(type) {
636          case xmlrpc_empty:
637          case xmlrpc_nil:
638             Z_TYPE_P(elem) = IS_NULL;
639             break;
640          case xmlrpc_string:
641             pStr = XMLRPC_GetValueString(el);
642             if(pStr) {
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;
646             }
647             break;
648          case xmlrpc_int:
649             Z_LVAL_P(elem) = XMLRPC_GetValueInt(el);
650             Z_TYPE_P(elem) = IS_LONG;
651             break;
652          case xmlrpc_boolean:
653             Z_LVAL_P(elem) = XMLRPC_GetValueBoolean(el);
654             Z_TYPE_P(elem) = IS_BOOL;
655             break;
656          case xmlrpc_double:
657             Z_DVAL_P(elem) = XMLRPC_GetValueDouble(el);
658             Z_TYPE_P(elem) = IS_DOUBLE;
659             break;
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;
664             break;
665          case xmlrpc_base64:
666             pStr = XMLRPC_GetValueBase64(el);
667             if(pStr) {
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;
671             }
672             break;
673          case xmlrpc_vector:
674                 array_init(elem);
675                 {
676                         XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
677
678                         while( xIter ) {
679                                 zval *val = XMLRPC_to_PHP(xIter);
680                                 if (val) {
681                                         add_zval(elem, XMLRPC_GetValueID(xIter), &val);
682                                 }
683                                 xIter = XMLRPC_VectorNext(el);
684                         }
685                 }
686                 break;
687          default:
688             break;
689       }
690       set_zval_xmlrpc_type(elem, type);
691    }
692    return elem;
693 }
694
695 /* {{{ proto string xmlrpc_encode_request(string method, mixed params)
696    Generates XML for a method request */
697 PHP_FUNCTION(xmlrpc_encode_request)
698 {
699         XMLRPC_REQUEST xRequest = NULL;
700         zval **method, **vals, **out_opts;
701         char* outBuf;
702         php_output_options out;
703
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 */
706         }
707
708         set_output_options(&out, (ZEND_NUM_ARGS() == 3) ? *out_opts : 0);
709
710         if(return_value_used) {
711                 xRequest = XMLRPC_RequestNew();
712
713                 if(xRequest) {
714                         XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
715                         if (Z_TYPE_PP(method) == IS_NULL) {
716                                 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
717                         } else {
718                                 XMLRPC_RequestSetMethodName(xRequest, Z_STRVAL_PP(method));
719                                 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
720                         }
721                         if (Z_TYPE_PP(vals) != IS_NULL) {
722                                 XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(&out, *vals));
723                         }
724
725                         outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
726                         if(outBuf) {
727                                 RETVAL_STRING(outBuf, 1);
728                                 free(outBuf);
729                         }
730                         XMLRPC_RequestFree(xRequest, 1);
731                 }
732         }
733 }
734 /* }}} */
735
736 /* {{{ proto string xmlrpc_encode(mixed value)
737    Generates XML for a PHP value */
738 PHP_FUNCTION(xmlrpc_encode)
739 {
740         XMLRPC_VALUE xOut = NULL;
741         zval **arg1;
742         char *outBuf;
743
744         if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
745                 WRONG_PARAM_COUNT;
746         }
747
748         if( return_value_used ) {
749                 /* convert native php type to xmlrpc type */
750                 xOut = PHP_to_XMLRPC(NULL, *arg1);
751
752                 /* generate raw xml from xmlrpc data */
753                 outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
754
755                 if(xOut) {
756                         if(outBuf) {
757                                 RETVAL_STRING(outBuf, 1);
758                                 free(outBuf);
759                         }
760                         /* cleanup */
761                         XMLRPC_CleanupValue(xOut);
762                 }
763         }
764 }
765 /* }}} */
766
767
768 zval* decode_request_worker (zval* xml_in, zval* encoding_in, zval* method_name_out)
769 {
770    zval* retval = NULL;
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;
774
775    /* generate XMLRPC_REQUEST from raw xml */
776    response = XMLRPC_REQUEST_FromXML(Z_STRVAL_P(xml_in), Z_STRLEN_P(xml_in), &opts);
777    if(response) {
778       /* convert xmlrpc data to native php types */
779       retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response));
780
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));
787          }
788       }
789
790       /* dust, sweep, and mop */
791       XMLRPC_RequestFree(response, 1);
792    }
793    return retval;
794 }
795
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)
799 {
800         zval **xml, **method, **encoding = NULL;
801         int argc = ZEND_NUM_ARGS();
802
803         if (argc < 2 || argc > 3 || (zend_get_parameters_ex(argc, &xml, &method, &encoding) == FAILURE)) {
804                 WRONG_PARAM_COUNT;
805         }
806
807         convert_to_string_ex(xml);
808         convert_to_string_ex(method);
809         if(argc == 3) {
810                 convert_to_string_ex(encoding);
811         }
812
813         if(return_value_used) {
814                 zval* retval = decode_request_worker(*xml, encoding ? *encoding : NULL, *method);
815                 if(retval) {
816                         *return_value = *retval;
817                         FREE_ZVAL(retval);
818                 }
819         }
820 }
821 /* }}} */
822
823
824 /* {{{ proto array xmlrpc_decode(string xml [, string encoding])
825    Decodes XML into native PHP types */
826 PHP_FUNCTION(xmlrpc_decode)
827 {
828         zval **arg1, **arg2 = NULL;
829         int argc = ZEND_NUM_ARGS();
830
831         if (argc < 1 || argc > 2 || (zend_get_parameters_ex(argc, &arg1, &arg2) == FAILURE)) {
832                 WRONG_PARAM_COUNT;
833         }
834
835         convert_to_string_ex(arg1);
836         if(argc == 2) {
837                 convert_to_string_ex(arg2);
838         }
839
840         if(return_value_used) {
841                 zval* retval = decode_request_worker(*arg1, arg2 ? *arg2 : NULL, NULL);
842                 if(retval) {
843                         *return_value = *retval;
844                         FREE_ZVAL(retval);
845                 }
846         }
847 }
848 /* }}} */
849
850
851 /*************************
852 * server related methods *
853 *************************/
854
855 /* {{{ proto resource xmlrpc_server_create(void)
856    Creates an xmlrpc server */
857 PHP_FUNCTION(xmlrpc_server_create)
858 {
859         if(ZEND_NUM_ARGS() != 0) {
860                 WRONG_PARAM_COUNT;
861         }
862
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);
868                 
869                 array_init(method_map);
870                 array_init(introspection_map);
871                 
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();
876
877                 XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
878
879                 /* store for later use */
880                 ZEND_REGISTER_RESOURCE(return_value,server, le_xmlrpc_server);
881         }
882 }
883 /* }}} */
884
885 /* {{{ proto int xmlrpc_server_destroy(resource server)
886    Destroys server resources */
887 PHP_FUNCTION(xmlrpc_server_destroy)
888 {
889         zval **arg1;
890         int bSuccess = FAILURE;
891
892         if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
893                 WRONG_PARAM_COUNT;
894         }
895
896         if(Z_TYPE_PP(arg1) == IS_RESOURCE) {
897                 int type;
898
899                 xmlrpc_server_data *server = zend_list_find(Z_LVAL_PP(arg1), &type);
900
901                 if(server && type == le_xmlrpc_server) {
902                         bSuccess = zend_list_delete(Z_LVAL_PP(arg1));
903
904                         /* called by hashtable destructor
905                          * destroy_server_data(server);
906                          */
907                 }
908         }
909         RETVAL_LONG(bSuccess == SUCCESS);
910 }
911 /* }}} */
912
913            
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.
916  */
917 static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data)
918 {
919    xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
920    zval* xmlrpc_params;
921    zval* callback_params[3];
922    TSRMLS_FETCH();
923    
924    /* convert xmlrpc to native php types */
925    xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest));
926
927    /* setup data hoojum */
928    callback_params[0] = pData->xmlrpc_method;
929    callback_params[1] = xmlrpc_params;
930    callback_params[2] = pData->caller_params;
931
932    /* Use same C function for all methods */
933
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);
936
937    pData->php_executed = 1;
938
939         zval_dtor(xmlrpc_params);
940         FREE_ZVAL(xmlrpc_params);
941
942         return NULL;
943 }
944
945 /* called by the C server when it first receives an introspection request.  We pass this on to
946  * our PHP listeners, if any
947  */
948 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data)
949 {
950    zval *retval_ptr, **php_function;
951    zval* callback_params[1];
952    xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
953    TSRMLS_FETCH();
954    
955    MAKE_STD_ZVAL(retval_ptr);
956    Z_TYPE_P(retval_ptr) = IS_NULL;
957
958    /* setup data hoojum */
959    callback_params[0] = pData->caller_params;
960
961    /* loop through and call all registered callbacks */
962    zend_hash_internal_pointer_reset(Z_ARRVAL_P(pData->server->introspection_map));
963    while(1) {
964       if(zend_hash_get_current_data(Z_ARRVAL_P(pData->server->introspection_map), 
965                                     (void**)&php_function) == SUCCESS) {
966
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) {
970             XMLRPC_VALUE xData;
971             STRUCT_XMLRPC_ERROR err = {0};
972
973             /* return value should be a string */
974             convert_to_string(retval_ptr);
975
976             xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_P(retval_ptr), &err);
977
978             if(xData) {
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));
981                }
982                XMLRPC_CleanupValue(xData);
983             }
984             else {
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));
989                }
990                else {
991                   php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s()", 
992                              Z_STRVAL_PP(php_function));
993                }
994             }
995          }
996          else {
997             /* user func failed */
998             php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error calling user introspection callback: %s()", Z_STRVAL_PP(php_function));
999          }
1000       }
1001       else {
1002          break;
1003       }
1004
1005       zend_hash_move_forward(Z_ARRVAL_P(pData->server->introspection_map));
1006    }
1007
1008    /* so we don't call the same callbacks ever again */
1009    zend_hash_clean(Z_ARRVAL_P(pData->server->introspection_map));
1010 }
1011
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)
1015 {
1016         zval **method_key, **method_name, **handle, *method_name_save;
1017         int type;
1018         xmlrpc_server_data* server;
1019
1020         if (ZEND_NUM_ARGS() != 3 || (zend_get_parameters_ex(3, &handle, &method_key, &method_name) == FAILURE)) {
1021                 WRONG_PARAM_COUNT;
1022         }
1023
1024         server = zend_list_find(Z_LVAL_PP(handle), &type);
1025
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
1029                  */
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);
1035
1036                         /* register our php method */
1037                         add_zval(server->method_map, Z_STRVAL_PP(method_key), &method_name_save);
1038
1039                         RETURN_BOOL(1);
1040                 }
1041         }
1042         RETURN_BOOL(0);
1043 }
1044 /* }}} */
1045
1046
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)
1050 {
1051         zval **method_name, **handle, *method_name_save;
1052         int type;
1053         xmlrpc_server_data* server;
1054
1055         if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &method_name) == FAILURE)) {
1056                 WRONG_PARAM_COUNT;
1057         }
1058
1059         server = zend_list_find(Z_LVAL_PP(handle), &type);
1060
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);
1066
1067                 /* register our php method */
1068                 add_zval(server->introspection_map, NULL, &method_name_save);
1069
1070                 RETURN_BOOL(1);
1071         }
1072         RETURN_BOOL(0);
1073 }
1074 /* }}} */
1075
1076
1077 /* this function is itchin for a re-write */
1078
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)
1082 {
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;
1088         int type;
1089         php_output_options out;
1090         int argc =ZEND_NUM_ARGS();
1091         
1092         if (argc < 3 || argc > 4 || (zend_get_parameters_ex(argc, &handle, &rawxml, &caller_params, &output_opts) != SUCCESS)) {
1093                 WRONG_PARAM_COUNT;
1094         }
1095         /* user output options */
1096         if (argc == 3) {
1097                 set_output_options(&out, NULL);
1098         }
1099         else {
1100                 set_output_options(&out, *output_opts);
1101         }
1102
1103         server = zend_list_find(Z_LVAL_PP(handle), &type);
1104
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);
1108
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);
1111
1112                 if(xRequest) {
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;
1120
1121                         if (!methodname) {
1122                                 methodname = "";
1123                         }
1124             
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;
1132
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) {
1138
1139                                 data.php_function = *php_function;
1140                         }
1141
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,
1144                          * or somesuch.
1145                          */
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);
1153                         }
1154
1155                         /* should we return data as xml? */
1156                         if(!out.b_php_out) {
1157                                 XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
1158                                 if(xResponse) {
1159                                         char *outBuf = 0;
1160                                         int buf_len = 0;
1161
1162                                         /* automagically determine output serialization type from request type */
1163                                         if (out.b_auto_version) { 
1164                                                 XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
1165                                                 if (opts) {
1166                                                         out.xmlrpc_out.version = opts->version;
1167                                                 }
1168                                         }
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);
1174
1175                                         /* generate xml */
1176                                         outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
1177                                         if(outBuf) {
1178                                                 RETVAL_STRINGL(outBuf, buf_len, 1);
1179                                                 free(outBuf);
1180                                         }
1181                                         /* cleanup after ourselves.  what a sty! */
1182                                         XMLRPC_RequestFree(xResponse, 0);
1183                                 }
1184                         } else { /* or as native php types? */
1185                                 *return_value = *data.return_data;
1186                                 zval_copy_ctor(return_value);
1187                         }
1188
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);
1194
1195                         if(xAnswer) {
1196                                 XMLRPC_CleanupValue(xAnswer);
1197                         }
1198
1199                         XMLRPC_RequestFree(xRequest, 1);
1200                 }
1201         }
1202 }
1203 /* }}} */
1204
1205
1206 /* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
1207    Adds introspection documentation  */
1208 PHP_FUNCTION(xmlrpc_server_add_introspection_data)
1209 {
1210         zval **handle, **desc;
1211         int type;
1212         xmlrpc_server_data* server;
1213
1214         if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &handle, &desc) == FAILURE)) {
1215                 WRONG_PARAM_COUNT;
1216         }
1217
1218         server = zend_list_find(Z_LVAL_PP(handle), &type);
1219
1220         if (type == le_xmlrpc_server) {
1221                 XMLRPC_VALUE xDesc = PHP_to_XMLRPC(NULL, *desc);
1222                 if (xDesc) {
1223                         int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
1224                         XMLRPC_CleanupValue(xDesc);
1225                         RETURN_LONG(retval);
1226                 }
1227         }
1228         RETURN_LONG(0);
1229 }
1230 /* }}} */
1231
1232
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)
1236 {
1237         zval **arg1, *retval;
1238
1239         if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg1) == FAILURE)) {
1240                 WRONG_PARAM_COUNT;
1241         }
1242
1243         convert_to_string_ex(arg1);
1244
1245         if(return_value_used) {
1246                 STRUCT_XMLRPC_ERROR err = {0};
1247                 XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(Z_STRVAL_PP(arg1), &err);
1248                 if(xVal) {
1249                         retval = XMLRPC_to_PHP(xVal);
1250
1251                         if(retval) {
1252                                 *return_value = *retval;
1253                                 zval_copy_ctor(return_value);
1254                         }
1255                         /* dust, sweep, and mop */
1256                         XMLRPC_CleanupValue(xVal);
1257                 } else {
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);
1262                         } else {
1263                                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid xml structure. Unable to create introspection data");
1264                         }
1265
1266                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error. no method description created");
1267                 }
1268         }
1269 }
1270 /* }}} */
1271
1272
1273 /************
1274 * type data *
1275 ************/
1276
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)
1280
1281 /* return a string matching a given xmlrpc type */
1282 static const char** get_type_str_mapping(void)
1283 {
1284    static const char* str_mapping[TYPE_STR_MAP_SIZE];
1285    static int first = 1;
1286    if (first) {
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";
1302       first = 0;
1303    }
1304    return (const char**)str_mapping;
1305 }
1306
1307 /* map an xmlrpc type to a string */
1308 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype)
1309 {
1310    const char** str_mapping = get_type_str_mapping();
1311
1312    if (vtype == xmlrpc_vector_none) {
1313       return str_mapping[type];
1314    } else {
1315       return str_mapping[XMLRPC_TYPE_COUNT + vtype];
1316    }
1317 }
1318
1319 /* map a string to an xmlrpc type */
1320 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str)
1321 {
1322    const char** str_mapping = get_type_str_mapping();
1323    int i;
1324
1325    if (str) {
1326       for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
1327          if (!strcmp(str_mapping[i], str)) {
1328             return (XMLRPC_VALUE_TYPE) i;
1329          }
1330       }
1331    }
1332    return xmlrpc_none;
1333 }
1334
1335 /* map a string to an xmlrpc vector type */
1336 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str)
1337 {
1338    const char** str_mapping = get_type_str_mapping();
1339    int i;
1340
1341    if (str) {
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);
1345          }
1346       }
1347    }
1348    return xmlrpc_none;
1349 }
1350
1351
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.
1355  */
1356 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype)
1357 {
1358    int bSuccess = FAILURE;
1359    TSRMLS_FETCH();
1360
1361    /* we only really care about strings because they can represent
1362     * base64 and datetime.  all other types have corresponding php types
1363     */
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);
1367          zval* type;
1368
1369          MAKE_STD_ZVAL(type);
1370
1371          Z_TYPE_P(type) = IS_STRING;
1372          Z_STRVAL_P(type) = estrdup(typestr);
1373          Z_STRLEN_P(type) = strlen(typestr);
1374
1375          if(newtype == xmlrpc_datetime) {
1376             XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, value->value.str.val);
1377             if(v) {
1378                time_t timestamp = XMLRPC_GetValueDateTime(v);
1379                if(timestamp) {
1380                   pval* ztimestamp;
1381
1382                   MAKE_STD_ZVAL(ztimestamp);
1383
1384                   ztimestamp->type = IS_LONG;
1385                   ztimestamp->value.lval = timestamp;
1386
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);
1390                   }
1391                }
1392                XMLRPC_CleanupValue(v);
1393             }
1394          }
1395          else {
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);
1398          }
1399       }
1400    }
1401    
1402    return bSuccess;
1403 }
1404
1405 /* return xmlrpc type of a php value */
1406 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(php_output_options* out, zval* value, zval** newvalue)
1407 {
1408    XMLRPC_VALUE_TYPE type = xmlrpc_none;
1409    TSRMLS_FETCH();
1410    XMLRPCLS_FETCH();
1411
1412    if (value) {
1413       switch (Z_TYPE_P(value)) {
1414          case IS_NULL:
1415             if (XMLRPCG(allow_null) || (out && out->b_allow_null)) {
1416                type = xmlrpc_nil;
1417             } else {
1418                type = xmlrpc_string;
1419             }
1420             break;
1421 #ifndef BOOL_AS_LONG
1422
1423    /* Right thing to do, but it breaks some legacy code. */
1424          case IS_BOOL:
1425             type = xmlrpc_boolean;
1426             break;
1427 #else
1428          case IS_BOOL:
1429 #endif
1430          case IS_LONG:
1431          case IS_RESOURCE:
1432             type = xmlrpc_int;
1433             break;
1434          case IS_DOUBLE:
1435             type = xmlrpc_double;
1436             break;
1437          case IS_CONSTANT:
1438             type = xmlrpc_string;
1439             break;
1440          case IS_STRING:
1441             type = xmlrpc_string;
1442             break;
1443          case IS_ARRAY:
1444          case IS_CONSTANT_ARRAY:
1445             type = xmlrpc_vector;
1446             break;
1447          case IS_OBJECT:
1448          {
1449             zval** attr;
1450             type = xmlrpc_vector;
1451
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));
1455                }
1456             }
1457             break;
1458          }
1459       }
1460
1461                 /* if requested, return an unmolested (magic removed) copy of the value */
1462                 if (newvalue) {
1463                         zval** val;
1464
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) {
1467                                         *newvalue = *val;
1468                                 }
1469                         } else {
1470                                 *newvalue = value;
1471                         }
1472                 }
1473         }
1474
1475         return type;
1476 }
1477
1478
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)
1482 {
1483         zval **arg, **type;
1484         XMLRPC_VALUE_TYPE vtype;
1485
1486         if (ZEND_NUM_ARGS() != 2 || (zend_get_parameters_ex(2, &arg, &type) == FAILURE)) {
1487                 WRONG_PARAM_COUNT;
1488         }
1489
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) {
1494                         RETURN_TRUE;
1495                 }
1496         } else {
1497                 zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", Z_STRVAL_PP(type));
1498         }
1499         RETURN_FALSE;
1500 }
1501 /* }}} */
1502
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)
1506 {
1507         zval **arg;
1508         XMLRPC_VALUE_TYPE type;
1509         XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
1510
1511         if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1512                 WRONG_PARAM_COUNT;
1513         }
1514
1515         type = get_zval_xmlrpc_type(NULL, *arg, 0);
1516         if (type == xmlrpc_vector) {
1517                 vtype = determine_vector_type(Z_ARRVAL_PP(arg));
1518         }
1519    
1520         RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype), 1);
1521 }
1522 /* }}} */
1523
1524 /* {{{ proto bool xmlrpc_is_fault(array)
1525    Determines if an array value represents an XMLRPC fault. */
1526 PHP_FUNCTION(xmlrpc_is_fault)
1527 {
1528         zval **arg, **val;
1529
1530         if (ZEND_NUM_ARGS() != 1 || (zend_get_parameters_ex(1, &arg) == FAILURE)) {
1531                 WRONG_PARAM_COUNT;
1532         }
1533
1534         if (Z_TYPE_PP(arg) != IS_ARRAY) {
1535                 php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Array argument expected");
1536         } else {
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.
1542                  */
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) {
1545                         RETURN_TRUE;
1546                 }
1547         }
1548
1549         RETURN_FALSE;
1550 }
1551 /* }}} */
1552
1553
1554
1555 /*
1556  * Local variables:
1557  * tab-width: 4
1558  * c-basic-offset: 4
1559  * End:
1560  */
1561