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