#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "ext/standard/info.h"
#include "ext/standard/php_string.h"
#include "ext/date/php_date.h"
#include "php_ini.h"
#include "php_xmlrpc.h"
#include "xmlrpc.h"
#define PHP_EXT_VERSION "0.51"
static int le_xmlrpc_server;
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_encode, 0, 0, 1)
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_decode, 0, 0, 1)
ZEND_ARG_INFO(0, value)
ZEND_ARG_INFO(0, encoding)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_decode_request, 0, 0, 2)
ZEND_ARG_INFO(0, xml)
ZEND_ARG_INFO(1, method)
ZEND_ARG_INFO(0, encoding)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_encode_request, 0, 0, 2)
ZEND_ARG_INFO(0, method)
ZEND_ARG_INFO(0, params)
ZEND_ARG_INFO(0, output_options)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_set_type, 0, 0, 2)
ZEND_ARG_INFO(1, value)
ZEND_ARG_INFO(0, type)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_is_fault, 0, 0, 1)
ZEND_ARG_INFO(0, arg)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO(arginfo_xmlrpc_server_create, 0)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_destroy, 0, 0, 1)
ZEND_ARG_INFO(0, server)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_register_method, 0, 0, 3)
ZEND_ARG_INFO(0, server)
ZEND_ARG_INFO(0, method_name)
ZEND_ARG_INFO(0, function)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_call_method, 0, 0, 3)
ZEND_ARG_INFO(0, server)
ZEND_ARG_INFO(0, xml)
ZEND_ARG_INFO(0, user_data)
ZEND_ARG_INFO(0, output_options)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_parse_method_descriptions, 0, 0, 1)
ZEND_ARG_INFO(0, xml)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_add_introspection_data, 0, 0, 2)
ZEND_ARG_INFO(0, server)
ZEND_ARG_INFO(0, desc)
ZEND_END_ARG_INFO()
ZEND_BEGIN_ARG_INFO_EX(arginfo_xmlrpc_server_register_introspection_callback, 0, 0, 2)
ZEND_ARG_INFO(0, server)
ZEND_ARG_INFO(0, function)
ZEND_END_ARG_INFO()
const zend_function_entry xmlrpc_functions[] = {
PHP_FE(xmlrpc_encode, arginfo_xmlrpc_encode)
PHP_FE(xmlrpc_decode, arginfo_xmlrpc_decode)
PHP_FE(xmlrpc_decode_request, arginfo_xmlrpc_decode_request)
PHP_FE(xmlrpc_encode_request, arginfo_xmlrpc_encode_request)
PHP_FE(xmlrpc_get_type, arginfo_xmlrpc_encode)
PHP_FE(xmlrpc_set_type, arginfo_xmlrpc_set_type)
PHP_FE(xmlrpc_is_fault, arginfo_xmlrpc_is_fault)
PHP_FE(xmlrpc_server_create, arginfo_xmlrpc_server_create)
PHP_FE(xmlrpc_server_destroy, arginfo_xmlrpc_server_destroy)
PHP_FE(xmlrpc_server_register_method, arginfo_xmlrpc_server_register_method)
PHP_FE(xmlrpc_server_call_method, arginfo_xmlrpc_server_call_method)
PHP_FE(xmlrpc_parse_method_descriptions, arginfo_xmlrpc_parse_method_descriptions)
PHP_FE(xmlrpc_server_add_introspection_data, arginfo_xmlrpc_server_add_introspection_data)
PHP_FE(xmlrpc_server_register_introspection_callback, arginfo_xmlrpc_server_register_introspection_callback)
PHP_FE_END
};
zend_module_entry xmlrpc_module_entry = {
STANDARD_MODULE_HEADER,
"xmlrpc",
xmlrpc_functions,
PHP_MINIT(xmlrpc),
NULL,
NULL,
NULL,
PHP_MINFO(xmlrpc),
PHP_EXT_VERSION,
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_XMLRPC
ZEND_GET_MODULE(xmlrpc)
#endif
typedef struct _xmlrpc_server_data {
zval* method_map;
zval* introspection_map;
XMLRPC_SERVER server_ptr;
} xmlrpc_server_data;
typedef struct _php_output_options {
int b_php_out;
int b_auto_version;
STRUCT_XMLRPC_REQUEST_OUTPUT_OPTIONS xmlrpc_out;
} php_output_options;
typedef struct _xmlrpc_callback_data {
zval* xmlrpc_method;
zval* php_function;
zval* caller_params;
zval* return_data;
xmlrpc_server_data* server;
char php_executed;
} xmlrpc_callback_data;
#define OUTPUT_TYPE_KEY "output_type"
#define OUTPUT_TYPE_KEY_LEN (sizeof(OUTPUT_TYPE_KEY) - 1)
#define OUTPUT_TYPE_VALUE_PHP "php"
#define OUTPUT_TYPE_VALUE_XML "xml"
#define VERBOSITY_KEY "verbosity"
#define VERBOSITY_KEY_LEN (sizeof(VERBOSITY_KEY) - 1)
#define VERBOSITY_VALUE_NO_WHITE_SPACE "no_white_space"
#define VERBOSITY_VALUE_NEWLINES_ONLY "newlines_only"
#define VERBOSITY_VALUE_PRETTY "pretty"
#define ESCAPING_KEY "escaping"
#define ESCAPING_KEY_LEN (sizeof(ESCAPING_KEY) - 1)
#define ESCAPING_VALUE_CDATA "cdata"
#define ESCAPING_VALUE_NON_ASCII "non-ascii"
#define ESCAPING_VALUE_NON_PRINT "non-print"
#define ESCAPING_VALUE_MARKUP "markup"
#define VERSION_KEY "version"
#define VERSION_KEY_LEN (sizeof(VERSION_KEY) - 1)
#define VERSION_VALUE_SIMPLE "simple"
#define VERSION_VALUE_XMLRPC "xmlrpc"
#define VERSION_VALUE_SOAP11 "soap 1.1"
#define VERSION_VALUE_AUTO "auto"
#define ENCODING_KEY "encoding"
#define ENCODING_KEY_LEN (sizeof(ENCODING_KEY) - 1)
#define ENCODING_DEFAULT "iso-8859-1"
#define OBJECT_TYPE_ATTR "xmlrpc_type"
#define OBJECT_VALUE_ATTR "scalar"
#define OBJECT_VALUE_TS_ATTR "timestamp"
#define FAULT_CODE "faultCode"
#define FAULT_CODE_LEN (sizeof(FAULT_CODE) - 1)
#define FAULT_STRING "faultString"
#define FAULT_STRING_LEN (sizeof(FAULT_STRING) - 1)
XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval** newvalue);
static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data);
int sset_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
zval* decode_request_worker(char *xml_in, int xml_in_len, char *encoding_in, zval* method_name_out);
const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype);
XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str);
XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str);
int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE type);
static void destroy_server_data(xmlrpc_server_data *server TSRMLS_DC)
{
if (server) {
XMLRPC_ServerDestroy(server->server_ptr);
zval_dtor(server->method_map);
FREE_ZVAL(server->method_map);
zval_dtor(server->introspection_map);
FREE_ZVAL(server->introspection_map);
efree(server);
}
}
static void xmlrpc_server_destructor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
if (rsrc && rsrc->ptr) {
destroy_server_data((xmlrpc_server_data*) rsrc->ptr TSRMLS_CC);
}
}
PHP_MINIT_FUNCTION(xmlrpc)
{
le_xmlrpc_server = zend_register_list_destructors_ex(xmlrpc_server_destructor, NULL, "xmlrpc server", module_number);
return SUCCESS;
}
PHP_MINFO_FUNCTION(xmlrpc)
{
php_info_print_table_start();
php_info_print_table_row(2, "core library version", XMLRPC_GetVersionString());
php_info_print_table_row(2, "php extension version", PHP_EXT_VERSION);
php_info_print_table_row(2, "author", "Dan Libby");
php_info_print_table_row(2, "homepage", "http://xmlrpc-epi.sourceforge.net");
php_info_print_table_row(2, "open sourced by", "Epinions.com");
php_info_print_table_end();
}
#if 0
static int add_long(zval* list, char* id, int num) {
if(id) return add_assoc_long(list, id, num);
else return add_next_index_long(list, num);
}
static int add_double(zval* list, char* id, double num) {
if(id) return add_assoc_double(list, id, num);
else return add_next_index_double(list, num);
}
static int add_string(zval* list, char* id, char* string, int duplicate) {
if(id) return add_assoc_string(list, id, string, duplicate);
else return add_next_index_string(list, string, duplicate);
}
static int add_stringl(zval* list, char* id, char* string, uint length, int duplicate) {
if(id) return add_assoc_stringl(list, id, string, length, duplicate);
else return add_next_index_stringl(list, string, length, duplicate);
}
#endif
static int add_zval(zval* list, const char* id, zval** val)
{
if (list && val) {
if (id) {
int id_len = strlen(id);
if (!(id_len > 1 && id[0] == '0') && is_numeric_string((char *)id, id_len, NULL, NULL, 0) == IS_LONG) {
long index = strtol(id, NULL, 0);
return zend_hash_index_update(Z_ARRVAL_P(list), index, (void *) val, sizeof(zval **), NULL);
} else {
return zend_hash_update(Z_ARRVAL_P(list), (char*) id, strlen(id) + 1, (void *) val, sizeof(zval **), NULL);
}
} else {
return zend_hash_next_index_insert(Z_ARRVAL_P(list), (void *) val, sizeof(zval **), NULL);
}
}
return 0;
}
#define my_zend_hash_get_current_key(ht, my_key, num_index) zend_hash_get_current_key(ht, my_key, num_index, 0)
static void set_output_options(php_output_options* options, zval* output_opts)
{
if (options) {
options->b_php_out = 0;
options->b_auto_version = 1;
options->xmlrpc_out.version = xmlrpc_version_1_0;
options->xmlrpc_out.xml_elem_opts.encoding = ENCODING_DEFAULT;
options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping | xml_elem_non_ascii_escaping | xml_elem_non_print_escaping;
if (output_opts && Z_TYPE_P(output_opts) == IS_ARRAY) {
zval** val;
if (zend_hash_find(Z_ARRVAL_P(output_opts), OUTPUT_TYPE_KEY, OUTPUT_TYPE_KEY_LEN + 1, (void**) &val) == SUCCESS) {
if (Z_TYPE_PP(val) == IS_STRING) {
if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_PHP)) {
options->b_php_out = 1;
} else if (!strcmp(Z_STRVAL_PP(val), OUTPUT_TYPE_VALUE_XML)) {
options->b_php_out = 0;
}
}
}
if (zend_hash_find(Z_ARRVAL_P(output_opts), VERBOSITY_KEY, VERBOSITY_KEY_LEN + 1, (void**) &val) == SUCCESS) {
if (Z_TYPE_PP(val) == IS_STRING) {
if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NO_WHITE_SPACE)) {
options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_no_white_space;
} else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_NEWLINES_ONLY)) {
options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_newlines_only;
} else if (!strcmp(Z_STRVAL_PP(val), VERBOSITY_VALUE_PRETTY)) {
options->xmlrpc_out.xml_elem_opts.verbosity = xml_elem_pretty;
}
}
}
if (zend_hash_find(Z_ARRVAL_P(output_opts), VERSION_KEY, VERSION_KEY_LEN + 1, (void**) &val) == SUCCESS) {
if (Z_TYPE_PP(val) == IS_STRING) {
options->b_auto_version = 0;
if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_XMLRPC)) {
options->xmlrpc_out.version = xmlrpc_version_1_0;
} else if (!strcmp(Z_STRVAL_PP(val), VERSION_VALUE_SIMPLE)) {
options->xmlrpc_out.version = xmlrpc_version_simple;
} else if (!strcmp((*val)->value.str.val, VERSION_VALUE_SOAP11)) {
options->xmlrpc_out.version = xmlrpc_version_soap_1_1;
} else {
options->b_auto_version = 1;
}
}
}
if (zend_hash_find(Z_ARRVAL_P(output_opts), ENCODING_KEY, ENCODING_KEY_LEN + 1, (void**)&val) == SUCCESS) {
if (Z_TYPE_PP(val) == IS_STRING) {
options->xmlrpc_out.xml_elem_opts.encoding = estrdup(Z_STRVAL_PP(val));
}
}
if (zend_hash_find(Z_ARRVAL_P(output_opts), ESCAPING_KEY, ESCAPING_KEY_LEN + 1, (void**)&val) == SUCCESS) {
if (Z_TYPE_PP(val) == IS_ARRAY) {
zval** iter_val;
zend_hash_internal_pointer_reset(Z_ARRVAL_PP(val));
options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_no_escaping;
while (1) {
if (zend_hash_get_current_data(Z_ARRVAL_PP(val), (void**)&iter_val) == SUCCESS) {
if (Z_TYPE_PP(iter_val) == IS_STRING && Z_STRVAL_PP(iter_val)) {
if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_CDATA)) {
options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_cdata_escaping;
} else if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_ASCII)) {
options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_ascii_escaping;
} else if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_NON_PRINT)) {
options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_non_print_escaping;
} else if (!strcmp(Z_STRVAL_PP(iter_val), ESCAPING_VALUE_MARKUP)) {
options->xmlrpc_out.xml_elem_opts.escaping |= xml_elem_markup_escaping;
}
}
} else {
break;
}
zend_hash_move_forward(Z_ARRVAL_PP(val));
}
} else if (Z_TYPE_PP(val) == IS_STRING) {
if (!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_CDATA)) {
options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_cdata_escaping;
} else if (!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_ASCII)) {
options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_ascii_escaping;
} else if (!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_NON_PRINT)) {
options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_non_print_escaping;
} else if (!strcmp(Z_STRVAL_PP(val), ESCAPING_VALUE_MARKUP)) {
options->xmlrpc_out.xml_elem_opts.escaping = xml_elem_markup_escaping;
}
}
}
}
}
}
static XMLRPC_VECTOR_TYPE determine_vector_type (HashTable *ht)
{
int bArray = 0, bStruct = 0, bMixed = 0;
unsigned long num_index, last_num = 0;
char* my_key;
zend_hash_internal_pointer_reset(ht);
while (1) {
int res = my_zend_hash_get_current_key(ht, &my_key, &num_index);
if (res == HASH_KEY_IS_LONG) {
if (bStruct) {
bMixed = 1;
break;
} else if (last_num > 0 && last_num != num_index-1) {
bStruct = 1;
break;
}
bArray = 1;
last_num = num_index;
} else if (res == HASH_KEY_NON_EXISTENT) {
break;
} else if (res == HASH_KEY_IS_STRING) {
if (bArray) {
bMixed = 1;
break;
}
bStruct = 1;
}
zend_hash_move_forward(ht);
}
return bMixed ? xmlrpc_vector_mixed : (bStruct ? xmlrpc_vector_struct : xmlrpc_vector_array);
}
static XMLRPC_VALUE PHP_to_XMLRPC_worker (const char* key, zval* in_val, int depth TSRMLS_DC)
{
XMLRPC_VALUE xReturn = NULL;
if (in_val) {
zval* val = NULL;
XMLRPC_VALUE_TYPE type = get_zval_xmlrpc_type(in_val, &val);
if (val) {
switch (type) {
case xmlrpc_base64:
if (Z_TYPE_P(val) == IS_NULL) {
xReturn = XMLRPC_CreateValueEmpty();
XMLRPC_SetValueID(xReturn, key, 0);
} else {
if (Z_TYPE_P(val) != IS_STRING) {
zval *newvalue;
ALLOC_INIT_ZVAL(newvalue);
MAKE_COPY_ZVAL(&val, newvalue);
convert_to_string(newvalue);
xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(newvalue), Z_STRLEN_P(newvalue));
zval_ptr_dtor(&newvalue);
} else {
xReturn = XMLRPC_CreateValueBase64(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
}
}
break;
case xmlrpc_datetime:
convert_to_string(val);
xReturn = XMLRPC_CreateValueDateTime_ISO8601(key, Z_STRVAL_P(val));
break;
case xmlrpc_boolean:
convert_to_boolean(val);
xReturn = XMLRPC_CreateValueBoolean(key, Z_LVAL_P(val));
break;
case xmlrpc_int:
convert_to_long(val);
xReturn = XMLRPC_CreateValueInt(key, Z_LVAL_P(val));
break;
case xmlrpc_double:
convert_to_double(val);
xReturn = XMLRPC_CreateValueDouble(key, Z_DVAL_P(val));
break;
case xmlrpc_string:
convert_to_string(val);
xReturn = XMLRPC_CreateValueString(key, Z_STRVAL_P(val), Z_STRLEN_P(val));
break;
case xmlrpc_vector:
{
unsigned long num_index;
zval** pIter;
char* my_key;
HashTable *ht = NULL;
zval *val_arr;
XMLRPC_VECTOR_TYPE vtype;
ht = HASH_OF(val);
if (ht && ht->nApplyCount > 1) {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "XML-RPC doesn't support circular references");
return NULL;
}
MAKE_STD_ZVAL(val_arr);
MAKE_COPY_ZVAL(&val, val_arr);
convert_to_array(val_arr);
vtype = determine_vector_type(Z_ARRVAL_P(val_arr));
xReturn = XMLRPC_CreateVector(key, vtype);
zend_hash_internal_pointer_reset(Z_ARRVAL_P(val_arr));
while(zend_hash_get_current_data(Z_ARRVAL_P(val_arr), (void**)&pIter) == SUCCESS) {
int res = my_zend_hash_get_current_key(Z_ARRVAL_P(val_arr), &my_key, &num_index);
switch (res) {
case HASH_KEY_NON_EXISTENT:
break;
case HASH_KEY_IS_STRING:
case HASH_KEY_IS_LONG:
ht = HASH_OF(*pIter);
if (ht) {
ht->nApplyCount++;
}
if (res == HASH_KEY_IS_LONG) {
char *num_str = NULL;
if (vtype != xmlrpc_vector_array) {
spprintf(&num_str, 0, "%ld", num_index);
}
XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(num_str, *pIter, depth++ TSRMLS_CC));
if (num_str) {
efree(num_str);
}
} else {
XMLRPC_AddValueToVector(xReturn, PHP_to_XMLRPC_worker(my_key, *pIter, depth++ TSRMLS_CC));
}
if (ht) {
ht->nApplyCount--;
}
break;
}
zend_hash_move_forward(Z_ARRVAL_P(val_arr));
}
zval_ptr_dtor(&val_arr);
}
break;
default:
break;
}
}
}
return xReturn;
}
static XMLRPC_VALUE PHP_to_XMLRPC(zval* root_val TSRMLS_DC)
{
return PHP_to_XMLRPC_worker(NULL, root_val, 0 TSRMLS_CC);
}
static zval* XMLRPC_to_PHP(XMLRPC_VALUE el)
{
zval* elem = NULL;
const char* pStr;
if (el) {
XMLRPC_VALUE_TYPE type = XMLRPC_GetValueType(el);
MAKE_STD_ZVAL(elem);
switch(type) {
case xmlrpc_empty:
Z_TYPE_P(elem) = IS_NULL;
break;
case xmlrpc_string:
pStr = XMLRPC_GetValueString(el);
if (pStr) {
Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
Z_TYPE_P(elem) = IS_STRING;
}
break;
case xmlrpc_int:
Z_LVAL_P(elem) = XMLRPC_GetValueInt(el);
Z_TYPE_P(elem) = IS_LONG;
break;
case xmlrpc_boolean:
Z_LVAL_P(elem) = XMLRPC_GetValueBoolean(el);
Z_TYPE_P(elem) = IS_BOOL;
break;
case xmlrpc_double:
Z_DVAL_P(elem) = XMLRPC_GetValueDouble(el);
Z_TYPE_P(elem) = IS_DOUBLE;
break;
case xmlrpc_datetime:
Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
Z_STRVAL_P(elem) = estrndup(XMLRPC_GetValueDateTime_ISO8601(el), Z_STRLEN_P(elem));
Z_TYPE_P(elem) = IS_STRING;
break;
case xmlrpc_base64:
pStr = XMLRPC_GetValueBase64(el);
if (pStr) {
Z_STRLEN_P(elem) = XMLRPC_GetValueStringLen(el);
Z_STRVAL_P(elem) = estrndup(pStr, Z_STRLEN_P(elem));
Z_TYPE_P(elem) = IS_STRING;
}
break;
case xmlrpc_vector:
array_init(elem);
{
XMLRPC_VALUE xIter = XMLRPC_VectorRewind(el);
while( xIter ) {
zval *val = XMLRPC_to_PHP(xIter);
if (val) {
add_zval(elem, XMLRPC_GetValueID(xIter), &val);
}
xIter = XMLRPC_VectorNext(el);
}
}
break;
default:
break;
}
set_zval_xmlrpc_type(elem, type);
}
return elem;
}
PHP_FUNCTION(xmlrpc_encode_request)
{
XMLRPC_REQUEST xRequest = NULL;
char *outBuf;
zval *vals, *out_opts = NULL;
char *method = NULL;
int method_len;
php_output_options out;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!z|a", &method, &method_len, &vals, &out_opts) == FAILURE) {
return;
}
set_output_options(&out, out_opts ? out_opts : 0);
if (return_value_used) {
xRequest = XMLRPC_RequestNew();
if (xRequest) {
XMLRPC_RequestSetOutputOptions(xRequest, &out.xmlrpc_out);
if (method == NULL) {
XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_response);
} else {
XMLRPC_RequestSetMethodName(xRequest, method);
XMLRPC_RequestSetRequestType(xRequest, xmlrpc_request_call);
}
if (Z_TYPE_P(vals) != IS_NULL) {
XMLRPC_RequestSetData(xRequest, PHP_to_XMLRPC(vals TSRMLS_CC));
}
outBuf = XMLRPC_REQUEST_ToXML(xRequest, 0);
if (outBuf) {
RETVAL_STRING(outBuf, 1);
free(outBuf);
}
XMLRPC_RequestFree(xRequest, 1);
}
}
if (strcmp(out.xmlrpc_out.xml_elem_opts.encoding, ENCODING_DEFAULT) != 0) {
efree((char *)out.xmlrpc_out.xml_elem_opts.encoding);
}
}
PHP_FUNCTION(xmlrpc_encode)
{
XMLRPC_VALUE xOut = NULL;
zval **arg1;
char *outBuf;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg1) == FAILURE) {
return;
}
if (return_value_used) {
xOut = PHP_to_XMLRPC(*arg1 TSRMLS_CC);
outBuf = XMLRPC_VALUE_ToXML(xOut, 0);
if (xOut) {
if (outBuf) {
RETVAL_STRING(outBuf, 1);
free(outBuf);
}
XMLRPC_CleanupValue(xOut);
}
}
}
zval* decode_request_worker(char *xml_in, int xml_in_len, char *encoding_in, zval* method_name_out)
{
zval* retval = NULL;
XMLRPC_REQUEST response;
STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS opts = {{0}};
const char *method_name;
opts.xml_elem_opts.encoding = encoding_in ? utf8_get_encoding_id_from_string(encoding_in) : ENCODING_DEFAULT;
response = XMLRPC_REQUEST_FromXML(xml_in, xml_in_len, &opts);
if (response) {
retval = XMLRPC_to_PHP(XMLRPC_RequestGetData(response));
if (XMLRPC_RequestGetRequestType(response) == xmlrpc_request_call) {
if (method_name_out) {
method_name = XMLRPC_RequestGetMethodName(response);
if (method_name) {
zval_dtor(method_name_out);
Z_TYPE_P(method_name_out) = IS_STRING;
Z_STRVAL_P(method_name_out) = estrdup(method_name);
Z_STRLEN_P(method_name_out) = strlen(Z_STRVAL_P(method_name_out));
} else if (retval) {
zval_ptr_dtor(&retval);
retval = NULL;
}
}
}
XMLRPC_RequestFree(response, 1);
}
return retval;
}
PHP_FUNCTION(xmlrpc_decode_request)
{
char *xml, *encoding = NULL;
zval **method;
int xml_len, encoding_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sZ|s", &xml, &xml_len, &method, &encoding, &encoding_len) == FAILURE) {
return;
}
if (return_value_used) {
zval* retval = decode_request_worker(xml, xml_len, encoding_len ? encoding : NULL, *method);
if (retval) {
*return_value = *retval;
FREE_ZVAL(retval);
}
}
}
PHP_FUNCTION(xmlrpc_decode)
{
char *arg1, *arg2 = NULL;
int arg1_len, arg2_len = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &arg1, &arg1_len, &arg2, &arg2_len) == FAILURE) {
return;
}
if (return_value_used) {
zval* retval = decode_request_worker(arg1, arg1_len, arg2_len ? arg2 : NULL, NULL);
if (retval) {
*return_value = *retval;
FREE_ZVAL(retval);
}
}
}
PHP_FUNCTION(xmlrpc_server_create)
{
if (zend_parse_parameters_none() == FAILURE) {
return;
}
if (return_value_used) {
zval *method_map, *introspection_map;
xmlrpc_server_data *server = emalloc(sizeof(xmlrpc_server_data));
MAKE_STD_ZVAL(method_map);
MAKE_STD_ZVAL(introspection_map);
array_init(method_map);
array_init(introspection_map);
server->method_map = method_map;
server->introspection_map = introspection_map;
server->server_ptr = XMLRPC_ServerCreate();
XMLRPC_ServerRegisterIntrospectionCallback(server->server_ptr, php_xmlrpc_introspection_callback);
ZEND_REGISTER_RESOURCE(return_value,server, le_xmlrpc_server);
}
}
PHP_FUNCTION(xmlrpc_server_destroy)
{
zval *arg1;
int bSuccess = FAILURE, type;
xmlrpc_server_data *server;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg1) == FAILURE) {
return;
}
server = zend_list_find(Z_LVAL_P(arg1), &type);
if (server && type == le_xmlrpc_server) {
bSuccess = zend_list_delete(Z_LVAL_P(arg1));
}
RETVAL_LONG(bSuccess == SUCCESS);
}
static XMLRPC_VALUE php_xmlrpc_callback(XMLRPC_SERVER server, XMLRPC_REQUEST xRequest, void* data)
{
xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
zval** php_function;
zval* xmlrpc_params;
zval* callback_params[3];
TSRMLS_FETCH();
zval_dtor(pData->xmlrpc_method);
zval_dtor(pData->return_data);
ZVAL_STRING(pData->xmlrpc_method, XMLRPC_RequestGetMethodName(xRequest), 1);
xmlrpc_params = XMLRPC_to_PHP(XMLRPC_RequestGetData(xRequest));
if(zend_hash_find(Z_ARRVAL_P(pData->server->method_map),
Z_STRVAL_P(pData->xmlrpc_method),
Z_STRLEN_P(pData->xmlrpc_method) + 1,
(void**)&php_function) == SUCCESS) {
pData->php_function = *php_function;
}
callback_params[0] = pData->xmlrpc_method;
callback_params[1] = xmlrpc_params;
callback_params[2] = pData->caller_params;
call_user_function(CG(function_table), NULL, pData->php_function, pData->return_data, 3, callback_params TSRMLS_CC);
pData->php_executed = 1;
zval_ptr_dtor(&xmlrpc_params);
return PHP_to_XMLRPC(pData->return_data TSRMLS_CC);
}
static void php_xmlrpc_introspection_callback(XMLRPC_SERVER server, void* data)
{
zval retval, **php_function;
zval *callback_params[1];
char *php_function_name;
xmlrpc_callback_data* pData = (xmlrpc_callback_data*)data;
TSRMLS_FETCH();
callback_params[0] = pData->caller_params;
zend_hash_internal_pointer_reset(Z_ARRVAL_P(pData->server->introspection_map));
while (1) {
if (zend_hash_get_current_data(Z_ARRVAL_P(pData->server->introspection_map), (void**)&php_function) == SUCCESS) {
if (zend_is_callable(*php_function, 0, &php_function_name TSRMLS_CC)) {
if (call_user_function(CG(function_table), NULL, *php_function, &retval, 1, callback_params TSRMLS_CC) == SUCCESS) {
XMLRPC_VALUE xData;
STRUCT_XMLRPC_ERROR err = {0};
convert_to_string(&retval);
xData = XMLRPC_IntrospectionCreateDescription(Z_STRVAL(retval), &err);
if (xData) {
if (!XMLRPC_ServerAddIntrospectionData(server, xData)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s(), improper element structure", php_function_name);
}
XMLRPC_CleanupValue(xData);
} else {
if (err.xml_elem_error.parser_code) {
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()",
err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error, php_function_name);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to add introspection data returned from %s()", php_function_name);
}
}
zval_dtor(&retval);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error calling user introspection callback: %s()", php_function_name);
}
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid callback '%s' passed", php_function_name);
}
efree(php_function_name);
} else {
break;
}
zend_hash_move_forward(Z_ARRVAL_P(pData->server->introspection_map));
}
zend_hash_clean(Z_ARRVAL_P(pData->server->introspection_map));
}
PHP_FUNCTION(xmlrpc_server_register_method)
{
char *method_key;
int method_key_len;
zval *handle, *method_name_save, **method_name;
int type;
xmlrpc_server_data* server;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsZ", &handle, &method_key, &method_key_len, &method_name) == FAILURE) {
return;
}
server = zend_list_find(Z_LVAL_P(handle), &type);
if (type == le_xmlrpc_server) {
if (XMLRPC_ServerRegisterMethod(server->server_ptr, method_key, php_xmlrpc_callback)) {
ALLOC_ZVAL(method_name_save);
MAKE_COPY_ZVAL(method_name, method_name_save);
add_zval(server->method_map, method_key, &method_name_save);
RETURN_BOOL(1);
}
}
RETURN_BOOL(0);
}
PHP_FUNCTION(xmlrpc_server_register_introspection_callback)
{
zval **method_name, *handle, *method_name_save;
int type;
xmlrpc_server_data* server;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rZ", &handle, &method_name) == FAILURE) {
return;
}
server = zend_list_find(Z_LVAL_P(handle), &type);
if (type == le_xmlrpc_server) {
ALLOC_ZVAL(method_name_save);
MAKE_COPY_ZVAL(method_name, method_name_save);
add_zval(server->introspection_map, NULL, &method_name_save);
RETURN_BOOL(1);
}
RETURN_BOOL(0);
}
PHP_FUNCTION(xmlrpc_server_call_method)
{
xmlrpc_callback_data data = {0};
XMLRPC_REQUEST xRequest;
STRUCT_XMLRPC_REQUEST_INPUT_OPTIONS input_opts;
xmlrpc_server_data* server;
zval **caller_params, *handle, *output_opts = NULL;
char *rawxml;
int rawxml_len, type;
php_output_options out;
int argc =ZEND_NUM_ARGS();
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsZ|a", &handle, &rawxml, &rawxml_len, &caller_params, &output_opts) != SUCCESS) {
return;
}
if (argc == 3) {
set_output_options(&out, NULL);
} else {
set_output_options(&out, output_opts);
}
server = zend_list_find(Z_LVAL_P(handle), &type);
if (type == le_xmlrpc_server) {
input_opts.xml_elem_opts.encoding = utf8_get_encoding_id_from_string(out.xmlrpc_out.xml_elem_opts.encoding);
xRequest = XMLRPC_REQUEST_FromXML(rawxml, rawxml_len, &input_opts);
if (xRequest) {
const char* methodname = XMLRPC_RequestGetMethodName(xRequest);
XMLRPC_VALUE xAnswer = NULL;
MAKE_STD_ZVAL(data.xmlrpc_method);
MAKE_STD_ZVAL(data.return_data);
Z_TYPE_P(data.return_data) = IS_NULL;
Z_TYPE_P(data.xmlrpc_method) = IS_NULL;
data.caller_params = *caller_params;
data.php_executed = 0;
data.server = server;
xAnswer = XMLRPC_ServerCallMethod(server->server_ptr, xRequest, &data);
if (xAnswer && out.b_php_out) {
zval_dtor(data.return_data);
FREE_ZVAL(data.return_data);
data.return_data = XMLRPC_to_PHP(xAnswer);
} else if (data.php_executed && !out.b_php_out && !xAnswer) {
xAnswer = PHP_to_XMLRPC(data.return_data TSRMLS_CC);
}
if (!out.b_php_out) {
XMLRPC_REQUEST xResponse = XMLRPC_RequestNew();
if (xResponse) {
char *outBuf = 0;
int buf_len = 0;
if (out.b_auto_version) {
XMLRPC_REQUEST_OUTPUT_OPTIONS opts = XMLRPC_RequestGetOutputOptions(xRequest);
if (opts) {
out.xmlrpc_out.version = opts->version;
}
}
XMLRPC_RequestSetOutputOptions(xResponse, &out.xmlrpc_out);
XMLRPC_RequestSetRequestType(xResponse, xmlrpc_request_response);
XMLRPC_RequestSetData(xResponse, xAnswer);
XMLRPC_RequestSetMethodName(xResponse, methodname);
outBuf = XMLRPC_REQUEST_ToXML(xResponse, &buf_len);
if (outBuf) {
RETVAL_STRINGL(outBuf, buf_len, 1);
free(outBuf);
}
XMLRPC_RequestFree(xResponse, 0);
}
} else {
*return_value = *data.return_data;
zval_copy_ctor(return_value);
}
zval_ptr_dtor(&data.xmlrpc_method);
zval_dtor(data.return_data);
FREE_ZVAL(data.return_data);
if (xAnswer) {
XMLRPC_CleanupValue(xAnswer);
}
XMLRPC_RequestFree(xRequest, 1);
}
}
}
PHP_FUNCTION(xmlrpc_server_add_introspection_data)
{
zval *handle, *desc;
int type;
xmlrpc_server_data* server;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra", &handle, &desc) == FAILURE) {
return;
}
server = zend_list_find(Z_LVAL_P(handle), &type);
if (type == le_xmlrpc_server) {
XMLRPC_VALUE xDesc = PHP_to_XMLRPC(desc TSRMLS_CC);
if (xDesc) {
int retval = XMLRPC_ServerAddIntrospectionData(server->server_ptr, xDesc);
XMLRPC_CleanupValue(xDesc);
RETURN_LONG(retval);
}
}
RETURN_LONG(0);
}
PHP_FUNCTION(xmlrpc_parse_method_descriptions)
{
zval *retval;
char *arg1;
int arg1_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &arg1, &arg1_len) == FAILURE) {
return;
}
if (return_value_used) {
STRUCT_XMLRPC_ERROR err = {0};
XMLRPC_VALUE xVal = XMLRPC_IntrospectionCreateDescription(arg1, &err);
if (xVal) {
retval = XMLRPC_to_PHP(xVal);
if (retval) {
RETVAL_ZVAL(retval, 1, 1);
}
XMLRPC_CleanupValue(xVal);
} else {
if (err.xml_elem_error.parser_code) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error: [line %ld, column %ld, message: %s] Unable to create introspection data",
err.xml_elem_error.column, err.xml_elem_error.line, err.xml_elem_error.parser_error);
} else {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid xml structure. Unable to create introspection data");
}
php_error_docref(NULL TSRMLS_CC, E_WARNING, "xml parse error. no method description created");
}
}
}
#define XMLRPC_TYPE_COUNT 9
#define XMLRPC_VECTOR_TYPE_COUNT 4
#define TYPE_STR_MAP_SIZE (XMLRPC_TYPE_COUNT + XMLRPC_VECTOR_TYPE_COUNT)
static const char** get_type_str_mapping(void)
{
static const char* str_mapping[TYPE_STR_MAP_SIZE];
static int first = 1;
if (first) {
str_mapping[xmlrpc_none] = "none";
str_mapping[xmlrpc_empty] = "empty";
str_mapping[xmlrpc_base64] = "base64";
str_mapping[xmlrpc_boolean] = "boolean";
str_mapping[xmlrpc_datetime] = "datetime";
str_mapping[xmlrpc_double] = "double";
str_mapping[xmlrpc_int] = "int";
str_mapping[xmlrpc_string] = "string";
str_mapping[xmlrpc_vector] = "vector";
str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_none] = "none";
str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_array] = "array";
str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_mixed] = "mixed";
str_mapping[XMLRPC_TYPE_COUNT + xmlrpc_vector_struct] = "struct";
first = 0;
}
return (const char**)str_mapping;
}
const char* xmlrpc_type_as_str(XMLRPC_VALUE_TYPE type, XMLRPC_VECTOR_TYPE vtype)
{
const char** str_mapping = get_type_str_mapping();
if (vtype == xmlrpc_vector_none) {
return str_mapping[type];
} else {
return str_mapping[XMLRPC_TYPE_COUNT + vtype];
}
}
XMLRPC_VALUE_TYPE xmlrpc_str_as_type(const char* str)
{
const char** str_mapping = get_type_str_mapping();
int i;
if (str) {
for (i = 0; i < XMLRPC_TYPE_COUNT; i++) {
if (!strcmp(str_mapping[i], str)) {
return (XMLRPC_VALUE_TYPE) i;
}
}
}
return xmlrpc_none;
}
XMLRPC_VECTOR_TYPE xmlrpc_str_as_vector_type(const char* str)
{
const char** str_mapping = get_type_str_mapping();
int i;
if (str) {
for (i = XMLRPC_TYPE_COUNT; i < TYPE_STR_MAP_SIZE; i++) {
if (!strcmp(str_mapping[i], str)) {
return (XMLRPC_VECTOR_TYPE) (i - XMLRPC_TYPE_COUNT);
}
}
}
return xmlrpc_none;
}
int set_zval_xmlrpc_type(zval* value, XMLRPC_VALUE_TYPE newtype)
{
int bSuccess = FAILURE;
TSRMLS_FETCH();
if (Z_TYPE_P(value) == IS_STRING) {
if (newtype == xmlrpc_base64 || newtype == xmlrpc_datetime) {
const char* typestr = xmlrpc_type_as_str(newtype, xmlrpc_vector_none);
zval* type;
MAKE_STD_ZVAL(type);
Z_TYPE_P(type) = IS_STRING;
Z_STRVAL_P(type) = estrdup(typestr);
Z_STRLEN_P(type) = strlen(typestr);
if (newtype == xmlrpc_datetime) {
XMLRPC_VALUE v = XMLRPC_CreateValueDateTime_ISO8601(NULL, value->value.str.val);
if (v) {
time_t timestamp = (time_t) php_parse_date((char *)XMLRPC_GetValueDateTime_ISO8601(v), NULL);
if (timestamp != -1) {
zval* ztimestamp;
MAKE_STD_ZVAL(ztimestamp);
ztimestamp->type = IS_LONG;
ztimestamp->value.lval = timestamp;
convert_to_object(value);
if (SUCCESS == zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL)) {
bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_VALUE_TS_ATTR, sizeof(OBJECT_VALUE_TS_ATTR), (void *) &ztimestamp, sizeof(zval *), NULL);
}
} else {
zval_ptr_dtor(&type);
}
XMLRPC_CleanupValue(v);
} else {
zval_ptr_dtor(&type);
}
} else {
convert_to_object(value);
bSuccess = zend_hash_update(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void *) &type, sizeof(zval *), NULL);
}
}
}
return bSuccess;
}
XMLRPC_VALUE_TYPE get_zval_xmlrpc_type(zval* value, zval** newvalue)
{
XMLRPC_VALUE_TYPE type = xmlrpc_none;
TSRMLS_FETCH();
if (value) {
switch (Z_TYPE_P(value)) {
case IS_NULL:
type = xmlrpc_base64;
break;
#ifndef BOOL_AS_LONG
case IS_BOOL:
type = xmlrpc_boolean;
break;
#else
case IS_BOOL:
#endif
case IS_LONG:
case IS_RESOURCE:
type = xmlrpc_int;
break;
case IS_DOUBLE:
type = xmlrpc_double;
break;
case IS_CONSTANT:
type = xmlrpc_string;
break;
case IS_STRING:
type = xmlrpc_string;
break;
case IS_ARRAY:
type = xmlrpc_vector;
break;
case IS_OBJECT:
{
zval** attr;
type = xmlrpc_vector;
if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_TYPE_ATTR, sizeof(OBJECT_TYPE_ATTR), (void**) &attr) == SUCCESS) {
if (Z_TYPE_PP(attr) == IS_STRING) {
type = xmlrpc_str_as_type(Z_STRVAL_PP(attr));
}
}
break;
}
}
if (newvalue) {
zval** val;
if ((type == xmlrpc_base64 && Z_TYPE_P(value) == IS_OBJECT) || type == xmlrpc_datetime) {
if (zend_hash_find(Z_OBJPROP_P(value), OBJECT_VALUE_ATTR, sizeof(OBJECT_VALUE_ATTR), (void**) &val) == SUCCESS) {
*newvalue = *val;
}
} else {
*newvalue = value;
}
}
}
return type;
}
PHP_FUNCTION(xmlrpc_set_type)
{
zval **arg;
char *type;
int type_len;
XMLRPC_VALUE_TYPE vtype;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Zs", &arg, &type, &type_len) == FAILURE) {
return;
}
vtype = xmlrpc_str_as_type(type);
if (vtype != xmlrpc_none) {
if (set_zval_xmlrpc_type(*arg, vtype) == SUCCESS) {
RETURN_TRUE;
}
} else {
zend_error(E_WARNING,"invalid type '%s' passed to xmlrpc_set_type()", type);
}
RETURN_FALSE;
}
PHP_FUNCTION(xmlrpc_get_type)
{
zval **arg;
XMLRPC_VALUE_TYPE type;
XMLRPC_VECTOR_TYPE vtype = xmlrpc_vector_none;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z", &arg) == FAILURE) {
return;
}
type = get_zval_xmlrpc_type(*arg, 0);
if (type == xmlrpc_vector) {
vtype = determine_vector_type((Z_TYPE_PP(arg) == IS_OBJECT) ? Z_OBJPROP_PP(arg) : Z_ARRVAL_PP(arg));
}
RETURN_STRING((char*) xmlrpc_type_as_str(type, vtype), 1);
}
PHP_FUNCTION(xmlrpc_is_fault)
{
zval *arg, **val;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &arg) == FAILURE) {
return;
}
if (zend_hash_find(Z_ARRVAL_P(arg), FAULT_CODE, FAULT_CODE_LEN + 1, (void**) &val) == SUCCESS &&
zend_hash_find(Z_ARRVAL_P(arg), FAULT_STRING, FAULT_STRING_LEN + 1, (void**) &val) == SUCCESS) {
RETURN_TRUE;
}
RETURN_FALSE;
}