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