xref: /php-src/ext/dom/characterdata.c (revision 3c33430d)
1 /*
2    +----------------------------------------------------------------------+
3    | Copyright (c) The PHP Group                                          |
4    +----------------------------------------------------------------------+
5    | This source file is subject to version 3.01 of the PHP license,      |
6    | that is bundled with this package in the file LICENSE, and is        |
7    | available through the world-wide-web at the following url:           |
8    | https://www.php.net/license/3_01.txt                                 |
9    | If you did not receive a copy of the PHP license and are unable to   |
10    | obtain it through the world-wide-web, please send a note to          |
11    | license@php.net so we can mail you a copy immediately.               |
12    +----------------------------------------------------------------------+
13    | Authors: Christian Stocker <chregu@php.net>                          |
14    |          Rob Richards <rrichards@php.net>                            |
15    +----------------------------------------------------------------------+
16 */
17 
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 
22 #include "php.h"
23 #if defined(HAVE_LIBXML) && defined(HAVE_DOM)
24 #include "php_dom.h"
25 #include "dom_properties.h"
26 
27 /*
28 * class DOMCharacterData extends DOMNode
29 *
30 * URL: https://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-FF21A306
31 * Since:
32 */
33 
34 /* For some peculiar reason, many of these methods operate on unsigned numbers.
35  * Unfortunately, "old DOM" doesn't, so we have to conditionally convert...
36  * And the reason we're using "unsigned int" instead of "unsigned zend_long" is because libxml2 internally works with ints. */
dom_convert_number_unsigned(dom_object * intern,zend_long input,unsigned int * output)37 static bool dom_convert_number_unsigned(dom_object *intern, zend_long input, unsigned int *output)
38 {
39 	if (input < 0) {
40 		if (php_dom_follow_spec_intern(intern)) {
41 			*output = (unsigned int) input;
42 		} else {
43 			php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
44 			return false;
45 		}
46 	} else {
47 		*output = input;
48 	}
49 	return true;
50 }
51 
52 /* {{{ data	string
53 readonly=no
54 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-72AB8359
55 Since:
56 */
dom_characterdata_data_read(dom_object * obj,zval * retval)57 zend_result dom_characterdata_data_read(dom_object *obj, zval *retval)
58 {
59 	DOM_PROP_NODE(xmlNodePtr, nodep, obj);
60 	php_dom_get_content_into_zval(nodep, retval, false);
61 	return SUCCESS;
62 }
63 
dom_characterdata_data_write(dom_object * obj,zval * newval)64 zend_result dom_characterdata_data_write(dom_object *obj, zval *newval)
65 {
66 	DOM_PROP_NODE(xmlNodePtr, nodep, obj);
67 
68 	/* Typed property, this is already a string */
69 	ZEND_ASSERT(Z_TYPE_P(newval) == IS_STRING);
70 	zend_string *str = Z_STR_P(newval);
71 
72 	xmlNodeSetContentLen(nodep, BAD_CAST ZSTR_VAL(str), ZSTR_LEN(str));
73 
74 	return SUCCESS;
75 }
76 
77 /* }}} */
78 
79 /* {{{ length	long
80 readonly=yes
81 URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-7D61178C
82 Since:
83 */
dom_characterdata_length_read(dom_object * obj,zval * retval)84 zend_result dom_characterdata_length_read(dom_object *obj, zval *retval)
85 {
86 	DOM_PROP_NODE(xmlNodePtr, nodep, obj);
87 
88 	long length = 0;
89 	if (nodep->content) {
90 		length = xmlUTF8Strlen(nodep->content);
91 	}
92 
93 	ZVAL_LONG(retval, length);
94 
95 	return SUCCESS;
96 }
97 
98 /* }}} */
99 
100 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-6531BCCF
101 Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-substringdata
102 Since:
103 */
PHP_METHOD(DOMCharacterData,substringData)104 PHP_METHOD(DOMCharacterData, substringData)
105 {
106 	zval *id;
107 	xmlChar *cur;
108 	xmlChar *substring;
109 	xmlNodePtr node;
110 	zend_long offset_input, count_input;
111 	unsigned int count, offset;
112 	int length;
113 	dom_object *intern;
114 
115 	id = ZEND_THIS;
116 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &offset_input, &count_input) == FAILURE) {
117 		RETURN_THROWS();
118 	}
119 
120 	DOM_GET_OBJ(node, id, xmlNodePtr, intern);
121 
122 	cur = node->content;
123 	if (cur == NULL) {
124 		/* TODO: is this even possible? */
125 		cur = BAD_CAST "";
126 	}
127 
128 	length = xmlUTF8Strlen(cur);
129 	if (ZEND_LONG_INT_OVFL(offset_input) || ZEND_LONG_INT_OVFL(count_input)) {
130 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
131 		RETURN_FALSE;
132 	}
133 
134 	if (!dom_convert_number_unsigned(intern, offset_input, &offset) || !dom_convert_number_unsigned(intern, count_input, &count)) {
135 		RETURN_FALSE;
136 	}
137 
138 	if (offset > length) {
139 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
140 		RETURN_FALSE;
141 	}
142 
143 	if (count > length - offset) {
144 		count = length - offset;
145 	}
146 
147 	substring = xmlUTF8Strsub(cur, (int)offset, (int)count);
148 
149 	if (substring) {
150 		RETVAL_STRING((char *) substring);
151 		xmlFree(substring);
152 	} else {
153 		RETVAL_EMPTY_STRING();
154 	}
155 }
156 /* }}} end dom_characterdata_substring_data */
157 
158 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-32791A2F
159 Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-appenddata
160 Since:
161 */
dom_character_data_append_data(INTERNAL_FUNCTION_PARAMETERS,bool return_true)162 static void dom_character_data_append_data(INTERNAL_FUNCTION_PARAMETERS, bool return_true)
163 {
164 	zval *id;
165 	xmlNode *nodep;
166 	dom_object *intern;
167 	char *arg;
168 	size_t arg_len;
169 
170 	id = ZEND_THIS;
171 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s", &arg, &arg_len) == FAILURE) {
172 		RETURN_THROWS();
173 	}
174 
175 	DOM_GET_OBJ(nodep, id, xmlNodePtr, intern);
176 	xmlTextConcat(nodep, BAD_CAST arg, arg_len);
177 	if (return_true) {
178 		RETURN_TRUE;
179 	}
180 }
181 
PHP_METHOD(DOMCharacterData,appendData)182 PHP_METHOD(DOMCharacterData, appendData)
183 {
184 	dom_character_data_append_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
185 }
186 
PHP_METHOD(DOM_CharacterData,appendData)187 PHP_METHOD(DOM_CharacterData, appendData)
188 {
189 	dom_character_data_append_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
190 }
191 /* }}} end dom_characterdata_append_data */
192 
193 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-3EDB695F
194 Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-insertdata
195 Since:
196 */
dom_character_data_insert_data(INTERNAL_FUNCTION_PARAMETERS,bool return_true)197 static void dom_character_data_insert_data(INTERNAL_FUNCTION_PARAMETERS, bool return_true)
198 {
199 	zval *id;
200 	xmlChar		*cur, *first, *second;
201 	xmlNodePtr  node;
202 	char		*arg;
203 	zend_long        offset_input;
204 	unsigned int offset;
205 	int         length;
206 	size_t arg_len;
207 	dom_object	*intern;
208 
209 	id = ZEND_THIS;
210 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ls", &offset_input, &arg, &arg_len) == FAILURE) {
211 		RETURN_THROWS();
212 	}
213 
214 	DOM_GET_OBJ(node, id, xmlNodePtr, intern);
215 
216 	cur = node->content;
217 	if (cur == NULL) {
218 		/* TODO: is this even possible? */
219 		cur = BAD_CAST "";
220 	}
221 
222 	length = xmlUTF8Strlen(cur);
223 
224 	if (ZEND_LONG_INT_OVFL(offset_input)) {
225 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
226 		RETURN_FALSE;
227 	}
228 
229 	if (!dom_convert_number_unsigned(intern, offset_input, &offset)) {
230 		RETURN_FALSE;
231 	}
232 
233 	if (offset > length) {
234 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
235 		RETURN_FALSE;
236 	}
237 
238 	first = xmlUTF8Strndup(cur, (int)offset);
239 	second = xmlUTF8Strsub(cur, (int)offset, length - (int)offset);
240 
241 	xmlNodeSetContent(node, first);
242 	xmlNodeAddContent(node, BAD_CAST arg);
243 	xmlNodeAddContent(node, second);
244 
245 	xmlFree(first);
246 	xmlFree(second);
247 
248 	if (return_true) {
249 		RETURN_TRUE;
250 	}
251 }
252 
PHP_METHOD(DOMCharacterData,insertData)253 PHP_METHOD(DOMCharacterData, insertData)
254 {
255 	dom_character_data_insert_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
256 }
257 
PHP_METHOD(DOM_CharacterData,insertData)258 PHP_METHOD(DOM_CharacterData, insertData)
259 {
260 	dom_character_data_insert_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
261 }
262 /* }}} end dom_characterdata_insert_data */
263 
264 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-7C603781
265 Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-deletedata
266 Since:
267 */
dom_character_data_delete_data(INTERNAL_FUNCTION_PARAMETERS,bool return_true)268 static void dom_character_data_delete_data(INTERNAL_FUNCTION_PARAMETERS, bool return_true)
269 {
270 	zval *id;
271 	xmlChar    *cur, *substring, *second;
272 	xmlNodePtr  node;
273 	zend_long        offset, count_input;
274 	unsigned int count;
275 	int         length;
276 	dom_object	*intern;
277 
278 	id = ZEND_THIS;
279 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &offset, &count_input) == FAILURE) {
280 		RETURN_THROWS();
281 	}
282 
283 	DOM_GET_OBJ(node, id, xmlNodePtr, intern);
284 
285 	cur = node->content;
286 	if (cur == NULL) {
287 		/* TODO: is this even possible? */
288 		cur = BAD_CAST "";
289 	}
290 
291 	length = xmlUTF8Strlen(cur);
292 
293 	if (offset < 0 || ZEND_LONG_INT_OVFL(offset) || ZEND_LONG_INT_OVFL(count_input) || offset > length) {
294 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
295 		RETURN_FALSE;
296 	}
297 
298 	if (!dom_convert_number_unsigned(intern, count_input, &count)) {
299 		RETURN_FALSE;
300 	}
301 
302 	if (offset > 0) {
303 		substring = xmlUTF8Strsub(cur, 0, (int)offset);
304 	} else {
305 		substring = NULL;
306 	}
307 
308 	if (count > length - offset) {
309 		count = length - offset;
310 	}
311 
312 	second = xmlUTF8Strsub(cur, (int)offset + (int)count, length - (int)offset);
313 	substring = xmlStrcat(substring, second);
314 
315 	xmlNodeSetContent(node, substring);
316 
317 	xmlFree(second);
318 	xmlFree(substring);
319 
320 	if (return_true) {
321 		RETURN_TRUE;
322 	}
323 }
324 
PHP_METHOD(DOMCharacterData,deleteData)325 PHP_METHOD(DOMCharacterData, deleteData)
326 {
327 	dom_character_data_delete_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
328 }
329 
PHP_METHOD(DOM_CharacterData,deleteData)330 PHP_METHOD(DOM_CharacterData, deleteData)
331 {
332 	dom_character_data_delete_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
333 }
334 /* }}} end dom_characterdata_delete_data */
335 
336 /* {{{ URL: http://www.w3.org/TR/2003/WD-DOM-Level-3-Core-20030226/DOM3-Core.html#core-ID-E5CBA7FB
337 Modern spec URL: https://dom.spec.whatwg.org/#dom-characterdata-replacedata
338 Since:
339 */
dom_character_data_replace_data(INTERNAL_FUNCTION_PARAMETERS,bool return_true)340 static void dom_character_data_replace_data(INTERNAL_FUNCTION_PARAMETERS, bool return_true)
341 {
342 	zval *id;
343 	xmlChar		*cur, *substring, *second = NULL;
344 	xmlNodePtr  node;
345 	char		*arg;
346 	zend_long        offset, count_input;
347 	unsigned int count;
348 	int         length;
349 	size_t arg_len;
350 	dom_object	*intern;
351 
352 	id = ZEND_THIS;
353 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "lls", &offset, &count_input, &arg, &arg_len) == FAILURE) {
354 		RETURN_THROWS();
355 	}
356 
357 	DOM_GET_OBJ(node, id, xmlNodePtr, intern);
358 
359 	cur = node->content;
360 	if (cur == NULL) {
361 		/* TODO: is this even possible? */
362 		cur = BAD_CAST "";
363 	}
364 
365 	length = xmlUTF8Strlen(cur);
366 
367 	if (offset < 0 || ZEND_LONG_INT_OVFL(offset) || ZEND_LONG_INT_OVFL(count_input) || offset > length) {
368 		php_dom_throw_error(INDEX_SIZE_ERR, dom_get_strict_error(intern->document));
369 		RETURN_FALSE;
370 	}
371 
372 	if (!dom_convert_number_unsigned(intern, count_input, &count)) {
373 		RETURN_FALSE;
374 	}
375 
376 	if (offset > 0) {
377 		substring = xmlUTF8Strsub(cur, 0, (int)offset);
378 	} else {
379 		substring = NULL;
380 	}
381 
382 	if (count > length - offset) {
383 		count = length - offset;
384 	}
385 
386 	if (offset < length) {
387 		second = xmlUTF8Strsub(cur, (int)offset + count, length - (int)offset);
388 	}
389 
390 	substring = xmlStrcat(substring, BAD_CAST arg);
391 	substring = xmlStrcat(substring, second);
392 
393 	xmlNodeSetContent(node, substring);
394 
395 	if (second) {
396 		xmlFree(second);
397 	}
398 	xmlFree(substring);
399 
400 	if (return_true) {
401 		RETURN_TRUE;
402 	}
403 }
404 
PHP_METHOD(DOMCharacterData,replaceData)405 PHP_METHOD(DOMCharacterData, replaceData)
406 {
407 	dom_character_data_replace_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
408 }
409 
PHP_METHOD(DOM_CharacterData,replaceData)410 PHP_METHOD(DOM_CharacterData, replaceData)
411 {
412 	dom_character_data_replace_data(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
413 }
414 /* }}} end dom_characterdata_replace_data */
415 
416 #endif
417