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