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