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, uint 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 && 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) {
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) {
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 free(outBuf);
705 }
706 XMLRPC_RequestFree(xRequest, 1);
707 }
708 }
709
710 if (strcmp(out.xmlrpc_out.xml_elem_opts.encoding, ENCODING_DEFAULT) != 0) {
711 efree((char *)out.xmlrpc_out.xml_elem_opts.encoding);
712 }
713 }
714 /* }}} */
715
716 /* {{{ proto string xmlrpc_encode(mixed value)
717 Generates XML for a PHP value */
PHP_FUNCTION(xmlrpc_encode)718 PHP_FUNCTION(xmlrpc_encode)
719 {
720 XMLRPC_VALUE xOut = NULL;
721 zval *arg1;
722 char *outBuf;
723
724 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg1) == FAILURE) {
725 return;
726 }
727
728 if (USED_RET()) {
729 /* convert native php type to xmlrpc type */
730 xOut = PHP_to_XMLRPC(arg1);
731
732 /* generate raw xml from xmlrpc data */
733 outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
734
735 if (xOut) {
736 if (outBuf) {
737 RETVAL_STRING(outBuf);
738 free(outBuf);
739 }
740 /* cleanup */
741 XMLRPC_CleanupValue(xOut);
742 }
743 }
744 }
745 /* }}} */
746
decode_request_worker(char * xml_in,int xml_in_len,char * encoding_in,zval * method_name_out,zval * retval)747 void decode_request_worker(char *xml_in, int xml_in_len, char *encoding_in, zval* method_name_out, zval *retval) /* {{{ */
748 {
749 XMLRPC_REQUEST response;
750 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}};
751 const char *method_name;
752 opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in) : ENCODING_DEFAULT;
753
754 /* generate XMLRPC_REQUEST from raw xml */
755 response = XMLRPC_REQUEST_FromXML(xml_in, xml_in_len, &opts);
756 if (response) {
757 ZVAL_NULL(retval);
758 /* convert xmlrpc data to native php types */
759 XMLRPC_to_PHP(XMLRPC_RequestGetData(response), retval);
760
761 if (XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
762 if (method_name_out) {
763 method_name = XMLRPC_RequestGetMethodName(response);
764 if (method_name) {
765 zval_ptr_dtor(method_name_out);
766 ZVAL_STRING(method_name_out, method_name);
767 } else {
768 zval_ptr_dtor(retval);
769 ZVAL_NULL(retval);
770 }
771 }
772 }
773
774 /* dust, sweep, and mop */
775 XMLRPC_RequestFree(response, 1);
776 }
777 }
778 /* }}} */
779
780 /* {{{ proto array xmlrpc_decode_request(string xml, string& method [, string encoding])
781 Decodes XML into native PHP types */
PHP_FUNCTION(xmlrpc_decode_request)782 PHP_FUNCTION(xmlrpc_decode_request)
783 {
784 char *xml, *encoding = NULL;
785 zval *method;
786 size_t xml_len, encoding_len = 0;
787
788 if (zend_parse_parameters(ZEND_NUM_ARGS(), "sz/|s", &xml, &xml_len, &method, &encoding, &encoding_len) == FAILURE) {
789 return;
790 }
791
792 if (USED_RET()) {
793 decode_request_worker(xml, xml_len, encoding_len ? encoding : NULL, method, return_value);
794 }
795 }
796 /* }}} */
797
798 /* {{{ proto array xmlrpc_decode(string xml [, string encoding])
799 Decodes XML into native PHP types */
PHP_FUNCTION(xmlrpc_decode)800 PHP_FUNCTION(xmlrpc_decode)
801 {
802 char *arg1, *arg2 = NULL;
803 size_t arg1_len, arg2_len = 0;
804
805 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|s", &arg1, &arg1_len, &arg2, &arg2_len) == FAILURE) {
806 return;
807 }
808
809 if (USED_RET()) {
810 decode_request_worker(arg1, arg1_len, arg2_len ? arg2 : NULL, NULL, return_value);
811 }
812 }
813 /* }}} */
814
815 /*************************
816 * server related methods *
817 *************************/
818
819 /* {{{ proto resource xmlrpc_server_create(void)
820 Creates an xmlrpc server */
PHP_FUNCTION(xmlrpc_server_create)821 PHP_FUNCTION(xmlrpc_server_create)
822 {
823 if (zend_parse_parameters_none() == FAILURE) {
824 return;
825 }
826
827 if (USED_RET()) {
828 xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
829
830 /* allocate server data. free'd in destroy_server_data() */
831 array_init(&server->method_map);
832 array_init(&server->introspection_map);
833 server->server_ptr = XMLRPC_ServerCreate();
834
835 XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
836
837 /* store for later use */
838 RETURN_RES(zend_register_resource(server, le_xmlrpc_server));
839 }
840 }
841 /* }}} */
842
843 /* {{{ proto int xmlrpc_server_destroy(resource server)
844 Destroys server resources */
PHP_FUNCTION(xmlrpc_server_destroy)845 PHP_FUNCTION(xmlrpc_server_destroy)
846 {
847 zval *arg1;
848 int bSuccess = FAILURE;
849 xmlrpc_server_data *server;
850
851 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg1) == FAILURE) {
852 return;
853 }
854
855 if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(arg1), "xmlrpc server", le_xmlrpc_server)) == NULL) {
856 RETURN_FALSE;
857 }
858
859 bSuccess = zend_list_close(Z_RES_P(arg1));
860 /* called by hashtable destructor
861 * destroy_server_data(server);
862 */
863 RETURN_BOOL(bSuccess == SUCCESS);
864 }
865 /* }}} */
866
867 /* called by xmlrpc C engine as method handler for all registered methods.
868 * it then calls the corresponding PHP function to handle the method.
869 */
php_xmlrpc_callback(XMLRPC_SERVER server,XMLRPC_REQUEST xRequest,void * data)870 static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data) /* {{{ */
871 {
872 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
873 zval* php_function;
874 zval xmlrpc_params;
875 zval callback_params[3];
876
877 zval_ptr_dtor(&pData->xmlrpc_method);
878 zval_ptr_dtor(&pData->return_data);
879
880 /* convert xmlrpc to native php types */
881 ZVAL_STRING(&pData->xmlrpc_method, XMLRPC_RequestGetMethodName(xRequest));
882 XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest), &xmlrpc_params);
883
884 /* check if the called method has been previous registered */
885 if ((php_function = zend_hash_find(Z_ARRVAL(pData->server->method_map), Z_STR(pData->xmlrpc_method))) != NULL) {
886 ZVAL_COPY_VALUE(&pData->php_function, php_function);
887 }
888
889 /* setup data hoojum */
890 ZVAL_COPY_VALUE(&callback_params[0], &pData->xmlrpc_method);
891 ZVAL_COPY_VALUE(&callback_params[1], &xmlrpc_params);
892 ZVAL_COPY_VALUE(&callback_params[2], &pData->caller_params);
893
894 /* Use same C function for all methods */
895
896 /* php func prototype: function user_func($method_name, $xmlrpc_params, $user_params) */
897 call_user_function(CG(function_table), NULL, &pData->php_function, &pData->return_data, 3, callback_params);
898
899 pData->php_executed = 1;
900
901 zval_ptr_dtor(&xmlrpc_params);
902
903 return PHP_to_XMLRPC(&pData->return_data);
904 }
905 /* }}} */
906
907 /* called by the C server when it first receives an introspection request. We pass this on to
908 * our PHP listeners, if any
909 */
php_xmlrpc_introspection_callback(XMLRPC_SERVER server,void * data)910 static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data) /* {{{ */
911 {
912 zval retval, *php_function;
913 zval callback_params[1];
914 zend_string *php_function_name;
915 xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
916
917 /* setup data hoojum */
918 ZVAL_COPY_VALUE(&callback_params[0], &pData->caller_params);
919
920 ZEND_HASH_FOREACH_VAL(Z_ARRVAL(pData->server->introspection_map), php_function) {
921 if (zend_is_callable(php_function, 0, &php_function_name)) {
922 /* php func prototype: function string user_func($user_params) */
923 if (call_user_function(CG(function_table), NULL, php_function, &retval, 1, callback_params) == SUCCESS) {
924 XMLRPC_VALUE xData;
925 STRUCT_XMLRPC_ERROR err = {0};
926
927 /* return value should be a string */
928 convert_to_string(&retval);
929
930 xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL(retval), &err);
931
932 if (xData) {
933 if (!XMLRPC_ServerAddIntrospectionData(server, xData)) {
934 php_error_docref(NULL, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", ZSTR_VAL(php_function_name));
935 }
936 XMLRPC_CleanupValue(xData);
937 } else {
938 /* could not create description */
939 if (err.xml_elem_error.parser_code) {
940 php_error_docref(NULL, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to add introspection data returned from %s()",
941 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, ZSTR_VAL(php_function_name));
942 } else {
943 php_error_docref(NULL, E_WARNING, "Unable to add introspection data returned from %s()", ZSTR_VAL(php_function_name));
944 }
945 }
946 zval_ptr_dtor(&retval);
947 } else {
948 /* user func failed */
949 php_error_docref(NULL, E_WARNING, "Error calling user introspection callback: %s()", ZSTR_VAL(php_function_name));
950 }
951 } else {
952 php_error_docref(NULL, E_WARNING, "Invalid callback '%s' passed", ZSTR_VAL(php_function_name));
953 }
954 zend_string_release(php_function_name);
955 } ZEND_HASH_FOREACH_END();
956
957 /* so we don't call the same callbacks ever again */
958 zend_hash_clean(Z_ARRVAL(pData->server->introspection_map));
959 }
960 /* }}} */
961
962 /* {{{ proto bool xmlrpc_server_register_method(resource server, string method_name, string function)
963 Register a PHP function to handle method matching method_name */
PHP_FUNCTION(xmlrpc_server_register_method)964 PHP_FUNCTION(xmlrpc_server_register_method)
965 {
966 char *method_key;
967 size_t method_key_len;
968 zval *handle, *method_name;
969 xmlrpc_server_data* server;
970
971 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsz", &handle, &method_key, &method_key_len, &method_name) == FAILURE) {
972 return;
973 }
974
975 if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
976 RETURN_FALSE;
977 }
978
979 /* register with C engine. every method just calls our standard callback,
980 * and it then dispatches to php as necessary
981 */
982 if (XMLRPC_ServerRegisterMethod(server->server_ptr, method_key, php_xmlrpc_callback)) {
983 /* save for later use */
984
985 if (Z_REFCOUNTED_P(method_name)) {
986 Z_ADDREF_P(method_name);
987 }
988 /* register our php method */
989 add_zval(&server->method_map, method_key, method_name);
990
991 RETURN_TRUE;
992 }
993 }
994 /* }}} */
995
996 /* {{{ proto bool xmlrpc_server_register_introspection_callback(resource server, string function)
997 Register a PHP function to generate documentation */
PHP_FUNCTION(xmlrpc_server_register_introspection_callback)998 PHP_FUNCTION(xmlrpc_server_register_introspection_callback)
999 {
1000 zval *method_name, *handle;
1001 xmlrpc_server_data* server;
1002
1003 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rz", &handle, &method_name) == FAILURE) {
1004 return;
1005 }
1006
1007 if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
1008 RETURN_FALSE;
1009 }
1010
1011 if (Z_REFCOUNTED_P(method_name)) {
1012 Z_ADDREF_P(method_name);
1013 }
1014 /* register our php method */
1015 add_zval(&server->introspection_map, NULL, method_name);
1016
1017 RETURN_TRUE;
1018 }
1019 /* }}} */
1020
1021 /* this function is itchin for a re-write */
1022
1023 /* {{{ proto mixed xmlrpc_server_call_method(resource server, string xml, mixed user_data [, array output_options])
1024 Parses XML requests and call methods */
PHP_FUNCTION(xmlrpc_server_call_method)1025 PHP_FUNCTION(xmlrpc_server_call_method)
1026 {
1027 XMLRPC_REQUEST xRequest;
1028 xmlrpc_callback_data data;
1029 STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
1030 xmlrpc_server_data* server;
1031 zval *caller_params, *handle, *output_opts = NULL;
1032 char *rawxml;
1033 size_t rawxml_len;
1034 php_output_options out;
1035 int argc = ZEND_NUM_ARGS();
1036
1037 if (zend_parse_parameters(ZEND_NUM_ARGS(), "rsz|a", &handle, &rawxml, &rawxml_len, &caller_params, &output_opts) != SUCCESS) {
1038 return;
1039 }
1040 /* user output options */
1041 if (argc == 3) {
1042 set_output_options(&out, NULL);
1043 } else {
1044 set_output_options(&out, output_opts);
1045 }
1046
1047 if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
1048 RETURN_FALSE;
1049 }
1050
1051 /* HACK: use output encoding for now */
1052 input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
1053
1054 /* generate an XMLRPC_REQUEST from the raw xml input */
1055 xRequest = XMLRPC_REQUEST_FromXML(rawxml, rawxml_len, &input_opts);
1056
1057 if (xRequest) {
1058 const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
1059 XMLRPC_VALUE xAnswer = NULL;
1060 ZVAL_NULL(&data.xmlrpc_method); /* init. very important. spent a frustrating day finding this out. */
1061 ZVAL_NULL(&data.return_data);
1062 ZVAL_NULL(&data.return_data); /* in case value is never init'd, we don't dtor to think it is a string or something */
1063 ZVAL_NULL(&data.xmlrpc_method);
1064
1065 /* setup some data to pass to the callback function */
1066 ZVAL_COPY_VALUE(&data.caller_params, caller_params);
1067 data.php_executed = 0;
1068 data.server = server;
1069
1070 /* We could just call the php method directly ourselves at this point, but we do this
1071 * with a C callback in case the xmlrpc library ever implements some cool usage stats,
1072 * or somesuch.
1073 */
1074 xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
1075 if (xAnswer && out.b_php_out) {
1076 XMLRPC_to_PHP(xAnswer, &data.return_data);
1077 } else if (data.php_executed && !out.b_php_out && !xAnswer) {
1078 xAnswer = PHP_to_XMLRPC(&data.return_data);
1079 }
1080
1081 /* should we return data as xml? */
1082 if (!out.b_php_out) {
1083 XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
1084 if (xResponse) {
1085 char *outBuf = 0;
1086 int buf_len = 0;
1087
1088 /* automagically determine output serialization type from request type */
1089 if (out.b_auto_version) {
1090 XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
1091 if (opts) {
1092 out.xmlrpc_out.version = opts->version;
1093 }
1094 }
1095 /* set some required request hoojum */
1096 XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
1097 XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
1098 XMLRPC_RequestSetData(xResponse, xAnswer);
1099 XMLRPC_RequestSetMethodName(xResponse, methodname);
1100
1101 /* generate xml */
1102 outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
1103 if (outBuf) {
1104 RETVAL_STRINGL(outBuf, buf_len);
1105 free(outBuf);
1106 }
1107 /* cleanup after ourselves. what a sty! */
1108 XMLRPC_RequestFree(xResponse, 0);
1109 }
1110 } else { /* or as native php types? */
1111 ZVAL_COPY(return_value, &data.return_data);
1112 }
1113
1114 /* cleanup after ourselves. what a sty! */
1115 zval_ptr_dtor(&data.xmlrpc_method);
1116 zval_ptr_dtor(&data.return_data);
1117
1118 if (xAnswer) {
1119 XMLRPC_CleanupValue(xAnswer);
1120 }
1121
1122 XMLRPC_RequestFree(xRequest, 1);
1123 }
1124 }
1125 /* }}} */
1126
1127 /* {{{ proto int xmlrpc_server_add_introspection_data(resource server, array desc)
1128 Adds introspection documentation */
PHP_FUNCTION(xmlrpc_server_add_introspection_data)1129 PHP_FUNCTION(xmlrpc_server_add_introspection_data)
1130 {
1131 zval *handle, *desc;
1132 xmlrpc_server_data* server;
1133 XMLRPC_VALUE xDesc;
1134
1135 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra", &handle, &desc) == FAILURE) {
1136 return;
1137 }
1138
1139 if ((server = (xmlrpc_server_data *)zend_fetch_resource(Z_RES_P(handle), "xmlrpc server", le_xmlrpc_server)) == NULL) {
1140 RETURN_FALSE;
1141 }
1142
1143 xDesc = PHP_to_XMLRPC(desc);
1144 if (xDesc) {
1145 int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
1146 XMLRPC_CleanupValue(xDesc);
1147 RETURN_LONG(retval);
1148 }
1149 RETURN_LONG(0);
1150 }
1151 /* }}} */
1152
1153 /* {{{ proto array xmlrpc_parse_method_descriptions(string xml)
1154 Decodes XML into a list of method descriptions */
PHP_FUNCTION(xmlrpc_parse_method_descriptions)1155 PHP_FUNCTION(xmlrpc_parse_method_descriptions)
1156 {
1157 char *arg1;
1158 size_t arg1_len;
1159
1160 if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg1, &arg1_len) == FAILURE) {
1161 return;
1162 }
1163
1164 if (USED_RET()) {
1165 STRUCT_XMLRPC_ERROR err = {0};
1166 XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1, &err);
1167 if (xVal) {
1168 XMLRPC_to_PHP(xVal, return_value);
1169 /* dust, sweep, and mop */
1170 XMLRPC_CleanupValue(xVal);
1171 } else {
1172 /* could not create description */
1173 if (err.xml_elem_error.parser_code) {
1174 php_error_docref(NULL, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data",
1175 err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
1176 } else {
1177 php_error_docref(NULL, E_WARNING, "Invalid xml structure. Unable to create introspection data");
1178 }
1179
1180 php_error_docref(NULL, E_WARNING, "xml parse error. no method description created");
1181 }
1182 }
1183 }
1184 /* }}} */
1185
1186 /************
1187 * type data *
1188 ************/
1189
1190 #define XMLRPC_TYPE_COUNT 9
1191 #define XMLRPC_VECTOR_TYPE_COUNT 4
1192 #define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
1193
1194 /* return a string matching a given xmlrpc type */
get_type_str_mapping(void)1195 static const char** get_type_str_mapping(void) /* {{{ */
1196 {
1197 static const char* str_mapping[TYPE_STR_MAP_SIZE];
1198 static int first = 1;
1199 if (first) {
1200 /* warning. do not add/delete without changing size define */
1201 str_mapping[xmlrpc_none] = "none";
1202 str_mapping[xmlrpc_empty] = "empty";
1203 str_mapping[xmlrpc_base64] = "base64";
1204 str_mapping[xmlrpc_boolean] = "boolean";
1205 str_mapping[xmlrpc_datetime] = "datetime";
1206 str_mapping[xmlrpc_double] = "double";
1207 str_mapping[xmlrpc_int] = "int";
1208 str_mapping[xmlrpc_string] = "string";
1209 str_mapping[xmlrpc_vector] = "vector";
1210 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none";
1211 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array";
1212 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed";
1213 str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
1214 first = 0;
1215 }
1216 return (const char**)str_mapping;
1217 }
1218 /* }}} */
1219
1220 /* map an xmlrpc type to a string */
xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type,XMLRPC_VECTOR_TYPE vtype)1221 const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype) /* {{{ */
1222 {
1223 const char** str_mapping = get_type_str_mapping();
1224
1225 if (vtype == xmlrpc_vector_none) {
1226 return str_mapping[type];
1227 } else {
1228 return str_mapping[XMLRPC_TYPE_COUNT + vtype];
1229 }
1230 }
1231 /* }}} */
1232
1233 /* map a string to an xmlrpc type */
xmlrpc_str_as_type(const char * str)1234 XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str) /* {{{ */
1235 {
1236 const char** str_mapping = get_type_str_mapping();
1237 int i;
1238
1239 if (str) {
1240 for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
1241 if (!strcmp(str_mapping[i], str)) {
1242 return (XMLRPC_VALUE_TYPE) i;
1243 }
1244 }
1245 }
1246 return xmlrpc_none;
1247 }
1248 /* }}} */
1249
1250 /* map a string to an xmlrpc vector type */
xmlrpc_str_as_vector_type(const char * str)1251 XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str) /* {{{ */
1252 {
1253 const char** str_mapping = get_type_str_mapping();
1254 int i;
1255
1256 if (str) {
1257 for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
1258 if (!strcmp(str_mapping[i], str)) {
1259 return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
1260 }
1261 }
1262 }
1263 return xmlrpc_none;
1264 }
1265 /* }}} */
1266
1267 /* set a given value to a particular type.
1268 * note: this only works on strings, and only for date and base64,
1269 * which do not have native php types. black magic lies herein.
1270 */
set_zval_xmlrpc_type(zval * value,XMLRPC_VALUE_TYPE newtype)1271 int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype) /* {{{ */
1272 {
1273 int bSuccess = FAILURE;
1274
1275 /* we only really care about strings because they can represent
1276 * base64 and datetime. all other types have corresponding php types
1277 */
1278 if (Z_TYPE_P(value) == IS_STRING) {
1279 if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
1280 const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
1281 zval type;
1282
1283 ZVAL_STRING(&type, typestr);
1284
1285 if (newtype == xmlrpc_datetime) {
1286 XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, Z_STRVAL_P(value));
1287 if (v) {
1288 time_t timestamp = (time_t) php_parse_date((char *)XMLRPC_GetValueDateTime_ISO8601(v), NULL);
1289 if (timestamp != -1) {
1290 zval ztimestamp;
1291
1292 ZVAL_LONG(&ztimestamp, timestamp);
1293
1294 convert_to_object(value);
1295 if (zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1, &type)) {
1296 bSuccess = (zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR) - 1, &ztimestamp) != NULL)? SUCCESS : FAILURE;
1297 }
1298 } else {
1299 zval_ptr_dtor(&type);
1300 }
1301 XMLRPC_CleanupValue(v);
1302 } else {
1303 zval_ptr_dtor(&type);
1304 }
1305 } else {
1306 convert_to_object(value);
1307 bSuccess = (zend_hash_str_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1, &type) != NULL)? SUCCESS : FAILURE;
1308 }
1309 }
1310 }
1311
1312 return bSuccess;
1313 }
1314 /* }}} */
1315
1316 /* return xmlrpc type of a php value */
get_zval_xmlrpc_type(zval * value,zval * newvalue)1317 XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval* newvalue) /* {{{ */
1318 {
1319 XMLRPC_VALUE_TYPE type = xmlrpc_none;
1320
1321 if (value) {
1322 switch (Z_TYPE_P(value)) {
1323 case IS_NULL:
1324 type = xmlrpc_base64;
1325 break;
1326 #ifndef BOOL_AS_LONG
1327
1328 /* Right thing to do, but it breaks some legacy code. */
1329 case IS_TRUE:
1330 case IS_FALSE:
1331 type = xmlrpc_boolean;
1332 break;
1333 #else
1334 case IS_BOOL:
1335 #endif
1336 case IS_LONG:
1337 case IS_RESOURCE:
1338 type = xmlrpc_int;
1339 break;
1340 case IS_DOUBLE:
1341 type = xmlrpc_double;
1342 break;
1343 case IS_CONSTANT:
1344 type = xmlrpc_string;
1345 break;
1346 case IS_STRING:
1347 type = xmlrpc_string;
1348 break;
1349 case IS_ARRAY:
1350 type = xmlrpc_vector;
1351 break;
1352 case IS_OBJECT:
1353 {
1354 zval* attr;
1355 type = xmlrpc_vector;
1356
1357 if ((attr = zend_hash_str_find_ind(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR) - 1)) != NULL) {
1358 if (Z_TYPE_P(attr) == IS_STRING) {
1359 type = xmlrpc_str_as_type(Z_STRVAL_P(attr));
1360 }
1361 }
1362 break;
1363 }
1364 }
1365
1366 /* if requested, return an unmolested (magic removed) copy of the value */
1367 if (newvalue) {
1368 zval* val;
1369
1370 if ((type == xmlrpc_base64 && Z_TYPE_P(value) == IS_OBJECT) || type == xmlrpc_datetime) {
1371 if ((val = zend_hash_str_find_ind(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR) - 1)) != NULL) {
1372 ZVAL_COPY_VALUE(newvalue, val);
1373 }
1374 } else {
1375 ZVAL_COPY_VALUE(newvalue, value);
1376 }
1377 }
1378 }
1379
1380 return type;
1381 }
1382 /* }}} */
1383
1384 /* {{{ proto bool xmlrpc_set_type(string value, string type)
1385 Sets xmlrpc type, base64 or datetime, for a PHP string value */
PHP_FUNCTION(xmlrpc_set_type)1386 PHP_FUNCTION(xmlrpc_set_type)
1387 {
1388 zval *arg;
1389 char *type;
1390 size_t type_len;
1391 XMLRPC_VALUE_TYPE vtype;
1392
1393 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z/s", &arg, &type, &type_len) == FAILURE) {
1394 return;
1395 }
1396
1397 vtype = xmlrpc_str_as_type(type);
1398 if (vtype != xmlrpc_none) {
1399 if (set_zval_xmlrpc_type(arg, vtype) == SUCCESS) {
1400 RETURN_TRUE;
1401 }
1402 } else {
1403 zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type);
1404 }
1405 RETURN_FALSE;
1406 }
1407 /* }}} */
1408
1409 /* {{{ proto string xmlrpc_get_type(mixed value)
1410 Gets xmlrpc type for a PHP value. Especially useful for base64 and datetime strings */
PHP_FUNCTION(xmlrpc_get_type)1411 PHP_FUNCTION(xmlrpc_get_type)
1412 {
1413 zval *arg;
1414 XMLRPC_VALUE_TYPE type;
1415 XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
1416
1417 if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &arg) == FAILURE) {
1418 return;
1419 }
1420
1421 type = get_zval_xmlrpc_type(arg, 0);
1422 if (type == xmlrpc_vector) {
1423 vtype = determine_vector_type((Z_TYPE_P(arg) == IS_OBJECT) ? Z_OBJPROP_P(arg) : Z_ARRVAL_P(arg));
1424 }
1425
1426 RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype));
1427 }
1428 /* }}} */
1429
1430 /* {{{ proto bool xmlrpc_is_fault(array arg)
1431 Determines if an array value represents an XMLRPC fault. */
PHP_FUNCTION(xmlrpc_is_fault)1432 PHP_FUNCTION(xmlrpc_is_fault)
1433 {
1434 zval *arg;
1435
1436 if (zend_parse_parameters(ZEND_NUM_ARGS(), "a", &arg) == FAILURE) {
1437 return;
1438 }
1439
1440 /* The "correct" way to do this would be to call the xmlrpc
1441 * library XMLRPC_ValueIsFault() func. However, doing that
1442 * would require us to create an xmlrpc value from the php
1443 * array, which is rather expensive, especially if it was
1444 * a big array. Thus, we resort to this not so clever hackery.
1445 */
1446 if (zend_hash_str_exists(Z_ARRVAL_P(arg), FAULT_CODE, FAULT_CODE_LEN) &&
1447 zend_hash_str_exists(Z_ARRVAL_P(arg), FAULT_STRING, FAULT_STRING_LEN)) {
1448 RETURN_TRUE;
1449 }
1450
1451 RETURN_FALSE;
1452 }
1453 /* }}} */
1454
1455 /*
1456 * Local variables:
1457 * tab-width: 4
1458 * c-basic-offset: 4
1459 * End:
1460 */
1461