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