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-2015 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        RETURN_RES(zend_register_resource(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    if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(arg1), "xmlrpc server", le_xmlrpc_server)) == NULL) {
848        RETURN_FALSE;
849    }
850
851    bSuccess = zend_list_close(Z_RES_P(arg1));
852    /* called by hashtable destructor
853     * destroy_server_data(server);
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    if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
968        RETURN_FALSE;
969    }
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(), "rz", &handle, &method_name) == FAILURE) {
996        return;
997    }
998
999    if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
1000        RETURN_FALSE;
1001    }
1002
1003    if (Z_REFCOUNTED_P(method_name)) {
1004        Z_ADDREF_P(method_name);
1005    }
1006    /* register our php method */
1007    add_zval(&server->introspection_map, NULL, method_name);
1008
1009    RETURN_TRUE;
1010}
1011/* }}} */
1012
1013/* this function is itchin for a re-write */
1014
1015/* {{{ proto mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])
1016   Parses XML requests and call methods */
1017PHP_FUNCTION(xmlrpc_server_call_method)
1018{
1019    XMLRPC_REQUEST xRequest;
1020    xmlrpc_callback_data data;
1021    STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
1022    xmlrpc_server_data* server;
1023    zval *caller_params, *handle, *output_opts = NULL;
1024    char *rawxml;
1025    size_t rawxml_len;
1026    php_output_options out;
1027    int argc = ZEND_NUM_ARGS();
1028
1029    if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsz|a", &handle, &rawxml, &rawxml_len, &caller_params, &output_opts) != SUCCESS) {
1030        return;
1031    }
1032    /* user output options */
1033    if (argc == 3) {
1034        set_output_options(&out, NULL);
1035    } else {
1036        set_output_options(&out, output_opts);
1037    }
1038
1039    if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
1040        RETURN_FALSE;
1041    }
1042
1043    /* HACK: use output encoding for now */
1044    input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
1045
1046    /* generate an XMLRPC_REQUEST from the raw xml input */
1047    xRequest = XMLRPC_REQUEST_FromXML(rawxml, rawxml_len, &input_opts);
1048
1049    if (xRequest) {
1050        const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
1051        XMLRPC_VALUE xAnswer = NULL;
1052        ZVAL_NULL(&data.xmlrpc_method); /* init. very important.  spent a frustrating day finding this out. */
1053        ZVAL_NULL(&data.return_data);
1054        ZVAL_NULL(&data.return_data);  /* in case value is never init'd, we don't dtor to think it is a string or something */
1055        ZVAL_NULL(&data.xmlrpc_method);
1056
1057        /* setup some data to pass to the callback function */
1058        ZVAL_COPY_VALUE(&data.caller_params, caller_params);
1059        data.php_executed = 0;
1060        data.server = server;
1061
1062        /* We could just call the php method directly ourselves at this point, but we do this
1063         * with a C callback in case the xmlrpc library ever implements some cool usage stats,
1064         * or somesuch.
1065         */
1066        xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
1067        if (xAnswer && out.b_php_out) {
1068            XMLRPC_to_PHP(xAnswer, &data.return_data);
1069        } else if (data.php_executed && !out.b_php_out && !xAnswer) {
1070            xAnswer = PHP_to_XMLRPC(&data.return_data);
1071        }
1072
1073        /* should we return data as xml? */
1074        if (!out.b_php_out) {
1075            XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
1076            if (xResponse) {
1077                char *outBuf = 0;
1078                int buf_len = 0;
1079
1080                /* automagically determine output serialization type from request type */
1081                if (out.b_auto_version) {
1082                    XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
1083                    if (opts) {
1084                        out.xmlrpc_out.version = opts->version;
1085                    }
1086                }
1087                /* set some required request hoojum */
1088                XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
1089                XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
1090                XMLRPC_RequestSetData(xResponse, xAnswer);
1091                XMLRPC_RequestSetMethodName(xResponse, methodname);
1092
1093                /* generate xml */
1094                outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
1095                if (outBuf) {
1096                    RETVAL_STRINGL(outBuf, buf_len);
1097                    free(outBuf);
1098                }
1099                /* cleanup after ourselves.  what a sty! */
1100                XMLRPC_RequestFree(xResponse, 0);
1101            }
1102        } else { /* or as native php types? */
1103            ZVAL_COPY(return_value, &data.return_data);
1104        }
1105
1106        /* cleanup after ourselves.  what a sty! */
1107        zval_ptr_dtor(&data.xmlrpc_method);
1108        zval_ptr_dtor(&data.return_data);
1109
1110        if (xAnswer) {
1111            XMLRPC_CleanupValue(xAnswer);
1112        }
1113
1114        XMLRPC_RequestFree(xRequest, 1);
1115    }
1116}
1117/* }}} */
1118
1119/* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
1120   Adds introspection documentation  */
1121PHP_FUNCTION(xmlrpc_server_add_introspection_data)
1122{
1123    zval *handle, *desc;
1124    xmlrpc_server_data* server;
1125    XMLRPC_VALUE xDesc;
1126
1127    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra", &handle, &desc) == FAILURE) {
1128        return;
1129    }
1130
1131    if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
1132        RETURN_FALSE;
1133    }
1134
1135    xDesc = PHP_to_XMLRPC(desc);
1136    if (xDesc) {
1137        int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
1138        XMLRPC_CleanupValue(xDesc);
1139        RETURN_LONG(retval);
1140    }
1141    RETURN_LONG(0);
1142}
1143/* }}} */
1144
1145/* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
1146   Decodes XML into a list of method descriptions */
1147PHP_FUNCTION(xmlrpc_parse_method_descriptions)
1148{
1149    char *arg1;
1150    size_t arg1_len;
1151
1152    if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg1, &arg1_len) == FAILURE) {
1153        return;
1154    }
1155
1156    if (USED_RET()) {
1157        STRUCT_XMLRPC_ERROR err = {0};
1158        XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1, &err);
1159        if (xVal) {
1160            XMLRPC_to_PHP(xVal, return_value);
1161            /* dust, sweep, and mop */
1162            XMLRPC_CleanupValue(xVal);
1163        } else {
1164            /* could not create description */
1165            if (err.xml_elem_error.parser_code) {
1166                php_error_docref(NULL, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data",
1167                        err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
1168            } else {
1169                php_error_docref(NULL, E_WARNING, "Invalid xml structure. Unable to create introspection data");
1170            }
1171
1172            php_error_docref(NULL, E_WARNING, "xml parse error. no method description created");
1173        }
1174    }
1175}
1176/* }}} */
1177
1178/************
1179* type data *
1180************/
1181
1182#define XMLRPC_TYPE_COUNT 9
1183#define XMLRPC_VECTOR_TYPE_COUNT 4
1184#define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
1185
1186/* return a string matching a given xmlrpc type */
1187static const char** get_type_str_mapping(void) /* {{{ */
1188{
1189    static const char* str_mapping[TYPE_STR_MAP_SIZE];
1190    static int first = 1;
1191    if (first) {
1192        /* warning. do not add/delete without changing size define */
1193        str_mapping[xmlrpc_none]     = "none";
1194        str_mapping[xmlrpc_empty]    = "empty";
1195        str_mapping[xmlrpc_base64]   = "base64";
1196        str_mapping[xmlrpc_boolean]  = "boolean";
1197        str_mapping[xmlrpc_datetime] = "datetime";
1198        str_mapping[xmlrpc_double]   = "double";
1199        str_mapping[xmlrpc_int]      = "int";
1200        str_mapping[xmlrpc_string]   = "string";
1201        str_mapping[xmlrpc_vector]   = "vector";
1202        str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none]   = "none";
1203        str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array]  = "array";
1204        str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed]  = "mixed";
1205        str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
1206        first = 0;
1207    }
1208    return (const char**)str_mapping;
1209}
1210/* }}} */
1211
1212/* map an xmlrpc type to a string */
1213const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) /* {{{ */
1214{
1215    const char** str_mapping = get_type_str_mapping();
1216
1217    if (vtype == xmlrpc_vector_none) {
1218        return str_mapping[type];
1219    } else {
1220        return str_mapping[XMLRPC_TYPE_COUNT + vtype];
1221    }
1222}
1223/* }}} */
1224
1225/* map a string to an xmlrpc type */
1226XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) /* {{{ */
1227{
1228    const char** str_mapping = get_type_str_mapping();
1229    int i;
1230
1231    if (str) {
1232        for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
1233            if (!strcmp(str_mapping[i], str)) {
1234                return (XMLRPC_VALUE_TYPE) i;
1235            }
1236        }
1237    }
1238    return xmlrpc_none;
1239}
1240/* }}} */
1241
1242/* map a string to an xmlrpc vector type */
1243XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) /* {{{ */
1244{
1245    const char** str_mapping = get_type_str_mapping();
1246    int i;
1247
1248    if (str) {
1249        for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
1250            if (!strcmp(str_mapping[i], str)) {
1251                return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
1252            }
1253        }
1254    }
1255    return xmlrpc_none;
1256}
1257/* }}} */
1258
1259/* set a given value to a particular type.
1260 * note: this only works on strings, and only for date and base64,
1261 *       which do not have native php types. black magic lies herein.
1262 */
1263int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype) /* {{{ */
1264{
1265    int bSuccess = FAILURE;
1266
1267    /* we only really care about strings because they can represent
1268     * base64 and datetime.  all other types have corresponding php types
1269     */
1270    if (Z_TYPE_P(value) == IS_STRING) {
1271        if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
1272            const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
1273            zval type;
1274
1275            ZVAL_STRING(&type, typestr);
1276
1277            if (newtype == xmlrpc_datetime) {
1278                XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, Z_STRVAL_P(value));
1279                if (v) {
1280                    time_t timestamp = (time_t) php_parse_date((char *)XMLRPC_GetValueDateTime_ISO8601(v), NULL);
1281                    if (timestamp != -1) {
1282                        zval ztimestamp;
1283
1284                        ZVAL_LONG(&ztimestamp, timestamp);
1285
1286                        convert_to_object(value);
1287                        if (zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1, &type)) {
1288                            bSuccess = zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR) - 1, &ztimestamp) != NULL;
1289                        }
1290                    } else {
1291                        zval_ptr_dtor(&type);
1292                    }
1293                    XMLRPC_CleanupValue(v);
1294                } else {
1295                    zval_ptr_dtor(&type);
1296                }
1297            } else {
1298                convert_to_object(value);
1299                bSuccess = zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1, &type) != NULL;
1300            }
1301        }
1302    }
1303
1304    return bSuccess;
1305}
1306/* }}} */
1307
1308/* return xmlrpc type of a php value */
1309XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval* newvalue) /* {{{ */
1310{
1311    XMLRPC_VALUE_TYPE type = xmlrpc_none;
1312
1313    if (value) {
1314        switch (Z_TYPE_P(value)) {
1315            case IS_NULL:
1316                type = xmlrpc_base64;
1317                break;
1318#ifndef BOOL_AS_LONG
1319
1320            /* Right thing to do, but it breaks some legacy code. */
1321            case IS_TRUE:
1322            case IS_FALSE:
1323                type = xmlrpc_boolean;
1324                break;
1325#else
1326            case IS_BOOL:
1327#endif
1328            case IS_LONG:
1329            case IS_RESOURCE:
1330                type = xmlrpc_int;
1331                break;
1332            case IS_DOUBLE:
1333                type = xmlrpc_double;
1334                break;
1335            case IS_CONSTANT:
1336                type = xmlrpc_string;
1337                break;
1338            case IS_STRING:
1339                type = xmlrpc_string;
1340                break;
1341            case IS_ARRAY:
1342                type = xmlrpc_vector;
1343                break;
1344            case IS_OBJECT:
1345                {
1346                    zval* attr;
1347                    type = xmlrpc_vector;
1348
1349                    if ((attr = zend_hash_str_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1)) != NULL) {
1350                        if (Z_TYPE_P(attr) == IS_STRING) {
1351                            type = xmlrpc_str_as_type(Z_STRVAL_P(attr));
1352                        }
1353                    }
1354                    break;
1355                }
1356        }
1357
1358        /* if requested, return an unmolested (magic removed) copy of the value */
1359        if (newvalue) {
1360            zval* val;
1361
1362            if ((type == xmlrpc_base64 && Z_TYPE_P(value) != IS_NULL) || type == xmlrpc_datetime) {
1363                if ((val = zend_hash_str_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR) - 1)) != NULL) {
1364                    ZVAL_COPY_VALUE(newvalue, val);
1365                }
1366            } else {
1367                ZVAL_COPY_VALUE(newvalue, value);
1368            }
1369        }
1370    }
1371
1372    return type;
1373}
1374/* }}} */
1375
1376/* {{{ proto bool xmlrpc_set_type(string value, string type)
1377   Sets xmlrpc type, base64 or datetime, for a PHP string value */
1378PHP_FUNCTION(xmlrpc_set_type)
1379{
1380    zval *arg;
1381    char *type;
1382    size_t type_len;
1383    XMLRPC_VALUE_TYPE vtype;
1384
1385    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/s", &arg, &type, &type_len) == FAILURE) {
1386        return;
1387    }
1388
1389    vtype = xmlrpc_str_as_type(type);
1390    if (vtype != xmlrpc_none) {
1391        if (set_zval_xmlrpc_type(arg, vtype) == SUCCESS) {
1392            RETURN_TRUE;
1393        }
1394    } else {
1395        zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type);
1396    }
1397    RETURN_FALSE;
1398}
1399/* }}} */
1400
1401/* {{{ proto string xmlrpc_get_type(mixed value)
1402   Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */
1403PHP_FUNCTION(xmlrpc_get_type)
1404{
1405    zval *arg;
1406    XMLRPC_VALUE_TYPE type;
1407    XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
1408
1409    if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1410        return;
1411    }
1412
1413    type = get_zval_xmlrpc_type(arg, 0);
1414    if (type == xmlrpc_vector) {
1415        vtype = determine_vector_type((Z_TYPE_P(arg) == IS_OBJECT) ? Z_OBJPROP_P(arg) : Z_ARRVAL_P(arg));
1416    }
1417
1418    RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype));
1419}
1420/* }}} */
1421
1422/* {{{ proto bool xmlrpc_is_fault(array)
1423   Determines if an array value represents an XMLRPC fault. */
1424PHP_FUNCTION(xmlrpc_is_fault)
1425{
1426    zval *arg;
1427
1428    if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &arg) == FAILURE) {
1429        return;
1430    }
1431
1432    /* The "correct" way to do this would be to call the xmlrpc
1433     * library XMLRPC_ValueIsFault() func.  However, doing that
1434     * would require us to create an xmlrpc value from the php
1435     * array, which is rather expensive, especially if it was
1436     * a big array.  Thus, we resort to this not so clever hackery.
1437     */
1438    if (zend_hash_str_exists(Z_ARRVAL_P(arg), FAULT_CODE, FAULT_CODE_LEN) &&
1439            zend_hash_str_exists(Z_ARRVAL_P(arg), FAULT_STRING, FAULT_STRING_LEN)) {
1440        RETURN_TRUE;
1441    }
1442
1443    RETURN_FALSE;
1444}
1445/* }}} */
1446
1447/*
1448 * Local variables:
1449 * tab-width: 4
1450 * c-basic-offset: 4
1451 * End:
1452 */
1453
1454