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)
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)
282{
283    if (rsrc && rsrc->ptr) {
284        rsrc->gc.refcount++;
285        destroy_server_data((xmlrpc_server_data*) rsrc->ptr);
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)
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, 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++));
576                                if (num_str) {
577                                    efree(num_str);
578                                }
579                            } else {
580                                XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(my_key->val, pIter, depth++));
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)
598{
599    return PHP_to_XMLRPC_worker(NULL, root_val, 0);
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(), "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));
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(), "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);
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(), "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(), "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(), "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
869    zval_ptr_dtor(&pData->xmlrpc_method);
870    zval_ptr_dtor(&pData->return_data);
871
872    /* convert xmlrpc to native php types */
873    ZVAL_STRING(&pData->xmlrpc_method, XMLRPC_RequestGetMethodName(xRequest));
874    XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest), &xmlrpc_params);
875
876    /* check if the called method has been previous registered */
877    if ((php_function = zend_hash_find(Z_ARRVAL(pData->server->method_map), Z_STR(pData->xmlrpc_method))) != NULL) {
878        ZVAL_COPY_VALUE(&pData->php_function, php_function);
879    }
880
881    /* setup data hoojum */
882    ZVAL_COPY_VALUE(&callback_params[0], &pData->xmlrpc_method);
883    ZVAL_COPY_VALUE(&callback_params[1], &xmlrpc_params);
884    ZVAL_COPY_VALUE(&callback_params[2], &pData->caller_params);
885
886    /* Use same C function for all methods */
887
888    /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
889    call_user_function(CG(function_table), NULL, &pData->php_function, &pData->return_data, 3, callback_params);
890
891    pData->php_executed = 1;
892
893    zval_ptr_dtor(&xmlrpc_params);
894
895    return PHP_to_XMLRPC(&pData->return_data);
896}
897/* }}} */
898
899/* called by the C server when it first receives an introspection request.  We pass this on to
900 * our PHP listeners, if any
901 */
902static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) /* {{{ */
903{
904    zval retval, *php_function;
905    zval callback_params[1];
906    zend_string *php_function_name;
907    xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
908
909    /* setup data hoojum */
910    ZVAL_COPY_VALUE(&callback_params[0], &pData->caller_params);
911
912    ZEND_HASH_FOREACH_VAL(Z_ARRVAL(pData->server->introspection_map), php_function) {
913        if (zend_is_callable(php_function, 0, &php_function_name)) {
914            /* php func prototype: function string user_func($user_params) */
915            if (call_user_function(CG(function_table), NULL, php_function, &retval, 1, callback_params) == SUCCESS) {
916                XMLRPC_VALUE xData;
917                STRUCT_XMLRPC_ERROR err = {0};
918
919                /* return value should be a string */
920                convert_to_string(&retval);
921
922                xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL(retval), &err);
923
924                if (xData) {
925                    if (!XMLRPC_ServerAddIntrospectionData(server, xData)) {
926                        php_error_docref(NULL, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", php_function_name->val);
927                    }
928                    XMLRPC_CleanupValue(xData);
929                } else {
930                    /* could not create description */
931                    if (err.xml_elem_error.parser_code) {
932                        php_error_docref(NULL, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to add introspection data returned from %s()",
933                                err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, php_function_name->val);
934                    } else {
935                        php_error_docref(NULL, E_WARNING, "Unable to add introspection data returned from %s()", php_function_name->val);
936                    }
937                }
938                zval_ptr_dtor(&retval);
939            } else {
940                /* user func failed */
941                php_error_docref(NULL, E_WARNING, "Error calling user introspection callback: %s()", php_function_name->val);
942            }
943        } else {
944            php_error_docref(NULL, E_WARNING, "Invalid callback '%s' passed", php_function_name->val);
945        }
946        zend_string_release(php_function_name);
947    } ZEND_HASH_FOREACH_END();
948
949    /* so we don't call the same callbacks ever again */
950    zend_hash_clean(Z_ARRVAL(pData->server->introspection_map));
951}
952/* }}} */
953
954/* {{{ proto bool xmlrpc_server_register_method(resource server, string method_name, string function)
955   Register a PHP function to handle method matching method_name */
956PHP_FUNCTION(xmlrpc_server_register_method)
957{
958    char *method_key;
959    size_t method_key_len;
960    zval *handle, *method_name;
961    xmlrpc_server_data* server;
962
963    if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsz", &handle, &method_key, &method_key_len, &method_name) == FAILURE) {
964        return;
965    }
966
967    ZEND_FETCH_RESOURCE(server, xmlrpc_server_data*, handle, -1, "xmlrpc server", le_xmlrpc_server);
968
969    /* register with C engine. every method just calls our standard callback,
970     * and it then dispatches to php as necessary
971     */
972    if (XMLRPC_ServerRegisterMethod(server->server_ptr, method_key, php_xmlrpc_callback)) {
973        /* save for later use */
974
975        if (Z_REFCOUNTED_P(method_name)) {
976            Z_ADDREF_P(method_name);
977        }
978        /* register our php method */
979        add_zval(&server->method_map, method_key, method_name);
980
981        RETURN_TRUE;
982    }
983}
984/* }}} */
985
986/* {{{ proto bool xmlrpc_server_register_introspection_callback(resource server, string function)
987   Register a PHP function to generate documentation */
988PHP_FUNCTION(xmlrpc_server_register_introspection_callback)
989{
990    zval *method_name, *handle;
991    xmlrpc_server_data* server;
992
993    if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz", &handle, &method_name) == FAILURE) {
994        return;
995    }
996
997    ZEND_FETCH_RESOURCE(server, xmlrpc_server_data*, handle, -1, "xmlrpc server", le_xmlrpc_server);
998
999    if (Z_REFCOUNTED_P(method_name)) {
1000        Z_ADDREF_P(method_name);
1001    }
1002    /* register our php method */
1003    add_zval(&server->introspection_map, NULL, method_name);
1004
1005    RETURN_TRUE;
1006}
1007/* }}} */
1008
1009/* this function is itchin for a re-write */
1010
1011/* {{{ proto mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])
1012   Parses XML requests and call methods */
1013PHP_FUNCTION(xmlrpc_server_call_method)
1014{
1015    XMLRPC_REQUEST xRequest;
1016    xmlrpc_callback_data data;
1017    STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
1018    xmlrpc_server_data* server;
1019    zval *caller_params, *handle, *output_opts = NULL;
1020    char *rawxml;
1021    size_t rawxml_len;
1022    php_output_options out;
1023    int argc = ZEND_NUM_ARGS();
1024
1025    if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsz|a", &handle, &rawxml, &rawxml_len, &caller_params, &output_opts) != SUCCESS) {
1026        return;
1027    }
1028    /* user output options */
1029    if (argc == 3) {
1030        set_output_options(&out, NULL);
1031    } else {
1032        set_output_options(&out, output_opts);
1033    }
1034
1035    ZEND_FETCH_RESOURCE(server, xmlrpc_server_data*, handle, -1, "xmlrpc server", le_xmlrpc_server);
1036
1037    /* HACK: use output encoding for now */
1038    input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
1039
1040    /* generate an XMLRPC_REQUEST from the raw xml input */
1041    xRequest = XMLRPC_REQUEST_FromXML(rawxml, rawxml_len, &input_opts);
1042
1043    if (xRequest) {
1044        const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
1045        XMLRPC_VALUE xAnswer = NULL;
1046        ZVAL_NULL(&data.xmlrpc_method); /* init. very important.  spent a frustrating day finding this out. */
1047        ZVAL_NULL(&data.return_data);
1048        ZVAL_NULL(&data.return_data);  /* in case value is never init'd, we don't dtor to think it is a string or something */
1049        ZVAL_NULL(&data.xmlrpc_method);
1050
1051        /* setup some data to pass to the callback function */
1052        ZVAL_COPY_VALUE(&data.caller_params, caller_params);
1053        data.php_executed = 0;
1054        data.server = server;
1055
1056        /* We could just call the php method directly ourselves at this point, but we do this
1057         * with a C callback in case the xmlrpc library ever implements some cool usage stats,
1058         * or somesuch.
1059         */
1060        xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
1061        if (xAnswer && out.b_php_out) {
1062            XMLRPC_to_PHP(xAnswer, &data.return_data);
1063        } else if (data.php_executed && !out.b_php_out && !xAnswer) {
1064            xAnswer = PHP_to_XMLRPC(&data.return_data);
1065        }
1066
1067        /* should we return data as xml? */
1068        if (!out.b_php_out) {
1069            XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
1070            if (xResponse) {
1071                char *outBuf = 0;
1072                int buf_len = 0;
1073
1074                /* automagically determine output serialization type from request type */
1075                if (out.b_auto_version) {
1076                    XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
1077                    if (opts) {
1078                        out.xmlrpc_out.version = opts->version;
1079                    }
1080                }
1081                /* set some required request hoojum */
1082                XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
1083                XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
1084                XMLRPC_RequestSetData(xResponse, xAnswer);
1085                XMLRPC_RequestSetMethodName(xResponse, methodname);
1086
1087                /* generate xml */
1088                outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
1089                if (outBuf) {
1090                    RETVAL_STRINGL(outBuf, buf_len);
1091                    free(outBuf);
1092                }
1093                /* cleanup after ourselves.  what a sty! */
1094                XMLRPC_RequestFree(xResponse, 0);
1095            }
1096        } else { /* or as native php types? */
1097            ZVAL_COPY(return_value, &data.return_data);
1098        }
1099
1100        /* cleanup after ourselves.  what a sty! */
1101        zval_ptr_dtor(&data.xmlrpc_method);
1102        zval_ptr_dtor(&data.return_data);
1103
1104        if (xAnswer) {
1105            XMLRPC_CleanupValue(xAnswer);
1106        }
1107
1108        XMLRPC_RequestFree(xRequest, 1);
1109    }
1110}
1111/* }}} */
1112
1113/* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
1114   Adds introspection documentation  */
1115PHP_FUNCTION(xmlrpc_server_add_introspection_data)
1116{
1117    zval *handle, *desc;
1118    xmlrpc_server_data* server;
1119    XMLRPC_VALUE xDesc;
1120
1121    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra", &handle, &desc) == FAILURE) {
1122        return;
1123    }
1124
1125    ZEND_FETCH_RESOURCE(server, xmlrpc_server_data*, handle, -1, "xmlrpc server", le_xmlrpc_server);
1126
1127    xDesc = PHP_to_XMLRPC(desc);
1128    if (xDesc) {
1129        int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
1130        XMLRPC_CleanupValue(xDesc);
1131        RETURN_LONG(retval);
1132    }
1133    RETURN_LONG(0);
1134}
1135/* }}} */
1136
1137/* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
1138   Decodes XML into a list of method descriptions */
1139PHP_FUNCTION(xmlrpc_parse_method_descriptions)
1140{
1141    char *arg1;
1142    size_t arg1_len;
1143
1144    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg1, &arg1_len) == FAILURE) {
1145        return;
1146    }
1147
1148    if (USED_RET()) {
1149        STRUCT_XMLRPC_ERROR err = {0};
1150        XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1, &err);
1151        if (xVal) {
1152            XMLRPC_to_PHP(xVal, return_value);
1153            /* dust, sweep, and mop */
1154            XMLRPC_CleanupValue(xVal);
1155        } else {
1156            /* could not create description */
1157            if (err.xml_elem_error.parser_code) {
1158                php_error_docref(NULL, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data",
1159                        err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
1160            } else {
1161                php_error_docref(NULL, E_WARNING, "Invalid xml structure. Unable to create introspection data");
1162            }
1163
1164            php_error_docref(NULL, E_WARNING, "xml parse error. no method description created");
1165        }
1166    }
1167}
1168/* }}} */
1169
1170/************
1171* type data *
1172************/
1173
1174#define XMLRPC_TYPE_COUNT 9
1175#define XMLRPC_VECTOR_TYPE_COUNT 4
1176#define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
1177
1178/* return a string matching a given xmlrpc type */
1179static const char** get_type_str_mapping(void) /* {{{ */
1180{
1181    static const char* str_mapping[TYPE_STR_MAP_SIZE];
1182    static int first = 1;
1183    if (first) {
1184        /* warning. do not add/delete without changing size define */
1185        str_mapping[xmlrpc_none]     = "none";
1186        str_mapping[xmlrpc_empty]    = "empty";
1187        str_mapping[xmlrpc_base64]   = "base64";
1188        str_mapping[xmlrpc_boolean]  = "boolean";
1189        str_mapping[xmlrpc_datetime] = "datetime";
1190        str_mapping[xmlrpc_double]   = "double";
1191        str_mapping[xmlrpc_int]      = "int";
1192        str_mapping[xmlrpc_string]   = "string";
1193        str_mapping[xmlrpc_vector]   = "vector";
1194        str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none]   = "none";
1195        str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array]  = "array";
1196        str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed]  = "mixed";
1197        str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
1198        first = 0;
1199    }
1200    return (const char**)str_mapping;
1201}
1202/* }}} */
1203
1204/* map an xmlrpc type to a string */
1205const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) /* {{{ */
1206{
1207    const char** str_mapping = get_type_str_mapping();
1208
1209    if (vtype == xmlrpc_vector_none) {
1210        return str_mapping[type];
1211    } else {
1212        return str_mapping[XMLRPC_TYPE_COUNT + vtype];
1213    }
1214}
1215/* }}} */
1216
1217/* map a string to an xmlrpc type */
1218XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) /* {{{ */
1219{
1220    const char** str_mapping = get_type_str_mapping();
1221    int i;
1222
1223    if (str) {
1224        for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
1225            if (!strcmp(str_mapping[i], str)) {
1226                return (XMLRPC_VALUE_TYPE) i;
1227            }
1228        }
1229    }
1230    return xmlrpc_none;
1231}
1232/* }}} */
1233
1234/* map a string to an xmlrpc vector type */
1235XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) /* {{{ */
1236{
1237    const char** str_mapping = get_type_str_mapping();
1238    int i;
1239
1240    if (str) {
1241        for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
1242            if (!strcmp(str_mapping[i], str)) {
1243                return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
1244            }
1245        }
1246    }
1247    return xmlrpc_none;
1248}
1249/* }}} */
1250
1251/* set a given value to a particular type.
1252 * note: this only works on strings, and only for date and base64,
1253 *       which do not have native php types. black magic lies herein.
1254 */
1255int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype) /* {{{ */
1256{
1257    int bSuccess = FAILURE;
1258
1259    /* we only really care about strings because they can represent
1260     * base64 and datetime.  all other types have corresponding php types
1261     */
1262    if (Z_TYPE_P(value) == IS_STRING) {
1263        if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
1264            const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
1265            zval type;
1266
1267            ZVAL_STRING(&type, typestr);
1268
1269            if (newtype == xmlrpc_datetime) {
1270                XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, Z_STRVAL_P(value));
1271                if (v) {
1272                    time_t timestamp = (time_t) php_parse_date((char *)XMLRPC_GetValueDateTime_ISO8601(v), NULL);
1273                    if (timestamp != -1) {
1274                        zval ztimestamp;
1275
1276                        ZVAL_LONG(&ztimestamp, timestamp);
1277
1278                        convert_to_object(value);
1279                        if (zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1, &type)) {
1280                            bSuccess = zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR) - 1, &ztimestamp) != NULL;
1281                        }
1282                    } else {
1283                        zval_ptr_dtor(&type);
1284                    }
1285                    XMLRPC_CleanupValue(v);
1286                } else {
1287                    zval_ptr_dtor(&type);
1288                }
1289            } else {
1290                convert_to_object(value);
1291                bSuccess = zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1, &type) != NULL;
1292            }
1293        }
1294    }
1295
1296    return bSuccess;
1297}
1298/* }}} */
1299
1300/* return xmlrpc type of a php value */
1301XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval* newvalue) /* {{{ */
1302{
1303    XMLRPC_VALUE_TYPE type = xmlrpc_none;
1304
1305    if (value) {
1306        switch (Z_TYPE_P(value)) {
1307            case IS_NULL:
1308                type = xmlrpc_base64;
1309                break;
1310#ifndef BOOL_AS_LONG
1311
1312            /* Right thing to do, but it breaks some legacy code. */
1313            case IS_TRUE:
1314            case IS_FALSE:
1315                type = xmlrpc_boolean;
1316                break;
1317#else
1318            case IS_BOOL:
1319#endif
1320            case IS_LONG:
1321            case IS_RESOURCE:
1322                type = xmlrpc_int;
1323                break;
1324            case IS_DOUBLE:
1325                type = xmlrpc_double;
1326                break;
1327            case IS_CONSTANT:
1328                type = xmlrpc_string;
1329                break;
1330            case IS_STRING:
1331                type = xmlrpc_string;
1332                break;
1333            case IS_ARRAY:
1334                type = xmlrpc_vector;
1335                break;
1336            case IS_OBJECT:
1337                {
1338                    zval* attr;
1339                    type = xmlrpc_vector;
1340
1341                    if ((attr = zend_hash_str_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1)) != NULL) {
1342                        if (Z_TYPE_P(attr) == IS_STRING) {
1343                            type = xmlrpc_str_as_type(Z_STRVAL_P(attr));
1344                        }
1345                    }
1346                    break;
1347                }
1348        }
1349
1350        /* if requested, return an unmolested (magic removed) copy of the value */
1351        if (newvalue) {
1352            zval* val;
1353
1354            if ((type == xmlrpc_base64 && Z_TYPE_P(value) != IS_NULL) || type == xmlrpc_datetime) {
1355                if ((val = zend_hash_str_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR) - 1)) != NULL) {
1356                    ZVAL_COPY_VALUE(newvalue, val);
1357                }
1358            } else {
1359                ZVAL_COPY_VALUE(newvalue, value);
1360            }
1361        }
1362    }
1363
1364    return type;
1365}
1366/* }}} */
1367
1368/* {{{ proto bool xmlrpc_set_type(string value, string type)
1369   Sets xmlrpc type, base64 or datetime, for a PHP string value */
1370PHP_FUNCTION(xmlrpc_set_type)
1371{
1372    zval *arg;
1373    char *type;
1374    size_t type_len;
1375    XMLRPC_VALUE_TYPE vtype;
1376
1377    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/s", &arg, &type, &type_len) == FAILURE) {
1378        return;
1379    }
1380
1381    vtype = xmlrpc_str_as_type(type);
1382    if (vtype != xmlrpc_none) {
1383        if (set_zval_xmlrpc_type(arg, vtype) == SUCCESS) {
1384            RETURN_TRUE;
1385        }
1386    } else {
1387        zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type);
1388    }
1389    RETURN_FALSE;
1390}
1391/* }}} */
1392
1393/* {{{ proto string xmlrpc_get_type(mixed value)
1394   Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */
1395PHP_FUNCTION(xmlrpc_get_type)
1396{
1397    zval *arg;
1398    XMLRPC_VALUE_TYPE type;
1399    XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
1400
1401    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1402        return;
1403    }
1404
1405    type = get_zval_xmlrpc_type(arg, 0);
1406    if (type == xmlrpc_vector) {
1407        vtype = determine_vector_type((Z_TYPE_P(arg) == IS_OBJECT) ? Z_OBJPROP_P(arg) : Z_ARRVAL_P(arg));
1408    }
1409
1410    RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype));
1411}
1412/* }}} */
1413
1414/* {{{ proto bool xmlrpc_is_fault(array)
1415   Determines if an array value represents an XMLRPC fault. */
1416PHP_FUNCTION(xmlrpc_is_fault)
1417{
1418    zval *arg;
1419
1420    if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &arg) == FAILURE) {
1421        return;
1422    }
1423
1424    /* The "correct" way to do this would be to call the xmlrpc
1425     * library XMLRPC_ValueIsFault() func.  However, doing that
1426     * would require us to create an xmlrpc value from the php
1427     * array, which is rather expensive, especially if it was
1428     * a big array.  Thus, we resort to this not so clever hackery.
1429     */
1430    if (zend_hash_str_exists(Z_ARRVAL_P(arg), FAULT_CODE, FAULT_CODE_LEN) &&
1431            zend_hash_str_exists(Z_ARRVAL_P(arg), FAULT_STRING, FAULT_STRING_LEN)) {
1432        RETURN_TRUE;
1433    }
1434
1435    RETURN_FALSE;
1436}
1437/* }}} */
1438
1439/*
1440 * Local variables:
1441 * tab-width: 4
1442 * c-basic-offset: 4
1443 * End:
1444 */
1445
1446