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