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