diff --git a/ext/soap/soap.c b/ext/soap/soap.c
index 02ff10ae4d93a..710d72301b637 100644
--- a/ext/soap/soap.c
+++ b/ext/soap/soap.c
@@ -58,7 +58,7 @@ static sdlParamPtr get_param(sdlFunctionPtr function, const char *param_name, ze
static sdlFunctionPtr get_function(sdlPtr sdl, const char *function_name, size_t function_name_length);
static sdlFunctionPtr get_doc_function(sdlPtr sdl, xmlNodePtr params);
-static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, const char* actor, zval *function_name, uint32_t *num_params, zval **parameters, int *version, soapHeader **headers);
+static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, const char* actor, const char *soap_action, zval *function_name, uint32_t *num_params, zval **parameters, int *version, soapHeader **headers);
static xmlDocPtr serialize_response_call(sdlFunctionPtr function, const char *function_name, const char *uri,zval *ret, soapHeader *headers, int version);
static xmlDocPtr serialize_function_call(zval *this_ptr, sdlFunctionPtr function, const char *function_name, const char *uri, zval *arguments, uint32_t arg_count, int version, HashTable *soap_headers);
static xmlNodePtr serialize_parameter(sdlParamPtr param,zval *param_val, uint32_t index,const char *name, int style, xmlNodePtr parent);
@@ -1273,6 +1273,7 @@ PHP_METHOD(SoapServer, handle)
HashTable *old_class_map, *old_typemap;
int old_features;
zval tmp_soap;
+ const char *soap_action = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|s!", &arg, &arg_len) == FAILURE) {
RETURN_THROWS();
@@ -1341,42 +1342,44 @@ PHP_METHOD(SoapServer, handle)
if (!arg) {
if (SG(request_info).request_body && 0 == php_stream_rewind(SG(request_info).request_body)) {
- zval *server_vars, *encoding;
+ zval *server_vars, *encoding, *soap_action_z;
php_stream_filter *zf = NULL;
zend_string *server = ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER);
zend_is_auto_global(server);
- if ((server_vars = zend_hash_find(&EG(symbol_table), server)) != NULL &&
- Z_TYPE_P(server_vars) == IS_ARRAY &&
- (encoding = zend_hash_str_find(Z_ARRVAL_P(server_vars), "HTTP_CONTENT_ENCODING", sizeof("HTTP_CONTENT_ENCODING")-1)) != NULL &&
- Z_TYPE_P(encoding) == IS_STRING) {
-
- if (zend_string_equals_literal(Z_STR_P(encoding), "gzip")
- || zend_string_equals_literal(Z_STR_P(encoding), "x-gzip")
- || zend_string_equals_literal(Z_STR_P(encoding), "deflate")
- ) {
- zval filter_params;
-
- array_init_size(&filter_params, 1);
- add_assoc_long_ex(&filter_params, "window", sizeof("window")-1, 0x2f); /* ANY WBITS */
-
- zf = php_stream_filter_create("zlib.inflate", &filter_params, 0);
- zend_array_destroy(Z_ARR(filter_params));
-
- if (zf) {
- php_stream_filter_append(&SG(request_info).request_body->readfilters, zf);
+ if ((server_vars = zend_hash_find(&EG(symbol_table), server)) != NULL && Z_TYPE_P(server_vars) == IS_ARRAY) {
+ if ((encoding = zend_hash_str_find(Z_ARRVAL_P(server_vars), "HTTP_CONTENT_ENCODING", sizeof("HTTP_CONTENT_ENCODING")-1)) != NULL && Z_TYPE_P(encoding) == IS_STRING) {
+ if (zend_string_equals_literal(Z_STR_P(encoding), "gzip")
+ || zend_string_equals_literal(Z_STR_P(encoding), "x-gzip")
+ || zend_string_equals_literal(Z_STR_P(encoding), "deflate")
+ ) {
+ zval filter_params;
+
+ array_init_size(&filter_params, 1);
+ add_assoc_long_ex(&filter_params, "window", sizeof("window")-1, 0x2f); /* ANY WBITS */
+
+ zf = php_stream_filter_create("zlib.inflate", &filter_params, 0);
+ zend_array_destroy(Z_ARR(filter_params));
+
+ if (zf) {
+ php_stream_filter_append(&SG(request_info).request_body->readfilters, zf);
+ } else {
+ php_error_docref(NULL, E_WARNING,"Can't uncompress compressed request");
+ SOAP_SERVER_END_CODE();
+ return;
+ }
} else {
- php_error_docref(NULL, E_WARNING,"Can't uncompress compressed request");
+ php_error_docref(NULL, E_WARNING,"Request is compressed with unknown compression '%s'",Z_STRVAL_P(encoding));
SOAP_SERVER_END_CODE();
return;
}
- } else {
- php_error_docref(NULL, E_WARNING,"Request is compressed with unknown compression '%s'",Z_STRVAL_P(encoding));
- SOAP_SERVER_END_CODE();
- return;
}
}
+ if ((soap_action_z = zend_hash_str_find(Z_ARRVAL_P(server_vars), ZEND_STRL("HTTP_SOAPACTION"))) != NULL && Z_TYPE_P(soap_action_z) == IS_STRING) {
+ soap_action = Z_STRVAL_P(soap_action_z);
+ }
+
doc_request = soap_xmlParseFile("php://input");
if (zf) {
@@ -1420,7 +1423,7 @@ PHP_METHOD(SoapServer, handle)
old_soap_version = SOAP_GLOBAL(soap_version);
zend_try {
- function = deserialize_function_call(service->sdl, doc_request, service->actor, &function_name, &num_params, ¶ms, &soap_version, &soap_headers);
+ function = deserialize_function_call(service->sdl, doc_request, service->actor, soap_action, &function_name, &num_params, ¶ms, &soap_version, &soap_headers);
} zend_catch {
/* Avoid leaking persistent memory */
xmlFreeDoc(doc_request);
@@ -3060,6 +3063,8 @@ static void deserialize_parameters(xmlNodePtr params, sdlFunctionPtr function, u
}
/* }}} */
+/* This function attempts to find the right function name based on the first node's name in the soap body.
+ * If that doesn't work it falls back to get_doc_function(). */
static sdlFunctionPtr find_function(sdlPtr sdl, xmlNodePtr func, zval* function_name) /* {{{ */
{
sdlFunctionPtr function;
@@ -3090,6 +3095,37 @@ static sdlFunctionPtr find_function(sdlPtr sdl, xmlNodePtr func, zval* function_
}
/* }}} */
+static sdlFunctionPtr find_function_using_soap_action(const sdl *sdl, const char *soap_action, zval* function_name)
+{
+ if (!sdl) {
+ return NULL;
+ }
+
+ /* The soap action may be a http-quoted string, in which case we're removing the quotes here. */
+ size_t soap_action_length = strlen(soap_action);
+ if (soap_action[0] == '"') {
+ if (soap_action_length < 2 || soap_action[soap_action_length - 1] != '"') {
+ return NULL;
+ }
+ soap_action++;
+ soap_action_length -= 2;
+ }
+
+ /* TODO: This may depend on a particular target namespace, in which case this won't find a match when multiple different
+ * target namespaces are used until #45282 is resolved. */
+ sdlFunctionPtr function;
+ ZEND_HASH_FOREACH_PTR(&sdl->functions, function) {
+ if (function->binding && function->binding->bindingType == BINDING_SOAP) {
+ sdlSoapBindingFunctionPtr fnb = function->bindingAttributes;
+ if (fnb && fnb->soapAction && strncmp(fnb->soapAction, soap_action, soap_action_length) == 0 && fnb->soapAction[soap_action_length] == '\0') {
+ ZVAL_STRING(function_name, function->functionName);
+ return function;
+ }
+ }
+ } ZEND_HASH_FOREACH_END();
+ return NULL;
+}
+
static xmlNodePtr get_envelope(xmlNodePtr trav, int *version, char **envelope_ns) {
while (trav != NULL) {
if (trav->type == XML_ELEMENT_NODE) {
@@ -3115,12 +3151,12 @@ static xmlNodePtr get_envelope(xmlNodePtr trav, int *version, char **envelope_ns
return NULL;
}
-static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, const char* actor, zval *function_name, uint32_t *num_params, zval **parameters, int *version, soapHeader **headers) /* {{{ */
+static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, const char* actor, const char *soap_action, zval *function_name, uint32_t *num_params, zval **parameters, int *version, soapHeader **headers) /* {{{ */
{
char* envelope_ns = NULL;
xmlNodePtr trav,env,head,body,func;
xmlAttrPtr attr;
- sdlFunctionPtr function;
+ sdlFunctionPtr function = NULL;
encode_reset_ns();
@@ -3204,12 +3240,17 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c
}
trav = trav->next;
}
+ if (soap_action) {
+ function = find_function_using_soap_action(sdl, soap_action, function_name);
+ }
if (func == NULL) {
- function = get_doc_function(sdl, NULL);
- if (function != NULL) {
- ZVAL_STRING(function_name, (char *)function->functionName);
- } else {
- soap_server_fault("Client", "looks like we got \"Body\" without function call", NULL, NULL, NULL);
+ if (!function) {
+ function = get_doc_function(sdl, NULL);
+ if (function) {
+ ZVAL_STRING(function_name, (char *)function->functionName);
+ } else {
+ soap_server_fault("Client", "looks like we got \"Body\" without function call", NULL, NULL, NULL);
+ }
}
} else {
if (*version == SOAP_1_1) {
@@ -3223,7 +3264,9 @@ static sdlFunctionPtr deserialize_function_call(sdlPtr sdl, xmlDocPtr request, c
soap_server_fault("DataEncodingUnknown","Unknown Data Encoding Style", NULL, NULL, NULL);
}
}
- function = find_function(sdl, func, function_name);
+ if (!function) {
+ function = find_function(sdl, func, function_name);
+ }
if (sdl != NULL && function == NULL) {
if (*version == SOAP_1_2) {
soap_server_fault("rpc:ProcedureNotPresent","Procedure not present", NULL, NULL, NULL);
@@ -4187,6 +4230,10 @@ static sdlFunctionPtr get_function(sdlPtr sdl, const char *function_name, size_t
}
/* }}} */
+/* This function tries to find the function that matches the given parameters.
+ * If params is NULL, it will return the first function that has no parameters.
+ * If params is not NULL, it will return the first function that has the same
+ * parameters as the given XML node. */
static sdlFunctionPtr get_doc_function(sdlPtr sdl, xmlNodePtr params) /* {{{ */
{
if (sdl) {
diff --git a/ext/soap/tests/bugs/bug49169.phpt b/ext/soap/tests/bugs/bug49169.phpt
new file mode 100644
index 0000000000000..5693eb2f751c2
--- /dev/null
+++ b/ext/soap/tests/bugs/bug49169.phpt
@@ -0,0 +1,44 @@
+--TEST--
+Bug #49169 (SoapServer calls wrong function, although "SOAP action" header is correct)
+--EXTENSIONS--
+soap
+--INI--
+soap.wsdl_cache_enabled=0
+--SKIPIF--
+
+--POST--
+
+
+ hello
+
+
+--FILE--
+addfunction("test");
+$server->addfunction("test2");
+$_SERVER["HTTP_SOAPACTION"] = "#test";
+$server->handle();
+$_SERVER["HTTP_SOAPACTION"] = "#test2";
+$server->handle();
+?>
+--EXPECT--
+
+olleh
+
+5
diff --git a/ext/soap/tests/bugs/bug49169.wsdl b/ext/soap/tests/bugs/bug49169.wsdl
new file mode 100644
index 0000000000000..27cd5858ad4f2
--- /dev/null
+++ b/ext/soap/tests/bugs/bug49169.wsdl
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+