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