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