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