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