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