Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 69 additions & 20 deletions ext/dom/node.c
Original file line number Diff line number Diff line change
Expand Up @@ -2101,33 +2101,72 @@ PHP_METHOD(DOMNode, lookupNamespaceURI)
}
/* }}} end dom_node_lookup_namespace_uri */

/* Allocate, track and prepend a temporary nsDef entry for C14N.
* Returns the new xmlNsPtr for the caller to fill in href/prefix/_private,
* or NULL on allocation failure. */
static xmlNsPtr dom_alloc_ns_decl(HashTable *links, xmlNodePtr node)
{
xmlNsPtr ns = xmlMalloc(sizeof(*ns));
if (!ns) {
return NULL;
}

zval *zv = zend_hash_index_lookup(links, (zend_ulong) node);
if (Z_ISNULL_P(zv)) {
ZVAL_LONG(zv, 1);
} else {
Z_LVAL_P(zv)++;
}

memset(ns, 0, sizeof(*ns));
ns->type = XML_LOCAL_NAMESPACE;
ns->next = node->nsDef;
node->nsDef = ns;

return ns;
}

/* Mint a temporary nsDef entry so C14N finds namespaces that live on node->ns
* but have no matching xmlns attribute (typical for createElementNS). */
static void dom_add_synthetic_ns_decl(HashTable *links, xmlNodePtr node, xmlNsPtr src_ns)
{
xmlNsPtr ns = dom_alloc_ns_decl(links, node);
if (!ns) {
return;
}

ns->href = xmlStrdup(src_ns->href);
ns->prefix = src_ns->prefix ? xmlStrdup(src_ns->prefix) : NULL;
}

/* Same, but for attribute namespaces, which may collide by prefix with the
* element's own ns or with a sibling attribute's ns. */
static void dom_add_synthetic_ns_decl_for_attr(HashTable *links, xmlNodePtr node, xmlNsPtr src_ns)
{
for (xmlNsPtr existing = node->nsDef; existing; existing = existing->next) {
if (xmlStrEqual(existing->prefix, src_ns->prefix)) {
return;
}
}

dom_add_synthetic_ns_decl(links, node, src_ns);
}

static void dom_relink_ns_decls_element(HashTable *links, xmlNodePtr node)
{
if (node->type == XML_ELEMENT_NODE) {
for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
if (php_dom_ns_is_fast((const xmlNode *) attr, php_dom_ns_is_xmlns_magic_token)) {
xmlNsPtr ns = xmlMalloc(sizeof(*ns));
xmlNsPtr ns = dom_alloc_ns_decl(links, node);
if (!ns) {
return;
}

zval *zv = zend_hash_index_lookup(links, (zend_ulong) node);
if (Z_ISNULL_P(zv)) {
ZVAL_LONG(zv, 1);
} else {
Z_LVAL_P(zv)++;
}

bool should_free;
xmlChar *attr_value = php_libxml_attr_value(attr, &should_free);

memset(ns, 0, sizeof(*ns));
ns->type = XML_LOCAL_NAMESPACE;
ns->href = should_free ? attr_value : xmlStrdup(attr_value);
ns->prefix = attr->ns->prefix ? xmlStrdup(attr->name) : NULL;
ns->next = node->nsDef;
node->nsDef = ns;

ns->_private = attr;
if (attr->prev) {
attr->prev->next = attr->next;
Expand All @@ -2148,6 +2187,14 @@ static void dom_relink_ns_decls_element(HashTable *links, xmlNodePtr node)
* can return the current namespace. */
zend_hash_index_add_new_ptr(links, (zend_ulong) node | 1, node->ns);
node->ns = xmlSearchNs(node->doc, node, NULL);
} else if (node->ns) {
dom_add_synthetic_ns_decl(links, node, node->ns);
}

for (xmlAttrPtr attr = node->properties; attr; attr = attr->next) {
if (attr->ns && !php_dom_ns_is_fast((const xmlNode *) attr, php_dom_ns_is_xmlns_magic_token)) {
dom_add_synthetic_ns_decl_for_attr(links, node, attr->ns);
}
}
}
}
Expand Down Expand Up @@ -2177,13 +2224,15 @@ static void dom_unlink_ns_decls(HashTable *links)
node->nsDef = ns->next;

xmlAttrPtr attr = ns->_private;
if (attr->prev) {
attr->prev->next = attr;
} else {
node->properties = attr;
}
if (attr->next) {
attr->next->prev = attr;
if (attr) {
if (attr->prev) {
attr->prev->next = attr;
} else {
node->properties = attr;
}
if (attr->next) {
attr->next->prev = attr;
}
}

xmlFreeNs(ns);
Expand Down
28 changes: 28 additions & 0 deletions ext/dom/tests/modern/xml/gh21544.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
--TEST--
GH-21544 (Dom\XMLDocument::C14N() drops namespace declarations on DOM-built documents)
--CREDITS--
Toon Verwerft (veewee)
--EXTENSIONS--
dom
--FILE--
<?php

$doc = Dom\XMLDocument::createEmpty();
$root = $doc->createElementNS("urn:envelope", "env:Root");
$doc->appendChild($root);
$child = $doc->createElementNS("urn:child", "x:Child");
$root->appendChild($child);

$parsed = Dom\XMLDocument::createFromString(
'<env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"/></env:Root>'
);

echo "DOM-built C14N: " . $doc->C14N() . PHP_EOL;
echo "Parsed C14N: " . $parsed->C14N() . PHP_EOL;
var_dump($doc->C14N() === $parsed->C14N());

?>
--EXPECT--
DOM-built C14N: <env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"></x:Child></env:Root>
Parsed C14N: <env:Root xmlns:env="urn:envelope"><x:Child xmlns:x="urn:child"></x:Child></env:Root>
bool(true)
20 changes: 11 additions & 9 deletions ext/phar/dirstream.c
Original file line number Diff line number Diff line change
Expand Up @@ -247,11 +247,11 @@ static php_stream *phar_make_dirstream(const char *dir, size_t dirlen, const Has
*/
php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path, const char *mode, int options, zend_string **opened_path, php_stream_context *context STREAMS_DC) /* {{{ */
{
php_url *resource = NULL;
char *error;
phar_archive_data *phar;

if ((resource = phar_parse_url(wrapper, path, mode, options)) == NULL) {
php_url *resource = phar_parse_url(wrapper, path, mode, options);
if (!resource) {
php_stream_wrapper_log_error(wrapper, options, "phar url \"%s\" is unknown", path);
return NULL;
}
Expand Down Expand Up @@ -343,7 +343,7 @@ php_stream *phar_wrapper_open_dir(php_stream_wrapper *wrapper, const char *path,
*/
int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mode, int options, php_stream_context *context) /* {{{ */
{
phar_entry_info entry, *e;
phar_entry_info entry;
phar_archive_data *phar = NULL;
char *error;
php_url *resource = NULL;
Expand Down Expand Up @@ -390,7 +390,8 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo
return 0;
}

if ((e = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1, 2, &error, true))) {
phar_entry_info *e = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1, 2, &error, true);
if (e) {
/* directory exists, or is a subdirectory of an existing file */
if (e->is_temp_dir) {
zend_string_efree(e->filename);
Expand Down Expand Up @@ -444,7 +445,8 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo
entry.flags = PHAR_ENT_PERM_DEF_DIR;
entry.old_flags = PHAR_ENT_PERM_DEF_DIR;

if (NULL == zend_hash_add_mem(&phar->manifest, entry.filename, &entry, sizeof(phar_entry_info))) {
void *had_been_added = zend_hash_add_mem(&phar->manifest, entry.filename, &entry, sizeof(phar_entry_info));
if (!had_been_added) {
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\" in phar \"%s\", adding to manifest failed", ZSTR_VAL(entry.filename), phar->fname);
zend_string_efree(entry.filename);
return 0;
Expand All @@ -469,10 +471,8 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo
*/
int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options, php_stream_context *context) /* {{{ */
{
phar_entry_info *entry;
phar_archive_data *phar = NULL;
char *error;
php_url *resource = NULL;

/* pre-readonly check, we need to know if this is a data phar */
zend_string *arch = phar_split_fname(url, strlen(url), NULL, 2, 2);
Expand All @@ -492,7 +492,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options
return 0;
}

if ((resource = phar_parse_url(wrapper, url, "w", options)) == NULL) {
php_url *resource = phar_parse_url(wrapper, url, "w", options);
if (!resource) {
return 0;
}

Expand All @@ -518,7 +519,8 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options

size_t path_len = ZSTR_LEN(resource->path) - 1;

if (!(entry = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, path_len, 2, &error, true))) {
phar_entry_info *entry = phar_get_entry_info_dir(phar, ZSTR_VAL(resource->path) + 1, path_len, 2, &error, true);
if (!entry) {
if (error) {
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", ZSTR_VAL(resource->path)+1, ZSTR_VAL(resource->host), error);
efree(error);
Expand Down
21 changes: 10 additions & 11 deletions ext/phar/func_interceptors.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ static zend_string* phar_get_name_for_relative_paths(zend_string *filename, bool

zend_string *name = NULL;
if (using_include_path) {
if (!(name = phar_find_in_include_path(filename, NULL))) {
name = phar_find_in_include_path(filename, NULL);
if (!name) {
/* this file is not in the phar, use the original path */
zend_string_release_ex(arch, false);
return NULL;
Expand Down Expand Up @@ -337,15 +338,12 @@ static void phar_fancy_stat(zend_stat_t *stat_sb, int type, zval *return_value)
wmask=S_IWGRP;
xmask=S_IXGRP;
} else {
int groups, n, i;
gid_t *gids;

groups = getgroups(0, NULL);
if(groups > 0) {
gids=(gid_t *)safe_emalloc(groups, sizeof(gid_t), 0);
n=getgroups(groups, gids);
for(i=0;i<n;++i){
if(stat_sb->st_gid==gids[i]) {
int groups = getgroups(0, NULL);
if (groups > 0) {
gid_t *gids = safe_emalloc(groups, sizeof(gid_t), 0);
int n = getgroups(groups, gids);
for(int i = 0; i < n; ++i){
if (stat_sb->st_gid==gids[i]) {
rmask=S_IRGRP;
wmask=S_IWGRP;
xmask=S_IXGRP;
Expand Down Expand Up @@ -844,7 +842,8 @@ void phar_release_functions(void)
/* {{{ void phar_intercept_functions_init(void) */
#define PHAR_INTERCEPT(func) \
PHAR_G(orig_##func) = NULL; \
if (NULL != (orig = zend_hash_str_find_ptr(CG(function_table), #func, sizeof(#func)-1))) { \
orig = zend_hash_str_find_ptr(CG(function_table), #func, sizeof(#func)-1); \
if (orig) { \
PHAR_G(orig_##func) = orig->internal_function.handler; \
orig->internal_function.handler = PHP_FN(phar_##func); \
}
Expand Down
31 changes: 17 additions & 14 deletions ext/phar/phar.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ HashTable cached_alias;
static void phar_split_cache_list(void) /* {{{ */
{
char *tmp;
char *key, *lasts, *end;
char *key, *lasts;
char ds[2];
phar_archive_data *phar;
uint32_t i = 0;
Expand Down Expand Up @@ -124,7 +124,7 @@ static void phar_split_cache_list(void) /* {{{ */
key;
key = php_strtok_r(NULL, ds, &lasts)) {
size_t len;
end = strchr(key, DEFAULT_DIR_SEPARATOR);
const char *end = strchr(key, DEFAULT_DIR_SEPARATOR);
if (end) {
len = end - key;
} else {
Expand Down Expand Up @@ -1263,7 +1263,8 @@ static zend_result phar_parse_pharfile(php_stream *fp, char *fname, size_t fname
MAPPHAR_FAIL("Cannot open archive \"%s\", invalid alias");
}

if (NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len);
if (fd_ptr) {
if (SUCCESS != phar_free_alias(fd_ptr)) {
signature = NULL;
fp = NULL;
Expand Down Expand Up @@ -1460,10 +1461,9 @@ ZEND_ATTRIBUTE_NONNULL_ARGS(1, 7, 8) zend_result phar_create_or_parse_filename(c
/* assume tar format, PharData can specify other */
mydata->is_tar = 1;
} else {
phar_archive_data *fd_ptr;

if (alias && NULL != (fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len))) {
if (SUCCESS != phar_free_alias(fd_ptr)) {
if (alias) {
const phar_archive_data *fd_ptr = zend_hash_str_find_ptr(&(PHAR_G(phar_alias_map)), alias, alias_len);
if (fd_ptr && SUCCESS != phar_free_alias(fd_ptr)) {
spprintf(error, 4096, "phar error: phar \"%s\" cannot set alias \"%s\", already in use by another phar archive", mydata->fname, alias);

zend_hash_str_del(&(PHAR_G(phar_fname_map)), mydata->fname, fname_len);
Expand Down Expand Up @@ -1669,14 +1669,14 @@ static zend_result phar_open_from_fp(php_stream* fp, char *fname, size_t fname_l
continue;
} else if (!memcmp(pos, bz_magic, 3)) {
php_stream_filter *filter;
php_stream *temp;

if (!PHAR_G(has_bz2)) {
MAPPHAR_ALLOC_FAIL("unable to decompress bzipped phar archive \"%s\" to temporary file, enable bz2 extension in php.ini")
}

/* entire file is bzip-compressed, uncompress to temporary file */
if (!(temp = php_stream_fopen_tmpfile())) {
php_stream *temp = php_stream_fopen_tmpfile();
if (!temp) {
MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"")
}

Expand Down Expand Up @@ -1750,10 +1750,10 @@ static zend_result phar_open_from_fp(php_stream* fp, char *fname, size_t fname_l
static zend_result phar_analyze_path(const char *fname, const char *ext, size_t ext_len, int for_create) /* {{{ */
{
php_stream_statbuf ssb;
char *realpath;
char *filename = estrndup(fname, (ext - fname) + ext_len);

if ((realpath = expand_filepath(filename, NULL))) {
char *realpath = expand_filepath(filename, NULL);
if (realpath) {
#ifdef PHP_WIN32
phar_unixify_path_separators(realpath, strlen(realpath));
#endif
Expand Down Expand Up @@ -1800,7 +1800,8 @@ static zend_result phar_analyze_path(const char *fname, const char *ext, size_t

if (SUCCESS != php_stream_stat_path((char *) filename, &ssb)) {
if (!slash) {
if (!(realpath = expand_filepath(filename, NULL))) {
realpath = expand_filepath(filename, NULL);
if (!realpath) {
efree(filename);
return FAILURE;
}
Expand Down Expand Up @@ -1943,7 +1944,8 @@ zend_result phar_detect_phar_fname_ext(const char *filename, size_t filename_len
phar_archive_data *pphar;

if (is_complete) {
if (NULL != (pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), (char *) filename, filename_len))) {
pphar = zend_hash_str_find_ptr(&(PHAR_G(phar_fname_map)), filename, filename_len);
if (pphar) {
*ext_str = filename + (filename_len - pphar->ext_len);
woohoo:
*ext_len = pphar->ext_len;
Expand All @@ -1963,7 +1965,8 @@ zend_result phar_detect_phar_fname_ext(const char *filename, size_t filename_len
return FAILURE;
}

if (PHAR_G(manifest_cached) && NULL != (pphar = zend_hash_str_find_ptr(&cached_phars, (char *) filename, filename_len))) {
pphar = PHAR_G(manifest_cached) ? zend_hash_str_find_ptr(&cached_phars, filename, filename_len) : NULL;
if (pphar) {
*ext_str = filename + (filename_len - pphar->ext_len);
goto woohoo;
}
Expand Down
6 changes: 3 additions & 3 deletions ext/phar/phar_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ ZEND_BEGIN_MODULE_GLOBALS(phar)
char *openssl_privatekey;
uint32_t openssl_privatekey_len;
/* phar_get_archive cache */
char* last_phar_name;
const char *last_phar_name;
uint32_t last_phar_name_len;
uint32_t last_alias_len;
const char* last_alias;
Expand Down Expand Up @@ -245,9 +245,9 @@ typedef struct _phar_entry_info {
struct _phar_archive_data {
char *fname;
uint32_t fname_len;
/* for phar_detect_fname_ext, this stores the location of the file extension within fname */
/* The ext field stores the location of the file extension from the fname field, and thus should never be freed. */
uint32_t ext_len;
char *ext;
const char *ext;
char *alias;
uint32_t alias_len;
char version[12];
Expand Down
Loading
Loading