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