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