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