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