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