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