1 /*
2 This file is part of, or distributed with, libXMLRPC - a C library for
3 xml-encoded function calls.
4
5 Author: Dan Libby (dan@libby.com)
6 Epinions.com may be contacted at feedback@epinions-inc.com
7 */
8
9 /*
10 Copyright 2001 Epinions, Inc.
11
12 Subject to the following 3 conditions, Epinions, Inc. permits you, free
13 of charge, to (a) use, copy, distribute, modify, perform and display this
14 software and associated documentation files (the "Software"), and (b)
15 permit others to whom the Software is furnished to do so as well.
16
17 1) The above copyright notice and this permission notice shall be included
18 without modification in all copies or substantial portions of the
19 Software.
20
21 2) THE SOFTWARE IS PROVIDED "AS IS", WITHOUT ANY WARRANTY OR CONDITION OF
22 ANY KIND, EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY
23 IMPLIED WARRANTIES OF ACCURACY, MERCHANTABILITY, FITNESS FOR A PARTICULAR
24 PURPOSE OR NONINFRINGEMENT.
25
26 3) IN NO EVENT SHALL EPINIONS, INC. BE LIABLE FOR ANY DIRECT, INDIRECT,
27 SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OR LOST PROFITS ARISING OUT
28 OF OR IN CONNECTION WITH THE SOFTWARE (HOWEVER ARISING, INCLUDING
29 NEGLIGENCE), EVEN IF EPINIONS, INC. IS AWARE OF THE POSSIBILITY OF SUCH
30 DAMAGES.
31
32 */
33
34 /* auto-generated portions of this file are also subject to the php license */
35
36 /*
37 +----------------------------------------------------------------------+
38 | PHP Version 7 |
39 +----------------------------------------------------------------------+
40 | Copyright (c) 1997-2018 The PHP Group |
41 +----------------------------------------------------------------------+
42 | This source file is subject to version 3.01 of the PHP license, |
43 | that is bundled with this package in the file LICENSE, and is |
44 | available through the world-wide-web at the following url: |
45 | http://www.php.net/license/3_01.txt |
46 | If you did not receive a copy of the PHP license and are unable to |
47 | obtain it through the world-wide-web, please send a note to |
48 | license@php.net so we can mail you a copy immediately. |
49 +----------------------------------------------------------------------+
50 | Author: Dan Libby |
51 +----------------------------------------------------------------------+
52 */
53
54 /**********************************************************************
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 convert_to_string(&val);
526 xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL(val));
527 break;
528 case xmlrpc_boolean:
529 convert_to_boolean(&val);
530 xReturn = XMLRPC_CreateValueBoolean(key, Z_TYPE(val) == IS_TRUE);
531 break;
532 case xmlrpc_int:
533 ZVAL_LONG(&val, zval_get_long(&val));
534 xReturn = XMLRPC_CreateValueInt(key, Z_LVAL(val));
535 break;
536 case xmlrpc_double:
537 convert_to_double(&val);
538 xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL(val));
539 break;
540 case xmlrpc_string:
541 convert_to_string(&val);
542 xReturn = XMLRPC_CreateValueString(key, Z_STRVAL(val), Z_STRLEN(val));
543 break;
544 case xmlrpc_vector:
545 {
546 zend_ulong num_index;
547 zval* pIter;
548 zend_string* my_key;
549 HashTable *ht = NULL;
550 zval val_arr;
551 XMLRPC_VECTOR_TYPE vtype;
552
553 ht = HASH_OF(&val);
554 if (ht && !(GC_FLAGS(ht) & GC_IMMUTABLE)) {
555 if (GC_IS_RECURSIVE(ht)) {
556 zend_throw_error(NULL, "XML-RPC doesn't support circular references");
557 return NULL;
558 }
559 GC_PROTECT_RECURSION(ht);
560 }
561
562 ZVAL_COPY(&val_arr, &val);
563 convert_to_array(&val_arr);
564
565 vtype = determine_vector_type(Z_ARRVAL(val_arr));
566 xReturn = XMLRPC_CreateVector(key, vtype);
567
568 ZEND_HASH_FOREACH_KEY_VAL(Z_ARRVAL(val_arr), num_index, my_key, pIter) {
569 ZVAL_DEREF(pIter);
570 if (my_key == NULL) {
571 char *num_str = NULL;
572
573 if (vtype != xmlrpc_vector_array) {
574 spprintf(&num_str, 0, ZEND_LONG_FMT, num_index);
575 }
576
577 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(num_str, pIter, depth++));
578 if (num_str) {
579 efree(num_str);
580 }
581 } else {
582 XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(ZSTR_VAL(my_key), pIter, depth++));
583 }
584 } ZEND_HASH_FOREACH_END();
585 if (ht && !(GC_FLAGS(ht) & GC_IMMUTABLE)) {
586 GC_UNPROTECT_RECURSION(ht);
587 }
588 zval_ptr_dtor(&val_arr);
589 }
590 break;
591 default:
592 break;
593 }
594 }
595 }
596 return xReturn;
597 }
598
PHP_to_XMLRPC(zval * root_val)599 static XMLRPC_VALUE PHP_to_XMLRPC(zval* root_val)
600 {
601 return PHP_to_XMLRPC_worker(NULL, root_val, 0);
602 }
603
604 /* recursively convert xmlrpc values into php values */
XMLRPC_to_PHP(XMLRPC_VALUE el,zval * elem)605 static void XMLRPC_to_PHP(XMLRPC_VALUE el, zval *elem)
606 {
607 const char* pStr;
608
609 if (el) {
610 XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
611
612 switch (type) {
613 case xmlrpc_empty:
614 ZVAL_NULL(elem);
615 break;
616 case xmlrpc_string:
617 pStr = XMLRPC_GetValueString(el);
618 if (pStr) {
619 ZVAL_STRINGL(elem, pStr, XMLRPC_GetValueStringLen(el));
620 }
621 break;
622 case xmlrpc_int:
623 ZVAL_LONG(elem, XMLRPC_GetValueInt(el));
624 break;
625 case xmlrpc_boolean:
626 ZVAL_BOOL(elem, XMLRPC_GetValueBoolean(el));
627 break;
628 case xmlrpc_double:
629 ZVAL_DOUBLE(elem, XMLRPC_GetValueDouble(el));
630 break;
631 case xmlrpc_datetime:
632 ZVAL_STRINGL(elem, XMLRPC_GetValueDateTime_ISO8601(el), XMLRPC_GetValueStringLen(el));
633 break;
634 case xmlrpc_base64:
635 pStr = XMLRPC_GetValueBase64(el);
636 if (pStr) {
637 ZVAL_STRINGL(elem, pStr, XMLRPC_GetValueStringLen(el));
638 }
639 break;
640 case xmlrpc_vector:
641 array_init(elem);
642 {
643 XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
644
645 while( xIter ) {
646 zval val;
647 ZVAL_UNDEF(&val);
648 XMLRPC_to_PHP(xIter, &val);
649 if (!Z_ISUNDEF(val)) {
650 add_zval(elem, XMLRPC_GetValueID(xIter), &val);
651 }
652 xIter = XMLRPC_VectorNext(el);
653 }
654 }
655 break;
656 default:
657 break;
658 }
659 set_zval_xmlrpc_type(elem, type);
660 }
661 }
662
663 /* {{{ proto string xmlrpc_encode_request(string method, mixed params [, array output_options])
664 Generates XML for a method request */
PHP_FUNCTION(xmlrpc_encode_request)665 PHP_FUNCTION(xmlrpc_encode_request)
666 {
667 XMLRPC_REQUEST xRequest = NULL;
668 char *outBuf;
669 zval *vals, *out_opts = NULL;
670 char *method = NULL;
671 size_t method_len;
672 php_output_options out;
673
674 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s!z|a", &method, &method_len, &vals, &out_opts) == FAILURE) {
675 return;
676 }
677
678 set_output_options(&out, out_opts ? out_opts : 0);
679
680 if (USED_RET()) {
681 xRequest = XMLRPC_RequestNew();
682
683 if (xRequest) {
684 XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
685 if (method == NULL) {
686 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
687 } else {
688 XMLRPC_RequestSetMethodName(xRequest, method);
689 XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
690 }
691 if (Z_TYPE_P(vals) != IS_NULL) {
692 XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(vals));
693 }
694
695 outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
696 if (outBuf) {
697 RETVAL_STRING(outBuf);
698 #ifdef HAVE_XMLRPC_BUNDLED
699 efree(outBuf);
700 #else
701 free(outBuf);
702 #endif
703 }
704 XMLRPC_RequestFree(xRequest, 1);
705 }
706 }
707
708 if (strcmp(out.xmlrpc_out.xml_elem_opts.encoding, ENCODING_DEFAULT) != 0) {
709 efree((char *)out.xmlrpc_out.xml_elem_opts.encoding);
710 }
711 }
712 /* }}} */
713
714 /* {{{ proto string xmlrpc_encode(mixed value)
715 Generates XML for a PHP value */
PHP_FUNCTION(xmlrpc_encode)716 PHP_FUNCTION(xmlrpc_encode)
717 {
718 XMLRPC_VALUE xOut = NULL;
719 zval *arg1;
720 char *outBuf;
721
722 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg1) == FAILURE) {
723 return;
724 }
725
726 if (USED_RET()) {
727 /* convert native php type to xmlrpc type */
728 xOut = PHP_to_XMLRPC(arg1);
729
730 /* generate raw xml from xmlrpc data */
731 outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
732
733 if (xOut) {
734 if (outBuf) {
735 RETVAL_STRING(outBuf);
736 #ifdef HAVE_XMLRPC_BUNDLED
737 efree(outBuf);
738 #else
739 free(outBuf);
740 #endif
741 }
742 /* cleanup */
743 XMLRPC_CleanupValue(xOut);
744 }
745 }
746 }
747 /* }}} */
748
decode_request_worker(char * xml_in,int xml_in_len,char * encoding_in,zval * method_name_out,zval * retval)749 void decode_request_worker(char *xml_in, int xml_in_len, char *encoding_in, zval* method_name_out, zval *retval) /* {{{ */
750 {
751 XMLRPC_REQUEST response;
752 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}};
753 const char *method_name;
754 opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in) : ENCODING_DEFAULT;
755
756 /* generate XMLRPC_REQUEST from raw xml */
757 response = XMLRPC_REQUEST_FromXML(xml_in, xml_in_len, &opts);
758 if (response) {
759 ZVAL_NULL(retval);
760 /* convert xmlrpc data to native php types */
761 XMLRPC_to_PHP(XMLRPC_RequestGetData(response), retval);
762
763 if (XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
764 if (method_name_out) {
765 method_name = XMLRPC_RequestGetMethodName(response);
766 if (method_name) {
767 zval_ptr_dtor(method_name_out);
768 ZVAL_STRING(method_name_out, method_name);
769 } else {
770 zval_ptr_dtor(retval);
771 ZVAL_NULL(retval);
772 }
773 }
774 }
775
776 /* dust, sweep, and mop */
777 XMLRPC_RequestFree(response, 1);
778 }
779 }
780 /* }}} */
781
782 /* {{{ proto array xmlrpc_decode_request(string xml, string& method [, string encoding])
783 Decodes XML into native PHP types */
PHP_FUNCTION(xmlrpc_decode_request)784 PHP_FUNCTION(xmlrpc_decode_request)
785 {
786 char *xml, *encoding = NULL;
787 zval *method;
788 size_t xml_len, encoding_len = 0;
789
790 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/|s", &xml, &xml_len, &method, &encoding, &encoding_len) == FAILURE) {
791 return;
792 }
793
794 if (USED_RET()) {
795 decode_request_worker(xml, xml_len, encoding_len ? encoding : NULL, method, return_value);
796 }
797 }
798 /* }}} */
799
800 /* {{{ proto array xmlrpc_decode(string xml [, string encoding])
801 Decodes XML into native PHP types */
PHP_FUNCTION(xmlrpc_decode)802 PHP_FUNCTION(xmlrpc_decode)
803 {
804 char *arg1, *arg2 = NULL;
805 size_t arg1_len, arg2_len = 0;
806
807 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &arg1, &arg1_len, &arg2, &arg2_len) == FAILURE) {
808 return;
809 }
810
811 if (USED_RET()) {
812 decode_request_worker(arg1, arg1_len, arg2_len ? arg2 : NULL, NULL, return_value);
813 }
814 }
815 /* }}} */
816
817 /*************************
818 * server related methods *
819 *************************/
820
821 /* {{{ proto resource xmlrpc_server_create(void)
822 Creates an xmlrpc server */
PHP_FUNCTION(xmlrpc_server_create)823 PHP_FUNCTION(xmlrpc_server_create)
824 {
825 if (zend_parse_parameters_none() == FAILURE) {
826 return;
827 }
828
829 if (USED_RET()) {
830 xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
831
832 /* allocate server data. free'd in destroy_server_data() */
833 array_init(&server->method_map);
834 array_init(&server->introspection_map);
835 server->server_ptr = XMLRPC_ServerCreate();
836
837 XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
838
839 /* store for later use */
840 RETURN_RES(zend_register_resource(server, le_xmlrpc_server));
841 }
842 }
843 /* }}} */
844
845 /* {{{ proto int xmlrpc_server_destroy(resource server)
846 Destroys server resources */
PHP_FUNCTION(xmlrpc_server_destroy)847 PHP_FUNCTION(xmlrpc_server_destroy)
848 {
849 zval *arg1;
850 int bSuccess = FAILURE;
851 xmlrpc_server_data *server;
852
853 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg1) == FAILURE) {
854 return;
855 }
856
857 if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(arg1), "xmlrpc server", le_xmlrpc_server)) == NULL) {
858 RETURN_FALSE;
859 }
860
861 bSuccess = zend_list_close(Z_RES_P(arg1));
862 /* called by hashtable destructor
863 * destroy_server_data(server);
864 */
865 RETURN_BOOL(bSuccess == SUCCESS);
866 }
867 /* }}} */
868
869 /* called by xmlrpc C engine as method handler for all registered methods.
870 * it then calls the corresponding PHP function to handle the method.
871 */
php_xmlrpc_callback(XMLRPC_SERVER server,XMLRPC_REQUEST xRequest,void * data)872 static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data) /* {{{ */
873 {
874 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
875 zval* php_function;
876 zval xmlrpc_params;
877 zval callback_params[3];
878
879 zval_ptr_dtor(&pData->xmlrpc_method);
880 zval_ptr_dtor(&pData->return_data);
881
882 /* convert xmlrpc to native php types */
883 ZVAL_STRING(&pData->xmlrpc_method, XMLRPC_RequestGetMethodName(xRequest));
884 XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest), &xmlrpc_params);
885
886 /* check if the called method has been previous registered */
887 if ((php_function = zend_hash_find(Z_ARRVAL(pData->server->method_map), Z_STR(pData->xmlrpc_method))) != NULL) {
888 ZVAL_COPY_VALUE(&pData->php_function, php_function);
889 }
890
891 /* setup data hoojum */
892 ZVAL_COPY_VALUE(&callback_params[0], &pData->xmlrpc_method);
893 ZVAL_COPY_VALUE(&callback_params[1], &xmlrpc_params);
894 ZVAL_COPY_VALUE(&callback_params[2], &pData->caller_params);
895
896 /* Use same C function for all methods */
897
898 /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
899 call_user_function(CG(function_table), NULL, &pData->php_function, &pData->return_data, 3, callback_params);
900
901 pData->php_executed = 1;
902
903 zval_ptr_dtor(&xmlrpc_params);
904
905 return PHP_to_XMLRPC(&pData->return_data);
906 }
907 /* }}} */
908
909 /* called by the C server when it first receives an introspection request. We pass this on to
910 * our PHP listeners, if any
911 */
php_xmlrpc_introspection_callback(XMLRPC_SERVER server,void * data)912 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) /* {{{ */
913 {
914 zval retval, *php_function;
915 zval callback_params[1];
916 zend_string *php_function_name;
917 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
918
919 /* setup data hoojum */
920 ZVAL_COPY_VALUE(&callback_params[0], &pData->caller_params);
921
922 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(pData->server->introspection_map), php_function) {
923 if (zend_is_callable(php_function, 0, &php_function_name)) {
924 /* php func prototype: function string user_func($user_params) */
925 if (call_user_function(CG(function_table), NULL, php_function, &retval, 1, callback_params) == SUCCESS) {
926 XMLRPC_VALUE xData;
927 STRUCT_XMLRPC_ERROR err = {0};
928
929 /* return value should be a string */
930 convert_to_string(&retval);
931
932 xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL(retval), &err);
933
934 if (xData) {
935 if (!XMLRPC_ServerAddIntrospectionData(server, xData)) {
936 php_error_docref(NULL, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", ZSTR_VAL(php_function_name));
937 }
938 XMLRPC_CleanupValue(xData);
939 } else {
940 /* could not create description */
941 if (err.xml_elem_error.parser_code) {
942 php_error_docref(NULL, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to add introspection data returned from %s()",
943 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, ZSTR_VAL(php_function_name));
944 } else {
945 php_error_docref(NULL, E_WARNING, "Unable to add introspection data returned from %s()", ZSTR_VAL(php_function_name));
946 }
947 }
948 zval_ptr_dtor(&retval);
949 } else {
950 /* user func failed */
951 php_error_docref(NULL, E_WARNING, "Error calling user introspection callback: %s()", ZSTR_VAL(php_function_name));
952 }
953 } else {
954 php_error_docref(NULL, E_WARNING, "Invalid callback '%s' passed", ZSTR_VAL(php_function_name));
955 }
956 zend_string_release_ex(php_function_name, 0);
957 } ZEND_HASH_FOREACH_END();
958
959 /* so we don't call the same callbacks ever again */
960 zend_hash_clean(Z_ARRVAL(pData->server->introspection_map));
961 }
962 /* }}} */
963
964 /* {{{ proto bool xmlrpc_server_register_method(resource server, string method_name, string function)
965 Register a PHP function to handle method matching method_name */
PHP_FUNCTION(xmlrpc_server_register_method)966 PHP_FUNCTION(xmlrpc_server_register_method)
967 {
968 char *method_key;
969 size_t method_key_len;
970 zval *handle, *method_name;
971 xmlrpc_server_data* server;
972
973 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsz", &handle, &method_key, &method_key_len, &method_name) == FAILURE) {
974 return;
975 }
976
977 if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
978 RETURN_FALSE;
979 }
980
981 /* register with C engine. every method just calls our standard callback,
982 * and it then dispatches to php as necessary
983 */
984 if (XMLRPC_ServerRegisterMethod(server->server_ptr, method_key, php_xmlrpc_callback)) {
985 /* save for later use */
986
987 Z_TRY_ADDREF_P(method_name);
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 Z_TRY_ADDREF_P(method_name);
1012 /* register our php method */
1013 add_zval(&server->introspection_map, NULL, method_name);
1014
1015 RETURN_TRUE;
1016 }
1017 /* }}} */
1018
1019 /* this function is itchin for a re-write */
1020
1021 /* {{{ proto mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])
1022 Parses XML requests and call methods */
PHP_FUNCTION(xmlrpc_server_call_method)1023 PHP_FUNCTION(xmlrpc_server_call_method)
1024 {
1025 XMLRPC_REQUEST xRequest;
1026 xmlrpc_callback_data data;
1027 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
1028 xmlrpc_server_data* server;
1029 zval *caller_params, *handle, *output_opts = NULL;
1030 char *rawxml;
1031 size_t rawxml_len;
1032 php_output_options out;
1033 int argc = ZEND_NUM_ARGS();
1034
1035 if (zend_parse_parameters(argc, "rsz|a", &handle, &rawxml, &rawxml_len, &caller_params, &output_opts) != SUCCESS) {
1036 return;
1037 }
1038 /* user output options */
1039 if (argc == 3) {
1040 set_output_options(&out, NULL);
1041 } else {
1042 set_output_options(&out, output_opts);
1043 }
1044
1045 if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
1046 RETURN_FALSE;
1047 }
1048
1049 /* HACK: use output encoding for now */
1050 input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
1051
1052 /* generate an XMLRPC_REQUEST from the raw xml input */
1053 xRequest = XMLRPC_REQUEST_FromXML(rawxml, rawxml_len, &input_opts);
1054
1055 if (xRequest) {
1056 const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
1057 XMLRPC_VALUE xAnswer = NULL;
1058 ZVAL_NULL(&data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */
1059 ZVAL_NULL(&data.return_data);
1060 ZVAL_NULL(&data.return_data); /* in case value is never init'd, we don't dtor to think it is a string or something */
1061 ZVAL_NULL(&data.xmlrpc_method);
1062
1063 /* setup some data to pass to the callback function */
1064 ZVAL_COPY_VALUE(&data.caller_params, caller_params);
1065 data.php_executed = 0;
1066 data.server = server;
1067
1068 /* We could just call the php method directly ourselves at this point, but we do this
1069 * with a C callback in case the xmlrpc library ever implements some cool usage stats,
1070 * or somesuch.
1071 */
1072 xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
1073 if (xAnswer && out.b_php_out) {
1074 XMLRPC_to_PHP(xAnswer, &data.return_data);
1075 } else if (data.php_executed && !out.b_php_out && !xAnswer) {
1076 xAnswer = PHP_to_XMLRPC(&data.return_data);
1077 }
1078
1079 /* should we return data as xml? */
1080 if (!out.b_php_out) {
1081 XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
1082 if (xResponse) {
1083 char *outBuf = 0;
1084 int buf_len = 0;
1085
1086 /* automagically determine output serialization type from request type */
1087 if (out.b_auto_version) {
1088 XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
1089 if (opts) {
1090 out.xmlrpc_out.version = opts->version;
1091 }
1092 }
1093 /* set some required request hoojum */
1094 XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
1095 XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
1096 XMLRPC_RequestSetData(xResponse, xAnswer);
1097 XMLRPC_RequestSetMethodName(xResponse, methodname);
1098
1099 /* generate xml */
1100 outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
1101 if (outBuf) {
1102 RETVAL_STRINGL(outBuf, buf_len);
1103 #ifdef HAVE_XMLRPC_BUNDLED
1104 efree(outBuf);
1105 #else
1106 free(outBuf);
1107 #endif
1108 }
1109 /* cleanup after ourselves. what a sty! */
1110 XMLRPC_RequestFree(xResponse, 0);
1111 }
1112 } else { /* or as native php types? */
1113 ZVAL_COPY(return_value, &data.return_data);
1114 }
1115
1116 /* cleanup after ourselves. what a sty! */
1117 zval_ptr_dtor(&data.xmlrpc_method);
1118 zval_ptr_dtor(&data.return_data);
1119
1120 if (xAnswer) {
1121 XMLRPC_CleanupValue(xAnswer);
1122 }
1123
1124 XMLRPC_RequestFree(xRequest, 1);
1125 }
1126 }
1127 /* }}} */
1128
1129 /* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
1130 Adds introspection documentation */
PHP_FUNCTION(xmlrpc_server_add_introspection_data)1131 PHP_FUNCTION(xmlrpc_server_add_introspection_data)
1132 {
1133 zval *handle, *desc;
1134 xmlrpc_server_data* server;
1135 XMLRPC_VALUE xDesc;
1136
1137 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra", &handle, &desc) == FAILURE) {
1138 return;
1139 }
1140
1141 if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
1142 RETURN_FALSE;
1143 }
1144
1145 xDesc = PHP_to_XMLRPC(desc);
1146 if (xDesc) {
1147 int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
1148 XMLRPC_CleanupValue(xDesc);
1149 RETURN_LONG(retval);
1150 }
1151 RETURN_LONG(0);
1152 }
1153 /* }}} */
1154
1155 /* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
1156 Decodes XML into a list of method descriptions */
PHP_FUNCTION(xmlrpc_parse_method_descriptions)1157 PHP_FUNCTION(xmlrpc_parse_method_descriptions)
1158 {
1159 char *arg1;
1160 size_t arg1_len;
1161
1162 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg1, &arg1_len) == FAILURE) {
1163 return;
1164 }
1165
1166 if (USED_RET()) {
1167 STRUCT_XMLRPC_ERROR err = {0};
1168 XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1, &err);
1169 if (xVal) {
1170 XMLRPC_to_PHP(xVal, return_value);
1171 /* dust, sweep, and mop */
1172 XMLRPC_CleanupValue(xVal);
1173 } else {
1174 /* could not create description */
1175 if (err.xml_elem_error.parser_code) {
1176 php_error_docref(NULL, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data",
1177 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
1178 } else {
1179 php_error_docref(NULL, E_WARNING, "Invalid xml structure. Unable to create introspection data");
1180 }
1181
1182 php_error_docref(NULL, E_WARNING, "xml parse error. no method description created");
1183 }
1184 }
1185 }
1186 /* }}} */
1187
1188 /************
1189 * type data *
1190 ************/
1191
1192 #define XMLRPC_TYPE_COUNT 9
1193 #define XMLRPC_VECTOR_TYPE_COUNT 4
1194 #define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
1195
1196 /* return a string matching a given xmlrpc type */
get_type_str_mapping(void)1197 static const char** get_type_str_mapping(void) /* {{{ */
1198 {
1199 static const char* str_mapping[TYPE_STR_MAP_SIZE];
1200 static int first = 1;
1201 if (first) {
1202 /* warning. do not add/delete without changing size define */
1203 str_mapping[xmlrpc_none] = "none";
1204 str_mapping[xmlrpc_empty] = "empty";
1205 str_mapping[xmlrpc_base64] = "base64";
1206 str_mapping[xmlrpc_boolean] = "boolean";
1207 str_mapping[xmlrpc_datetime] = "datetime";
1208 str_mapping[xmlrpc_double] = "double";
1209 str_mapping[xmlrpc_int] = "int";
1210 str_mapping[xmlrpc_string] = "string";
1211 str_mapping[xmlrpc_vector] = "vector";
1212 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none";
1213 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array";
1214 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed";
1215 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
1216 first = 0;
1217 }
1218 return (const char**)str_mapping;
1219 }
1220 /* }}} */
1221
1222 /* map an xmlrpc type to a string */
xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type,XMLRPC_VECTOR_TYPE vtype)1223 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) /* {{{ */
1224 {
1225 const char** str_mapping = get_type_str_mapping();
1226
1227 if (vtype == xmlrpc_vector_none) {
1228 return str_mapping[type];
1229 } else {
1230 return str_mapping[XMLRPC_TYPE_COUNT + vtype];
1231 }
1232 }
1233 /* }}} */
1234
1235 /* map a string to an xmlrpc type */
xmlrpc_str_as_type(const char * str)1236 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) /* {{{ */
1237 {
1238 const char** str_mapping = get_type_str_mapping();
1239 int i;
1240
1241 if (str) {
1242 for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
1243 if (!strcmp(str_mapping[i], str)) {
1244 return (XMLRPC_VALUE_TYPE) i;
1245 }
1246 }
1247 }
1248 return xmlrpc_none;
1249 }
1250 /* }}} */
1251
1252 /* map a string to an xmlrpc vector type */
xmlrpc_str_as_vector_type(const char * str)1253 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) /* {{{ */
1254 {
1255 const char** str_mapping = get_type_str_mapping();
1256 int i;
1257
1258 if (str) {
1259 for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
1260 if (!strcmp(str_mapping[i], str)) {
1261 return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
1262 }
1263 }
1264 }
1265 return xmlrpc_none;
1266 }
1267 /* }}} */
1268
1269 /* set a given value to a particular type.
1270 * note: this only works on strings, and only for date and base64,
1271 * which do not have native php types. black magic lies herein.
1272 */
set_zval_xmlrpc_type(zval * value,XMLRPC_VALUE_TYPE newtype)1273 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype) /* {{{ */
1274 {
1275 int bSuccess = FAILURE;
1276
1277 /* we only really care about strings because they can represent
1278 * base64 and datetime. all other types have corresponding php types
1279 */
1280 if (Z_TYPE_P(value) == IS_STRING) {
1281 if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
1282 const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
1283 zval type;
1284
1285 ZVAL_STRING(&type, typestr);
1286
1287 if (newtype == xmlrpc_datetime) {
1288 XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, Z_STRVAL_P(value));
1289 if (v) {
1290 time_t timestamp = (time_t) php_parse_date((char *)XMLRPC_GetValueDateTime_ISO8601(v), NULL);
1291 if (timestamp != -1) {
1292 zval ztimestamp;
1293
1294 ZVAL_LONG(&ztimestamp, timestamp);
1295
1296 convert_to_object(value);
1297 zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1, &type);
1298 bSuccess = (zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR) - 1, &ztimestamp) != NULL)? SUCCESS : FAILURE;
1299 } else {
1300 zval_ptr_dtor(&type);
1301 }
1302 XMLRPC_CleanupValue(v);
1303 } else {
1304 zval_ptr_dtor(&type);
1305 }
1306 } else {
1307 convert_to_object(value);
1308 zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1, &type);
1309 bSuccess = SUCCESS;
1310 }
1311 }
1312 }
1313
1314 return bSuccess;
1315 }
1316 /* }}} */
1317
1318 /* return xmlrpc type of a php value */
get_zval_xmlrpc_type(zval * value,zval * newvalue)1319 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval* newvalue) /* {{{ */
1320 {
1321 XMLRPC_VALUE_TYPE type = xmlrpc_none;
1322
1323 if (value) {
1324 switch (Z_TYPE_P(value)) {
1325 case IS_NULL:
1326 type = xmlrpc_base64;
1327 break;
1328 #ifndef BOOL_AS_LONG
1329
1330 /* Right thing to do, but it breaks some legacy code. */
1331 case IS_TRUE:
1332 case IS_FALSE:
1333 type = xmlrpc_boolean;
1334 break;
1335 #else
1336 case IS_BOOL:
1337 #endif
1338 case IS_LONG:
1339 case IS_RESOURCE:
1340 type = xmlrpc_int;
1341 break;
1342 case IS_DOUBLE:
1343 type = xmlrpc_double;
1344 break;
1345 case IS_STRING:
1346 type = xmlrpc_string;
1347 break;
1348 case IS_ARRAY:
1349 type = xmlrpc_vector;
1350 break;
1351 case IS_OBJECT:
1352 {
1353 zval* attr;
1354 type = xmlrpc_vector;
1355
1356 if ((attr = zend_hash_str_find_ind(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1)) != NULL) {
1357 if (Z_TYPE_P(attr) == IS_STRING) {
1358 type = xmlrpc_str_as_type(Z_STRVAL_P(attr));
1359 }
1360 }
1361 break;
1362 }
1363 }
1364
1365 /* if requested, return an unmolested (magic removed) copy of the value */
1366 if (newvalue) {
1367 zval* val;
1368
1369 if ((type == xmlrpc_base64 && Z_TYPE_P(value) == IS_OBJECT) || type == xmlrpc_datetime) {
1370 if ((val = zend_hash_str_find_ind(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR) - 1)) != NULL) {
1371 ZVAL_COPY_VALUE(newvalue, val);
1372 }
1373 } else {
1374 ZVAL_COPY_VALUE(newvalue, value);
1375 }
1376 }
1377 }
1378
1379 return type;
1380 }
1381 /* }}} */
1382
1383 /* {{{ proto bool xmlrpc_set_type(string value, string type)
1384 Sets xmlrpc type, base64 or datetime, for a PHP string value */
PHP_FUNCTION(xmlrpc_set_type)1385 PHP_FUNCTION(xmlrpc_set_type)
1386 {
1387 zval *arg;
1388 char *type;
1389 size_t type_len;
1390 XMLRPC_VALUE_TYPE vtype;
1391
1392 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/s", &arg, &type, &type_len) == FAILURE) {
1393 return;
1394 }
1395
1396 vtype = xmlrpc_str_as_type(type);
1397 if (vtype != xmlrpc_none) {
1398 if (set_zval_xmlrpc_type(arg, vtype) == SUCCESS) {
1399 RETURN_TRUE;
1400 }
1401 } else {
1402 zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type);
1403 }
1404 RETURN_FALSE;
1405 }
1406 /* }}} */
1407
1408 /* {{{ proto string xmlrpc_get_type(mixed value)
1409 Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */
PHP_FUNCTION(xmlrpc_get_type)1410 PHP_FUNCTION(xmlrpc_get_type)
1411 {
1412 zval *arg;
1413 XMLRPC_VALUE_TYPE type;
1414 XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
1415
1416 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1417 return;
1418 }
1419
1420 type = get_zval_xmlrpc_type(arg, 0);
1421 if (type == xmlrpc_vector) {
1422 vtype = determine_vector_type((Z_TYPE_P(arg) == IS_OBJECT) ? Z_OBJPROP_P(arg) : Z_ARRVAL_P(arg));
1423 }
1424
1425 RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype));
1426 }
1427 /* }}} */
1428
1429 /* {{{ proto bool xmlrpc_is_fault(array arg)
1430 Determines if an array value represents an XMLRPC fault. */
PHP_FUNCTION(xmlrpc_is_fault)1431 PHP_FUNCTION(xmlrpc_is_fault)
1432 {
1433 zval *arg;
1434
1435 if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &arg) == FAILURE) {
1436 return;
1437 }
1438
1439 /* The "correct" way to do this would be to call the xmlrpc
1440 * library XMLRPC_ValueIsFault() func. However, doing that
1441 * would require us to create an xmlrpc value from the php
1442 * array, which is rather expensive, especially if it was
1443 * a big array. Thus, we resort to this not so clever hackery.
1444 */
1445 if (zend_hash_str_exists(Z_ARRVAL_P(arg), FAULT_CODE, FAULT_CODE_LEN) &&
1446 zend_hash_str_exists(Z_ARRVAL_P(arg), FAULT_STRING, FAULT_STRING_LEN)) {
1447 RETURN_TRUE;
1448 }
1449
1450 RETURN_FALSE;
1451 }
1452 /* }}} */
1453
1454 /*
1455 * Local variables:
1456 * tab-width: 4
1457 * c-basic-offset: 4
1458 * End:
1459 */
1460