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