1 /*
2  * This file is part of libdom.
3  * Licensed under the MIT License,
4  *			http://www.opensource.org/licenses/mit-license.php
5  * Copyright 2007 John-Mark Bell <jmb@netsurf-browser.org>
6  * Copyright 2009 Bo Yang <struggleyb.nku@gmail.com>
7  */
8 
9 #include <string.h>
10 
11 #include <dom/dom.h>
12 
13 #include "utils/namespace.h"
14 #include "utils/validate.h"
15 #include "utils/utils.h"
16 
17 
18 /** XML prefix */
19 static dom_string *xml;
20 /** XMLNS prefix */
21 static dom_string *xmlns;
22 
23 /* The namespace strings */
24 static const char *namespaces[DOM_NAMESPACE_COUNT] = {
25 	NULL,
26 	"http://www.w3.org/1999/xhtml",
27 	"http://www.w3.org/1998/Math/MathML",
28 	"http://www.w3.org/2000/svg",
29 	"http://www.w3.org/1999/xlink",
30 	"http://www.w3.org/XML/1998/namespace",
31 	"http://www.w3.org/2000/xmlns/"
32 };
33 
34 dom_string *dom_namespaces[DOM_NAMESPACE_COUNT] = {
35 	NULL,
36 };
37 
38 /**
39  * Initialise the namespace component
40  *
41  * \return DOM_NO_ERR on success.
42  */
_dom_namespace_initialise(void)43 static dom_exception _dom_namespace_initialise(void)
44 {
45 	int i;
46 	dom_exception err;
47 
48 	err = dom_string_create((const uint8_t *) "xml", SLEN("xml"), &xml);
49 	if (err != DOM_NO_ERR) {
50 		return err;
51 	}
52 
53 	err = dom_string_create((const uint8_t *) "xmlns", SLEN("xmlns"),
54 			&xmlns);
55 	if (err != DOM_NO_ERR) {
56 		dom_string_unref(xml);
57 		xml = NULL;
58 
59 		return err;
60 	}
61 
62 	for (i = 1; i < DOM_NAMESPACE_COUNT; i++) {
63 		err = dom_string_create(
64 				(const uint8_t *) namespaces[i],
65 				strlen(namespaces[i]), &dom_namespaces[i]);
66 		if (err != DOM_NO_ERR) {
67 			dom_string_unref(xmlns);
68 			xmlns = NULL;
69 
70 			dom_string_unref(xml);
71 			xml = NULL;
72 
73 			return err;
74 		}
75 	}
76 
77 	return DOM_NO_ERR;
78 }
79 
80 /**
81  * Finalise the namespace component
82  *
83  * \return DOM_NO_ERR on success.
84  */
dom_namespace_finalise(void)85 dom_exception dom_namespace_finalise(void)
86 {
87 	int i;
88 
89 	if (xmlns != NULL) {
90 		dom_string_unref(xmlns);
91 		xmlns = NULL;
92 	}
93 
94 	if (xml != NULL) {
95 		dom_string_unref(xml);
96 		xml = NULL;
97 	}
98 
99 	for (i = 1; i < DOM_NAMESPACE_COUNT; i++) {
100 		if (dom_namespaces[i] != NULL) {
101 			dom_string_unref(dom_namespaces[i]);
102 			dom_namespaces[i] = NULL;
103 		}
104 	}
105 
106 	return DOM_NO_ERR;
107 }
108 
109 /**
110  * Ensure a QName is valid
111  *
112  * \param qname      The qname to validate
113  * \param namespace  The namespace URI associated with the QName, or NULL
114  * \return DOM_NO_ERR                if valid,
115  *         DOM_INVALID_CHARACTER_ERR if ::qname contains an invalid character,
116  *         DOM_NAMESPACE_ERR         if ::qname is malformed, or it has a
117  *                                   prefix and ::namespace is NULL, or
118  *                                   ::qname has a prefix "xml" and
119  *                                   ::namespace is not
120  *                                   "http://www.w3.org/XML/1998/namespace",
121  *                                   or ::qname has a prefix "xmlns" and
122  *                                   ::namespace is not
123  *                                   "http://www.w3.org/2000/xmlns", or
124  *                                   ::namespace is
125  *                                   "http://www.w3.org/2000/xmlns" and
126  *                                   ::qname is not (or is not prefixed by)
127  *                                   "xmlns".
128  */
_dom_namespace_validate_qname(dom_string * qname,dom_string * namespace)129 dom_exception _dom_namespace_validate_qname(dom_string *qname,
130 		dom_string *namespace)
131 {
132 	uint32_t colon, len;
133 
134 	if (xml == NULL) {
135 		dom_exception err = _dom_namespace_initialise();
136 		if (err != DOM_NO_ERR)
137 			return err;
138 	}
139 
140 	if (qname == NULL) {
141 		if (namespace != NULL)
142 			return DOM_NAMESPACE_ERR;
143 		if (namespace == NULL)
144 			return DOM_NO_ERR;
145 	}
146 
147 	if (_dom_validate_name(qname) == false)
148 		return DOM_NAMESPACE_ERR;
149 
150 	len = dom_string_length(qname);
151 
152 	/* Find colon */
153 	colon = dom_string_index(qname, ':');
154 
155 	if (colon == (uint32_t) -1) {
156 		/* No prefix */
157 		/* If namespace URI is for xmlns, ensure qname == "xmlns" */
158 		if (namespace != NULL &&
159 				dom_string_isequal(namespace,
160 				dom_namespaces[DOM_NAMESPACE_XMLNS]) &&
161 				dom_string_isequal(qname, xmlns) == false) {
162 			return DOM_NAMESPACE_ERR;
163 		}
164 
165 		/* If qname == "xmlns", ensure namespace URI is for xmlns */
166 		if (namespace != NULL &&
167 				dom_string_isequal(qname, xmlns) &&
168 				dom_string_isequal(namespace,
169 				dom_namespaces[DOM_NAMESPACE_XMLNS]) == false) {
170 			return DOM_NAMESPACE_ERR;
171 		}
172 	} else if (colon == 0) {
173 		/* Some name like ":name" */
174 		if (namespace != NULL)
175 			return DOM_NAMESPACE_ERR;
176 	} else {
177 		/* Prefix */
178 		dom_string *prefix;
179 		dom_string *lname;
180 		dom_exception err;
181 
182 		/* Ensure there is a namespace URI */
183 		if (namespace == NULL) {
184 			return DOM_NAMESPACE_ERR;
185 		}
186 
187 		err = dom_string_substr(qname, 0, colon, &prefix);
188 		if (err != DOM_NO_ERR) {
189 			return err;
190 		}
191 
192 		err = dom_string_substr(qname, colon + 1, len, &lname);
193 		if (err != DOM_NO_ERR) {
194 			dom_string_unref(prefix);
195 			return err;
196 		}
197 
198 		if ((_dom_validate_ncname(prefix) == false) ||
199 		    (_dom_validate_ncname(lname) == false)) {
200 			dom_string_unref(prefix);
201 			dom_string_unref(lname);
202 			return DOM_NAMESPACE_ERR;
203 		}
204 		dom_string_unref(lname);
205 
206 		/* Test for invalid XML namespace */
207 		if (dom_string_isequal(prefix, xml) &&
208 				dom_string_isequal(namespace,
209 				dom_namespaces[DOM_NAMESPACE_XML]) == false) {
210 			dom_string_unref(prefix);
211 			return DOM_NAMESPACE_ERR;
212 		}
213 
214 		/* Test for invalid xmlns namespace */
215 		if (dom_string_isequal(prefix, xmlns) &&
216 				dom_string_isequal(namespace,
217 				dom_namespaces[DOM_NAMESPACE_XMLNS]) == false) {
218 			dom_string_unref(prefix);
219 			return DOM_NAMESPACE_ERR;
220 		}
221 
222 		/* Test for presence of xmlns namespace with non xmlns prefix */
223 		if (dom_string_isequal(namespace,
224 				dom_namespaces[DOM_NAMESPACE_XMLNS]) &&
225 				dom_string_isequal(prefix, xmlns) == false) {
226 			dom_string_unref(prefix);
227 			return DOM_NAMESPACE_ERR;
228 		}
229 
230 		dom_string_unref(prefix);
231 	}
232 
233 	return DOM_NO_ERR;
234 }
235 
236 /**
237  * Split a QName into a namespace prefix and localname string
238  *
239  * \param qname      The qname to split
240  * \param prefix     Pointer to location to receive prefix
241  * \param localname  Pointer to location to receive localname
242  * \return DOM_NO_ERR on success.
243  *
244  * If there is no prefix present in ::qname, then ::prefix will be NULL.
245  *
246  * ::prefix and ::localname will be referenced. The caller should unreference
247  * them once finished.
248  */
_dom_namespace_split_qname(dom_string * qname,dom_string ** prefix,dom_string ** localname)249 dom_exception _dom_namespace_split_qname(dom_string *qname,
250 		dom_string **prefix, dom_string **localname)
251 {
252 	uint32_t colon;
253 	dom_exception err;
254 
255 	if (xml == NULL) {
256 		err = _dom_namespace_initialise();
257 		if (err != DOM_NO_ERR)
258 			return err;
259 	}
260 
261 	/* Find colon, if any */
262 	colon = dom_string_index(qname, ':');
263 
264 	if (colon == (uint32_t) -1) {
265 		/* None found => no prefix */
266 		*prefix = NULL;
267 		*localname = dom_string_ref(qname);
268 	} else {
269 		/* Found one => prefix */
270 		err = dom_string_substr(qname, 0, colon, prefix);
271 		if (err != DOM_NO_ERR) {
272 			return err;
273 		}
274 
275 		err = dom_string_substr(qname, colon + 1,
276 				dom_string_length(qname), localname);
277 		if (err != DOM_NO_ERR) {
278 			dom_string_unref(*prefix);
279 			*prefix = NULL;
280 			return err;
281 		}
282 	}
283 
284 	return DOM_NO_ERR;
285 }
286 
287 /**
288  * Get the XML prefix dom_string
289  *
290  * \return the xml prefix dom_string.
291  *
292  * Note: The client of this function may or may not call the dom_string_ref
293  * on the returned dom_string, because this string will only be destroyed when
294  * the dom_finalise is called. But if the client call dom_string_ref, it must
295  * call dom_string_unref to maintain a correct ref count of the dom_string.
296  */
_dom_namespace_get_xml_prefix(void)297 dom_string *_dom_namespace_get_xml_prefix(void)
298 {
299 	if (xml == NULL) {
300 		if (_dom_namespace_initialise() != DOM_NO_ERR)
301 			return NULL;
302 	}
303 
304 	return xml;
305 }
306 
307 /**
308  * Get the XMLNS prefix dom_string.
309  *
310  * \return the xmlns prefix dom_string
311  *
312  * Note: The client of this function may or may not call the dom_string_ref
313  * on the returned dom_string, because this string will only be destroyed when
314  * the dom_finalise is called. But if the client call dom_string_ref, it must
315  * call dom_string_unref to maintain a correct ref count of the dom_string.
316  */
_dom_namespace_get_xmlns_prefix(void)317 dom_string *_dom_namespace_get_xmlns_prefix(void)
318 {
319 	if (xml == NULL) {
320 		if (_dom_namespace_initialise() != DOM_NO_ERR)
321 			return NULL;
322 	}
323 
324 	return xmlns;
325 }
326 
327