Skip to content

Commit 5fa23c5

Browse files
author
Rob Richards
committed
fix for bug #25666 (XML namespaces broken in PHP5)
1 parent 103b3c6 commit 5fa23c5

File tree

2 files changed

+140
-20
lines changed

2 files changed

+140
-20
lines changed

Diff for: ext/xml/compat.c

+138-19
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020
#if HAVE_LIBXML && HAVE_XML
2121
#include "expat_compat.h"
2222

23+
typedef struct _php_xml_ns {
24+
xmlNsPtr nsptr;
25+
int ref_count;
26+
void *next;
27+
void *prev;
28+
} php_xml_ns;
29+
2330
#ifdef LIBXML_EXPAT_COMPAT
2431

2532
#define IS_NS_DECL(__ns) \
@@ -34,51 +41,111 @@ _find_namespace_decl(XML_Parser parser, const xmlChar *tagname, const xmlChar **
3441
xmlChar *value;
3542
xmlChar *partial;
3643
xmlChar *namespace;
44+
php_xml_ns *cur_ns_scope = NULL;
45+
php_xml_ns *exist_ns_scope;
46+
xmlNsPtr nsptr, curnsptr;
47+
48+
exist_ns_scope = xmlHashLookup(parser->_reverse_ns_map, tagname);
49+
50+
if (exist_ns_scope) {
51+
while (exist_ns_scope->next != NULL)
52+
exist_ns_scope = exist_ns_scope->next;
53+
}
3754

3855
while (attr_p && *attr_p) {
3956
name = attr_p[0];
4057
value = xmlStrdup(attr_p[1]);
4158

4259
partial = xmlSplitQName(parser->parser, name, &namespace);
60+
4361
if (IS_NS_DECL(namespace)) {
62+
4463
if (parser->h_start_ns) {
4564
parser->h_start_ns(parser->user, partial, (const XML_Char *) value);
4665
}
47-
xmlHashAddEntry(parser->_ns_map, partial, value);
48-
xmlHashAddEntry(parser->_reverse_ns_map, tagname, xmlStrdup(partial));
49-
break;
66+
if (xmlHashLookup(parser->_ns_map, partial) == NULL) {
67+
xmlHashAddEntry(parser->_ns_map, partial, value);
68+
} else {
69+
xmlFree(value);
70+
}
71+
72+
nsptr = (xmlNsPtr) xmlMalloc(sizeof(xmlNs));
73+
74+
if (nsptr) {
75+
memset(nsptr, 0, sizeof(xmlNs));
76+
nsptr->type = XML_LOCAL_NAMESPACE;
77+
78+
if (value != NULL)
79+
nsptr->href = xmlStrdup(value);
80+
if (partial != NULL)
81+
nsptr->prefix = xmlStrdup(partial);
82+
83+
if (cur_ns_scope == NULL) {
84+
cur_ns_scope = emalloc(sizeof(php_xml_ns));
85+
cur_ns_scope->next = NULL;
86+
cur_ns_scope->prev = NULL;
87+
cur_ns_scope->nsptr = nsptr;
88+
cur_ns_scope->ref_count = 0;
89+
90+
if (exist_ns_scope) {
91+
exist_ns_scope->next = cur_ns_scope;
92+
cur_ns_scope->prev = exist_ns_scope;
93+
} else {
94+
xmlHashAddEntry(parser->_reverse_ns_map, tagname, cur_ns_scope);
95+
}
96+
97+
exist_ns_scope = cur_ns_scope;
98+
} else {
99+
curnsptr = cur_ns_scope->nsptr;
100+
while (curnsptr->next != NULL) {
101+
curnsptr = curnsptr->next;
102+
}
103+
curnsptr->next = nsptr;
104+
}
105+
}
106+
107+
} else {
108+
xmlFree(value);
109+
}
110+
111+
xmlFree(partial);
112+
if (namespace != NULL) {
113+
xmlFree(namespace);
50114
}
51115

52-
xmlFree(value);
53116
attr_p += 2;
54117
}
118+
119+
if (exist_ns_scope) {
120+
exist_ns_scope->ref_count++;
121+
}
55122
}
56123

57124
static void
58125
_qualify_namespace(XML_Parser parser, const xmlChar *name, xmlChar **qualified)
59126
{
60127
xmlChar *partial;
61128
xmlChar *namespace;
62-
int len;
63129

64130
partial = xmlSplitQName(parser->parser, name, &namespace);
65131
if (namespace) {
66132
xmlChar *nsvalue;
67133

68134
nsvalue = xmlHashLookup(parser->_ns_map, namespace);
69135
if (nsvalue) {
70-
len = strlen(nsvalue) + strlen(partial) + 1; /* colon */
71-
*qualified = malloc(len+1);
72-
memcpy(*qualified, nsvalue, strlen(nsvalue));
73-
memcpy(*qualified + strlen(nsvalue), ":", 1);
74-
memcpy(*qualified + strlen(nsvalue) + 1, partial, strlen(partial));
75-
(*qualified)[len] = '\0';
136+
/* Use libxml functions otherwise its memory deallocation is screwed up */
137+
*qualified = xmlStrdup(nsvalue);
138+
*qualified = xmlStrncat(*qualified, parser->_ns_seperator, 1);
139+
*qualified = xmlStrncat(*qualified, partial, strlen(partial));
76140
} else {
77141
*qualified = xmlStrdup(name);
78142
}
143+
xmlFree(namespace);
79144
} else {
80145
*qualified = xmlStrdup(name);
81146
}
147+
148+
xmlFree(partial);
82149
}
83150

84151
static void
@@ -103,6 +170,15 @@ _start_element_handler(void *user, const xmlChar *name, const xmlChar **attribut
103170
xmlFree(qualified_name);
104171
}
105172

173+
static void
174+
_namespace_handler(XML_Parser parser, xmlNsPtr nsptr)
175+
{
176+
if (nsptr != NULL) {
177+
_namespace_handler(parser, nsptr->next);
178+
parser->h_end_ns(parser->user, nsptr->prefix);
179+
}
180+
}
181+
106182
static void
107183
_end_element_handler(void *user, const xmlChar *name)
108184
{
@@ -114,20 +190,40 @@ _end_element_handler(void *user, const xmlChar *name)
114190
}
115191

116192
if (parser->use_namespace) {
117-
xmlChar *nsname;
118-
119-
nsname = xmlHashLookup(parser->_reverse_ns_map, name);
120-
if (nsname && parser->h_end_ns) {
121-
parser->h_end_ns(parser->user, nsname);
122-
}
123-
124193
_qualify_namespace(parser, name, &qualified_name);
125194
} else {
126195
qualified_name = xmlStrdup(name);
127196
}
128197

129198
parser->h_end_element(parser->user, (const XML_Char *) qualified_name);
130199

200+
if (parser->use_namespace) {
201+
int tag_counter;
202+
php_xml_ns *cur_ns_scope, *prev_ns_scope;
203+
xmlNsPtr nsptr;
204+
205+
cur_ns_scope = xmlHashLookup(parser->_reverse_ns_map, name);
206+
if (cur_ns_scope) {
207+
while (cur_ns_scope->next != NULL) {
208+
cur_ns_scope = cur_ns_scope->next;
209+
}
210+
tag_counter = --cur_ns_scope->ref_count;
211+
if (tag_counter == 0) {
212+
nsptr = cur_ns_scope->nsptr;
213+
if (nsptr && parser->h_end_ns) {
214+
_namespace_handler(parser, nsptr);
215+
}
216+
xmlFreeNsList(nsptr);
217+
cur_ns_scope->nsptr = NULL;
218+
prev_ns_scope = cur_ns_scope->prev;
219+
if (prev_ns_scope != NULL) {
220+
efree(cur_ns_scope);
221+
prev_ns_scope->next = NULL;
222+
}
223+
}
224+
}
225+
}
226+
131227
xmlFree(qualified_name);
132228
}
133229

@@ -304,6 +400,8 @@ XML_ParserCreate_MM(const XML_Char *encoding, const XML_Memory_Handling_Suite *m
304400
parser = (XML_Parser) emalloc(sizeof(struct _XML_Parser));
305401
memset(parser, 0, sizeof(struct _XML_Parser));
306402
parser->use_namespace = 0;
403+
parser->_ns_seperator = NULL;
404+
307405
parser->parser = xmlCreatePushParserCtxt((xmlSAXHandlerPtr) &php_xml_compat_handlers, (void *) parser, NULL, 0, NULL);
308406
if (parser->parser == NULL) {
309407
efree(parser);
@@ -318,6 +416,7 @@ XML_ParserCreate_MM(const XML_Char *encoding, const XML_Memory_Handling_Suite *m
318416
parser->use_namespace = 1;
319417
parser->_ns_map = xmlHashCreate(10);
320418
parser->_reverse_ns_map = xmlHashCreate(10);
419+
parser->_ns_seperator = xmlStrdup(sep);
321420
}
322421
return parser;
323422
}
@@ -544,12 +643,32 @@ _free_ns_name(void *ptr, xmlChar *name)
544643
xmlFree(ptr);
545644
}
546645

646+
static void
647+
_free_ns_pointer(void *ptr, xmlChar *name)
648+
{
649+
php_xml_ns *cur_ns_scope;
650+
651+
/* Child scopes should already be removed, but in the event
652+
of malformed xml, they may still be resident and need to be cleaned */
653+
cur_ns_scope = ((php_xml_ns *) ptr)->next;
654+
if (cur_ns_scope != NULL) {
655+
_free_ns_pointer(cur_ns_scope, NULL);
656+
}
657+
658+
xmlFreeNsList(((php_xml_ns *) ptr)->nsptr);
659+
660+
efree(ptr);
661+
}
662+
547663
void
548664
XML_ParserFree(XML_Parser parser)
549665
{
550666
if (parser->use_namespace) {
551667
xmlHashFree(parser->_ns_map, _free_ns_name);
552-
xmlHashFree(parser->_reverse_ns_map, _free_ns_name);
668+
xmlHashFree(parser->_reverse_ns_map, _free_ns_pointer);
669+
if (parser->_ns_seperator) {
670+
xmlFree(parser->_ns_seperator);
671+
}
553672
}
554673
xmlFreeParserCtxt(parser->parser);
555674
efree(parser);

Diff for: ext/xml/expat_compat.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ typedef struct _XML_Parser {
5656

5757
xmlHashTablePtr _ns_map;
5858
xmlHashTablePtr _reverse_ns_map;
59-
59+
xmlChar *_ns_seperator;
60+
6061
void *user;
6162
xmlParserCtxtPtr parser;
6263

0 commit comments

Comments
 (0)