mirror of
https://github.com/php/php-src.git
synced 2025-08-19 17:04:47 +02:00
- Turns out the external entity loader is not a per-thread global but a true
global. Changed code accordingly; however, applications that embed PHP and also use libxml2 may be affected negatively.
This commit is contained in:
parent
88e36378ff
commit
6affeb6b60
2 changed files with 131 additions and 130 deletions
|
@ -55,6 +55,7 @@
|
||||||
/* a true global for initialization */
|
/* a true global for initialization */
|
||||||
static int _php_libxml_initialized = 0;
|
static int _php_libxml_initialized = 0;
|
||||||
static int _php_libxml_per_request_initialization = 1;
|
static int _php_libxml_per_request_initialization = 1;
|
||||||
|
static xmlExternalEntityLoader _php_libxml_default_entity_loader;
|
||||||
|
|
||||||
typedef struct _php_libxml_func_handler {
|
typedef struct _php_libxml_func_handler {
|
||||||
php_libxml_export_node export_func;
|
php_libxml_export_node export_func;
|
||||||
|
@ -268,7 +269,6 @@ static PHP_GINIT_FUNCTION(libxml)
|
||||||
libxml_globals->stream_context = NULL;
|
libxml_globals->stream_context = NULL;
|
||||||
libxml_globals->error_buffer.c = NULL;
|
libxml_globals->error_buffer.c = NULL;
|
||||||
libxml_globals->error_list = NULL;
|
libxml_globals->error_list = NULL;
|
||||||
libxml_globals->defaultEntityLoader = NULL;
|
|
||||||
libxml_globals->entity_loader.fci.size = 0;
|
libxml_globals->entity_loader.fci.size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,6 +549,131 @@ static void php_libxml_internal_error_handler(int error_type, void *ctx, const c
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,
|
||||||
|
const char *ID, xmlParserCtxtPtr context)
|
||||||
|
{
|
||||||
|
xmlParserInputPtr ret = NULL;
|
||||||
|
const char *resource = NULL;
|
||||||
|
zval *public = NULL,
|
||||||
|
*system = NULL,
|
||||||
|
*ctxzv = NULL,
|
||||||
|
**params[] = {&public, &system, &ctxzv},
|
||||||
|
*retval_ptr = NULL;
|
||||||
|
int retval;
|
||||||
|
zend_fcall_info *fci;
|
||||||
|
TSRMLS_FETCH();
|
||||||
|
|
||||||
|
fci = &LIBXML(entity_loader).fci;
|
||||||
|
|
||||||
|
if (fci->size == 0) {
|
||||||
|
/* no custom user-land callback set up; delegate to original loader */
|
||||||
|
return _php_libxml_default_entity_loader(URL, ID, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
ALLOC_INIT_ZVAL(public);
|
||||||
|
if (ID != NULL) {
|
||||||
|
ZVAL_STRING(public, ID, 1);
|
||||||
|
}
|
||||||
|
ALLOC_INIT_ZVAL(system);
|
||||||
|
if (URL != NULL) {
|
||||||
|
ZVAL_STRING(system, URL, 1);
|
||||||
|
}
|
||||||
|
MAKE_STD_ZVAL(ctxzv);
|
||||||
|
array_init_size(ctxzv, 4);
|
||||||
|
|
||||||
|
#define ADD_NULL_OR_STRING_KEY(memb) \
|
||||||
|
if (context->memb == NULL) { \
|
||||||
|
add_assoc_null_ex(ctxzv, #memb, sizeof(#memb)); \
|
||||||
|
} else { \
|
||||||
|
add_assoc_string_ex(ctxzv, #memb, sizeof(#memb), \
|
||||||
|
(char *)context->memb, 1); \
|
||||||
|
}
|
||||||
|
|
||||||
|
ADD_NULL_OR_STRING_KEY(directory)
|
||||||
|
ADD_NULL_OR_STRING_KEY(intSubName)
|
||||||
|
ADD_NULL_OR_STRING_KEY(extSubURI)
|
||||||
|
ADD_NULL_OR_STRING_KEY(extSubSystem)
|
||||||
|
|
||||||
|
#undef ADD_NULL_OR_STRING_KEY
|
||||||
|
|
||||||
|
fci->retval_ptr_ptr = &retval_ptr;
|
||||||
|
fci->params = params;
|
||||||
|
fci->param_count = sizeof(params)/sizeof(*params);
|
||||||
|
fci->no_separation = 1;
|
||||||
|
|
||||||
|
retval = zend_call_function(fci, &LIBXML(entity_loader).fcc TSRMLS_CC);
|
||||||
|
if (retval != SUCCESS || fci->retval_ptr_ptr == NULL) {
|
||||||
|
php_libxml_ctx_error(context,
|
||||||
|
"Call to user entity loader callback '%s' has failed",
|
||||||
|
fci->function_name);
|
||||||
|
} else {
|
||||||
|
retval_ptr = *fci->retval_ptr_ptr;
|
||||||
|
if (retval_ptr == NULL) {
|
||||||
|
php_libxml_ctx_error(context,
|
||||||
|
"Call to user entity loader callback '%s' has failed; "
|
||||||
|
"probably it has thrown an exception",
|
||||||
|
fci->function_name);
|
||||||
|
} else if (Z_TYPE_P(retval_ptr) == IS_STRING) {
|
||||||
|
is_string:
|
||||||
|
resource = Z_STRVAL_P(retval_ptr);
|
||||||
|
} else if (Z_TYPE_P(retval_ptr) == IS_RESOURCE) {
|
||||||
|
php_stream *stream;
|
||||||
|
php_stream_from_zval_no_verify(stream, &retval_ptr);
|
||||||
|
if (stream == NULL) {
|
||||||
|
php_libxml_ctx_error(context,
|
||||||
|
"The user entity loader callback '%s' has returned a "
|
||||||
|
"resource, but it is not a stream",
|
||||||
|
fci->function_name);
|
||||||
|
} else {
|
||||||
|
/* TODO: allow storing the encoding in the stream context? */
|
||||||
|
xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
|
||||||
|
xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
|
||||||
|
if (pib == NULL) {
|
||||||
|
php_libxml_ctx_error(context, "Could not allocate parser "
|
||||||
|
"input buffer");
|
||||||
|
} else {
|
||||||
|
/* make stream not being closed when the zval is freed */
|
||||||
|
zend_list_addref(stream->rsrc_id);
|
||||||
|
pib->context = stream;
|
||||||
|
pib->readcallback = php_libxml_streams_IO_read;
|
||||||
|
pib->closecallback = php_libxml_streams_IO_close;
|
||||||
|
|
||||||
|
ret = xmlNewIOInputStream(context, pib, enc);
|
||||||
|
if (ret == NULL) {
|
||||||
|
xmlFreeParserInputBuffer(pib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (Z_TYPE_P(retval_ptr) != IS_NULL) {
|
||||||
|
/* retval not string nor resource nor null; convert to string */
|
||||||
|
SEPARATE_ZVAL(&retval_ptr);
|
||||||
|
convert_to_string(retval_ptr);
|
||||||
|
goto is_string;
|
||||||
|
} /* else is null; don't try anything */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == NULL) {
|
||||||
|
if (resource == NULL) {
|
||||||
|
if (ID == NULL) {
|
||||||
|
ID = "NULL";
|
||||||
|
}
|
||||||
|
php_libxml_ctx_error(context,
|
||||||
|
"Failed to load external entity \"%s\"\n", ID);
|
||||||
|
} else {
|
||||||
|
/* we got the resource in the form of a string; open it */
|
||||||
|
ret = xmlNewInputFromFile(context, resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zval_ptr_dtor(&public);
|
||||||
|
zval_ptr_dtor(&system);
|
||||||
|
zval_ptr_dtor(&ctxzv);
|
||||||
|
if (retval_ptr != NULL) {
|
||||||
|
zval_ptr_dtor(&retval_ptr);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...)
|
PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
|
@ -586,6 +711,9 @@ PHP_LIBXML_API void php_libxml_initialize(void)
|
||||||
if (!_php_libxml_initialized) {
|
if (!_php_libxml_initialized) {
|
||||||
/* we should be the only one's to ever init!! */
|
/* we should be the only one's to ever init!! */
|
||||||
xmlInitParser();
|
xmlInitParser();
|
||||||
|
|
||||||
|
_php_libxml_default_entity_loader = xmlGetExternalEntityLoader();
|
||||||
|
xmlSetExternalEntityLoader(_php_libxml_external_entity_loader);
|
||||||
|
|
||||||
zend_hash_init(&php_libxml_exports, 0, NULL, NULL, 1);
|
zend_hash_init(&php_libxml_exports, 0, NULL, NULL, 1);
|
||||||
|
|
||||||
|
@ -926,126 +1054,6 @@ static PHP_FUNCTION(libxml_disable_entity_loader)
|
||||||
}
|
}
|
||||||
/* }}} */
|
/* }}} */
|
||||||
|
|
||||||
static xmlParserInputPtr _php_libxml_user_entity_loader(const char *URL,
|
|
||||||
const char *ID, xmlParserCtxtPtr context)
|
|
||||||
{
|
|
||||||
xmlParserInputPtr ret = NULL;
|
|
||||||
const char *resource = NULL;
|
|
||||||
zval *public = NULL,
|
|
||||||
*system = NULL,
|
|
||||||
*ctxzv = NULL,
|
|
||||||
**params[] = {&public, &system, &ctxzv},
|
|
||||||
*retval_ptr = NULL;
|
|
||||||
int retval;
|
|
||||||
zend_fcall_info *fci;
|
|
||||||
TSRMLS_FETCH();
|
|
||||||
|
|
||||||
fci = &LIBXML(entity_loader).fci;
|
|
||||||
|
|
||||||
ALLOC_INIT_ZVAL(public);
|
|
||||||
if (ID != NULL) {
|
|
||||||
ZVAL_STRING(public, ID, 1);
|
|
||||||
}
|
|
||||||
ALLOC_INIT_ZVAL(system);
|
|
||||||
if (URL != NULL) {
|
|
||||||
ZVAL_STRING(system, URL, 1);
|
|
||||||
}
|
|
||||||
MAKE_STD_ZVAL(ctxzv);
|
|
||||||
array_init_size(ctxzv, 4);
|
|
||||||
|
|
||||||
#define ADD_NULL_OR_STRING_KEY(memb) \
|
|
||||||
if (context->memb == NULL) { \
|
|
||||||
add_assoc_null_ex(ctxzv, #memb, sizeof(#memb)); \
|
|
||||||
} else { \
|
|
||||||
add_assoc_string_ex(ctxzv, #memb, sizeof(#memb), \
|
|
||||||
(char *)context->memb, 1); \
|
|
||||||
}
|
|
||||||
|
|
||||||
ADD_NULL_OR_STRING_KEY(directory)
|
|
||||||
ADD_NULL_OR_STRING_KEY(intSubName)
|
|
||||||
ADD_NULL_OR_STRING_KEY(extSubURI)
|
|
||||||
ADD_NULL_OR_STRING_KEY(extSubSystem)
|
|
||||||
|
|
||||||
#undef ADD_NULL_OR_STRING_KEY
|
|
||||||
|
|
||||||
fci->retval_ptr_ptr = &retval_ptr;
|
|
||||||
fci->params = params;
|
|
||||||
fci->param_count = sizeof(params)/sizeof(*params);
|
|
||||||
fci->no_separation = 1;
|
|
||||||
|
|
||||||
retval = zend_call_function(fci, &LIBXML(entity_loader).fcc TSRMLS_CC);
|
|
||||||
if (retval != SUCCESS || fci->retval_ptr_ptr == NULL) {
|
|
||||||
php_libxml_ctx_error(context,
|
|
||||||
"Call to user entity loader callback '%s' has failed",
|
|
||||||
fci->function_name);
|
|
||||||
} else {
|
|
||||||
retval_ptr = *fci->retval_ptr_ptr;
|
|
||||||
if (retval_ptr == NULL) {
|
|
||||||
php_libxml_ctx_error(context,
|
|
||||||
"Call to user entity loader callback '%s' has failed; "
|
|
||||||
"probably it has thrown an exception",
|
|
||||||
fci->function_name);
|
|
||||||
} else if (Z_TYPE_P(retval_ptr) == IS_STRING) {
|
|
||||||
is_string:
|
|
||||||
resource = Z_STRVAL_P(retval_ptr);
|
|
||||||
} else if (Z_TYPE_P(retval_ptr) == IS_RESOURCE) {
|
|
||||||
php_stream *stream;
|
|
||||||
php_stream_from_zval_no_verify(stream, &retval_ptr);
|
|
||||||
if (stream == NULL) {
|
|
||||||
php_libxml_ctx_error(context,
|
|
||||||
"The user entity loader callback '%s' has returned a "
|
|
||||||
"resource, but it is not a stream",
|
|
||||||
fci->function_name);
|
|
||||||
} else {
|
|
||||||
/* TODO: allow storing the encoding in the stream context? */
|
|
||||||
xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
|
|
||||||
xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
|
|
||||||
if (pib == NULL) {
|
|
||||||
php_libxml_ctx_error(context, "Could not allocate parser "
|
|
||||||
"input buffer");
|
|
||||||
} else {
|
|
||||||
/* make stream not being closed when the zval is freed */
|
|
||||||
zend_list_addref(stream->rsrc_id);
|
|
||||||
pib->context = stream;
|
|
||||||
pib->readcallback = php_libxml_streams_IO_read;
|
|
||||||
pib->closecallback = php_libxml_streams_IO_close;
|
|
||||||
|
|
||||||
ret = xmlNewIOInputStream(context, pib, enc);
|
|
||||||
if (ret == NULL) {
|
|
||||||
xmlFreeParserInputBuffer(pib);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (Z_TYPE_P(retval_ptr) != IS_NULL) {
|
|
||||||
/* retval not string nor resource nor null; convert to string */
|
|
||||||
SEPARATE_ZVAL(&retval_ptr);
|
|
||||||
convert_to_string(retval_ptr);
|
|
||||||
goto is_string;
|
|
||||||
} /* else is null; don't try anything */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == NULL) {
|
|
||||||
if (resource == NULL) {
|
|
||||||
if (ID == NULL) {
|
|
||||||
ID = "NULL";
|
|
||||||
}
|
|
||||||
php_libxml_ctx_error(context,
|
|
||||||
"Failed to load external entity \"%s\"\n", ID);
|
|
||||||
} else {
|
|
||||||
/* we got the resource in the form of a string; open it */
|
|
||||||
ret = xmlNewInputFromFile(context, resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
zval_ptr_dtor(&public);
|
|
||||||
zval_ptr_dtor(&system);
|
|
||||||
zval_ptr_dtor(&ctxzv);
|
|
||||||
if (retval_ptr != NULL) {
|
|
||||||
zval_ptr_dtor(&retval_ptr);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
|
/* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
|
||||||
Changes the default external entity loader */
|
Changes the default external entity loader */
|
||||||
static PHP_FUNCTION(libxml_set_external_entity_loader)
|
static PHP_FUNCTION(libxml_set_external_entity_loader)
|
||||||
|
@ -1057,21 +1065,15 @@ static PHP_FUNCTION(libxml_set_external_entity_loader)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_php_libxml_destroy_fci(&LIBXML(entity_loader).fci);
|
||||||
|
|
||||||
if (fci.size > 0) { /* argument not null */
|
if (fci.size > 0) { /* argument not null */
|
||||||
/* save for later invocations with NULL */
|
|
||||||
if (LIBXML(defaultEntityLoader) == NULL) {
|
|
||||||
LIBXML(defaultEntityLoader) = xmlGetExternalEntityLoader();
|
|
||||||
}
|
|
||||||
_php_libxml_destroy_fci(&LIBXML(entity_loader).fci);
|
|
||||||
LIBXML(entity_loader).fci = fci;
|
LIBXML(entity_loader).fci = fci;
|
||||||
Z_ADDREF_P(fci.function_name);
|
Z_ADDREF_P(fci.function_name);
|
||||||
if (fci.object_ptr != NULL) {
|
if (fci.object_ptr != NULL) {
|
||||||
Z_ADDREF_P(fci.object_ptr);
|
Z_ADDREF_P(fci.object_ptr);
|
||||||
}
|
}
|
||||||
LIBXML(entity_loader).fcc = fcc;
|
LIBXML(entity_loader).fcc = fcc;
|
||||||
xmlSetExternalEntityLoader(_php_libxml_user_entity_loader);
|
|
||||||
} else {
|
|
||||||
xmlSetExternalEntityLoader(LIBXML(defaultEntityLoader));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_TRUE;
|
RETURN_TRUE;
|
||||||
|
|
|
@ -43,7 +43,6 @@ ZEND_BEGIN_MODULE_GLOBALS(libxml)
|
||||||
zval *stream_context;
|
zval *stream_context;
|
||||||
smart_str error_buffer;
|
smart_str error_buffer;
|
||||||
zend_llist *error_list;
|
zend_llist *error_list;
|
||||||
xmlExternalEntityLoader defaultEntityLoader; /* saved here to allow it restored */
|
|
||||||
struct _php_libxml_entity_resolver {
|
struct _php_libxml_entity_resolver {
|
||||||
zend_fcall_info fci;
|
zend_fcall_info fci;
|
||||||
zend_fcall_info_cache fcc;
|
zend_fcall_info_cache fcc;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue