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