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