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