xref: /PHP-7.4/ext/libxml/libxml.c (revision f15f8fc5)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Authors: Shane Caraveo <shane@php.net>                               |
16    |          Wez Furlong <wez@thebrainroom.com>                          |
17    +----------------------------------------------------------------------+
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include "php.h"
25 #include "SAPI.h"
26 
27 #include "zend_variables.h"
28 #include "ext/standard/php_string.h"
29 #include "ext/standard/info.h"
30 #include "ext/standard/file.h"
31 
32 #if HAVE_LIBXML
33 
34 #include <libxml/parser.h>
35 #include <libxml/parserInternals.h>
36 #include <libxml/tree.h>
37 #include <libxml/uri.h>
38 #include <libxml/xmlerror.h>
39 #include <libxml/xmlsave.h>
40 #ifdef LIBXML_SCHEMAS_ENABLED
41 #include <libxml/relaxng.h>
42 #include <libxml/xmlschemas.h>
43 #endif
44 
45 #include "php_libxml.h"
46 
47 #define PHP_LIBXML_ERROR 0
48 #define PHP_LIBXML_CTX_ERROR 1
49 #define PHP_LIBXML_CTX_WARNING 2
50 
51 /* a true global for initialization */
52 static int _php_libxml_initialized = 0;
53 static int _php_libxml_per_request_initialization = 1;
54 static xmlExternalEntityLoader _php_libxml_default_entity_loader;
55 
56 typedef struct _php_libxml_func_handler {
57 	php_libxml_export_node export_func;
58 } php_libxml_func_handler;
59 
60 static HashTable php_libxml_exports;
61 
62 static ZEND_DECLARE_MODULE_GLOBALS(libxml)
63 static PHP_GINIT_FUNCTION(libxml);
64 
65 static PHP_FUNCTION(libxml_set_streams_context);
66 static PHP_FUNCTION(libxml_use_internal_errors);
67 static PHP_FUNCTION(libxml_get_last_error);
68 static PHP_FUNCTION(libxml_clear_errors);
69 static PHP_FUNCTION(libxml_get_errors);
70 static PHP_FUNCTION(libxml_set_external_entity_loader);
71 static PHP_FUNCTION(libxml_disable_entity_loader);
72 
73 static zend_class_entry *libxmlerror_class_entry;
74 
75 /* {{{ dynamically loadable module stuff */
76 #ifdef COMPILE_DL_LIBXML
77 #ifdef ZTS
78 ZEND_TSRMLS_CACHE_DEFINE()
79 #endif
80 ZEND_GET_MODULE(libxml)
81 #endif /* COMPILE_DL_LIBXML */
82 /* }}} */
83 
84 /* {{{ function prototypes */
85 static PHP_MINIT_FUNCTION(libxml);
86 static PHP_RINIT_FUNCTION(libxml);
87 static PHP_RSHUTDOWN_FUNCTION(libxml);
88 static PHP_MSHUTDOWN_FUNCTION(libxml);
89 static PHP_MINFO_FUNCTION(libxml);
90 static int php_libxml_post_deactivate(void);
91 
92 /* }}} */
93 
94 /* {{{ arginfo */
95 ZEND_BEGIN_ARG_INFO(arginfo_libxml_set_streams_context, 0)
96 	ZEND_ARG_INFO(0, context)
97 ZEND_END_ARG_INFO()
98 
99 ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_use_internal_errors, 0, 0, 0)
100 	ZEND_ARG_INFO(0, use_errors)
101 ZEND_END_ARG_INFO()
102 
103 ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_last_error, 0)
104 ZEND_END_ARG_INFO()
105 
106 ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_errors, 0)
107 ZEND_END_ARG_INFO()
108 
109 ZEND_BEGIN_ARG_INFO(arginfo_libxml_clear_errors, 0)
110 ZEND_END_ARG_INFO()
111 
112 ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_disable_entity_loader, 0, 0, 0)
113 	ZEND_ARG_INFO(0, disable)
114 ZEND_END_ARG_INFO()
115 
116 ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_set_external_entity_loader, 0, 0, 1)
117 	ZEND_ARG_INFO(0, resolver_function)
118 ZEND_END_ARG_INFO()
119 /* }}} */
120 
121 /* {{{ extension definition structures */
122 static const zend_function_entry libxml_functions[] = {
123 	PHP_FE(libxml_set_streams_context, arginfo_libxml_set_streams_context)
124 	PHP_FE(libxml_use_internal_errors, arginfo_libxml_use_internal_errors)
125 	PHP_FE(libxml_get_last_error, arginfo_libxml_get_last_error)
126 	PHP_FE(libxml_clear_errors, arginfo_libxml_clear_errors)
127 	PHP_FE(libxml_get_errors, arginfo_libxml_get_errors)
128 	PHP_FE(libxml_disable_entity_loader, arginfo_libxml_disable_entity_loader)
129 	PHP_FE(libxml_set_external_entity_loader, arginfo_libxml_set_external_entity_loader)
130 	PHP_FE_END
131 };
132 
133 zend_module_entry libxml_module_entry = {
134 	STANDARD_MODULE_HEADER,
135 	"libxml",                /* extension name */
136 	libxml_functions,        /* extension function list */
137 	PHP_MINIT(libxml),       /* extension-wide startup function */
138 	PHP_MSHUTDOWN(libxml),   /* extension-wide shutdown function */
139 	PHP_RINIT(libxml),       /* per-request startup function */
140 	PHP_RSHUTDOWN(libxml),   /* per-request shutdown function */
141 	PHP_MINFO(libxml),       /* information function */
142 	PHP_LIBXML_VERSION,
143 	PHP_MODULE_GLOBALS(libxml), /* globals descriptor */
144 	PHP_GINIT(libxml),          /* globals ctor */
145 	NULL,                       /* globals dtor */
146 	php_libxml_post_deactivate, /* post deactivate */
147 	STANDARD_MODULE_PROPERTIES_EX
148 };
149 
150 /* }}} */
151 
152 /* {{{ internal functions for interoperability */
php_libxml_clear_object(php_libxml_node_object * object)153 static int php_libxml_clear_object(php_libxml_node_object *object)
154 {
155 	if (object->properties) {
156 		object->properties = NULL;
157 	}
158 	php_libxml_decrement_node_ptr(object);
159 	return php_libxml_decrement_doc_ref(object);
160 }
161 
php_libxml_unregister_node(xmlNodePtr nodep)162 static int php_libxml_unregister_node(xmlNodePtr nodep)
163 {
164 	php_libxml_node_object *wrapper;
165 
166 	php_libxml_node_ptr *nodeptr = nodep->_private;
167 
168 	if (nodeptr != NULL) {
169 		wrapper = nodeptr->_private;
170 		if (wrapper) {
171 			php_libxml_clear_object(wrapper);
172 		} else {
173 			if (nodeptr->node != NULL && nodeptr->node->type != XML_DOCUMENT_NODE) {
174 				nodeptr->node->_private = NULL;
175 			}
176 			nodeptr->node = NULL;
177 		}
178 	}
179 
180 	return -1;
181 }
182 
php_libxml_node_free(xmlNodePtr node)183 static void php_libxml_node_free(xmlNodePtr node)
184 {
185 	if(node) {
186 		if (node->_private != NULL) {
187 			((php_libxml_node_ptr *) node->_private)->node = NULL;
188 		}
189 		switch (node->type) {
190 			case XML_ATTRIBUTE_NODE:
191 				xmlFreeProp((xmlAttrPtr) node);
192 				break;
193 			case XML_ENTITY_DECL:
194 			case XML_ELEMENT_DECL:
195 			case XML_ATTRIBUTE_DECL:
196 				break;
197 			case XML_NOTATION_NODE:
198 				/* These require special handling */
199 				if (node->name != NULL) {
200 					xmlFree((char *) node->name);
201 				}
202 				if (((xmlEntityPtr) node)->ExternalID != NULL) {
203 					xmlFree((char *) ((xmlEntityPtr) node)->ExternalID);
204 				}
205 				if (((xmlEntityPtr) node)->SystemID != NULL) {
206 					xmlFree((char *) ((xmlEntityPtr) node)->SystemID);
207 				}
208 				xmlFree(node);
209 				break;
210 			case XML_NAMESPACE_DECL:
211 				if (node->ns) {
212 					xmlFreeNs(node->ns);
213 					node->ns = NULL;
214 				}
215 				node->type = XML_ELEMENT_NODE;
216 			default:
217 				xmlFreeNode(node);
218 		}
219 	}
220 }
221 
php_libxml_node_free_list(xmlNodePtr node)222 PHP_LIBXML_API void php_libxml_node_free_list(xmlNodePtr node)
223 {
224 	xmlNodePtr curnode;
225 
226 	if (node != NULL) {
227 		curnode = node;
228 		while (curnode != NULL) {
229 			node = curnode;
230 			switch (node->type) {
231 				/* Skip property freeing for the following types */
232 				case XML_NOTATION_NODE:
233 				case XML_ENTITY_DECL:
234 					break;
235 				case XML_ENTITY_REF_NODE:
236 					php_libxml_node_free_list((xmlNodePtr) node->properties);
237 					break;
238 				case XML_ATTRIBUTE_NODE:
239 						if ((node->doc != NULL) && (((xmlAttrPtr) node)->atype == XML_ATTRIBUTE_ID)) {
240 							xmlRemoveID(node->doc, (xmlAttrPtr) node);
241 						}
242 				case XML_ATTRIBUTE_DECL:
243 				case XML_DTD_NODE:
244 				case XML_DOCUMENT_TYPE_NODE:
245 				case XML_NAMESPACE_DECL:
246 				case XML_TEXT_NODE:
247 					php_libxml_node_free_list(node->children);
248 					break;
249 				default:
250 					php_libxml_node_free_list(node->children);
251 					php_libxml_node_free_list((xmlNodePtr) node->properties);
252 			}
253 
254 			curnode = node->next;
255 			xmlUnlinkNode(node);
256 			if (php_libxml_unregister_node(node) == 0) {
257 				node->doc = NULL;
258 			}
259 			php_libxml_node_free(node);
260 		}
261 	}
262 }
263 
264 /* }}} */
265 
266 /* {{{ startup, shutdown and info functions */
PHP_GINIT_FUNCTION(libxml)267 static PHP_GINIT_FUNCTION(libxml)
268 {
269 #if defined(COMPILE_DL_LIBXML) && defined(ZTS)
270 	ZEND_TSRMLS_CACHE_UPDATE();
271 #endif
272 	ZVAL_UNDEF(&libxml_globals->stream_context);
273 	libxml_globals->error_buffer.s = NULL;
274 	libxml_globals->error_list = NULL;
275 	ZVAL_UNDEF(&libxml_globals->entity_loader.object);
276 	libxml_globals->entity_loader.fci.size = 0;
277 	libxml_globals->entity_loader_disabled = 0;
278 }
279 
_php_libxml_destroy_fci(zend_fcall_info * fci,zval * object)280 static void _php_libxml_destroy_fci(zend_fcall_info *fci, zval *object)
281 {
282 	if (fci->size > 0) {
283 		zval_ptr_dtor(&fci->function_name);
284 		fci->size = 0;
285 	}
286 	if (!Z_ISUNDEF_P(object)) {
287 		zval_ptr_dtor(object);
288 		ZVAL_UNDEF(object);
289 	}
290 }
291 
292 /* Channel libxml file io layer through the PHP streams subsystem.
293  * This allows use of ftps:// and https:// urls */
294 
php_libxml_streams_IO_open_wrapper(const char * filename,const char * mode,const int read_only)295 static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char *mode, const int read_only)
296 {
297 	php_stream_statbuf ssbuf;
298 	php_stream_context *context = NULL;
299 	php_stream_wrapper *wrapper = NULL;
300 	char *resolved_path;
301 	const char *path_to_open = NULL;
302 	void *ret_val = NULL;
303 	int isescaped=0;
304 	xmlURI *uri;
305 
306 	if (strstr(filename, "%00")) {
307 		php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes");
308 		return NULL;
309 	}
310 
311 	uri = xmlParseURI(filename);
312 	if (uri && (uri->scheme == NULL ||
313 			(xmlStrncmp(BAD_CAST uri->scheme, BAD_CAST "file", 4) == 0))) {
314 		resolved_path = xmlURIUnescapeString(filename, 0, NULL);
315 		isescaped = 1;
316 #if LIBXML_VERSION >= 20902 && defined(PHP_WIN32)
317 		/* Libxml 2.9.2 prefixes local paths with file:/ instead of file://,
318 			thus the php stream wrapper will fail on a valid case. For this
319 			reason the prefix is rather better cut off. */
320 		{
321 			size_t pre_len = sizeof("file:/") - 1;
322 
323 			if (strncasecmp(resolved_path, "file:/", pre_len) == 0
324 				&& '/' != resolved_path[pre_len]) {
325 				xmlChar *tmp = xmlStrdup(resolved_path + pre_len);
326 				xmlFree(resolved_path);
327 				resolved_path = tmp;
328 			}
329 		}
330 #endif
331 	} else {
332 		resolved_path = (char *)filename;
333 	}
334 
335 	if (uri) {
336 		xmlFreeURI(uri);
337 	}
338 
339 	if (resolved_path == NULL) {
340 		return NULL;
341 	}
342 
343 	/* logic copied from _php_stream_stat, but we only want to fail
344 	   if the wrapper supports stat, otherwise, figure it out from
345 	   the open.  This logic is only to support hiding warnings
346 	   that the streams layer puts out at times, but for libxml we
347 	   may try to open files that don't exist, but it is not a failure
348 	   in xml processing (eg. DTD files)  */
349 	wrapper = php_stream_locate_url_wrapper(resolved_path, &path_to_open, 0);
350 	if (wrapper && read_only && wrapper->wops->url_stat) {
351 		if (wrapper->wops->url_stat(wrapper, path_to_open, PHP_STREAM_URL_STAT_QUIET, &ssbuf, NULL) == -1) {
352 			if (isescaped) {
353 				xmlFree(resolved_path);
354 			}
355 			return NULL;
356 		}
357 	}
358 
359 	context = php_stream_context_from_zval(Z_ISUNDEF(LIBXML(stream_context))? NULL : &LIBXML(stream_context), 0);
360 
361 	ret_val = php_stream_open_wrapper_ex(path_to_open, (char *)mode, REPORT_ERRORS, NULL, context);
362 	if (ret_val) {
363 		/* Prevent from closing this by fclose() */
364 		((php_stream*)ret_val)->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
365 	}
366 	if (isescaped) {
367 		xmlFree(resolved_path);
368 	}
369 	return ret_val;
370 }
371 
php_libxml_streams_IO_open_read_wrapper(const char * filename)372 static void *php_libxml_streams_IO_open_read_wrapper(const char *filename)
373 {
374 	return php_libxml_streams_IO_open_wrapper(filename, "rb", 1);
375 }
376 
php_libxml_streams_IO_open_write_wrapper(const char * filename)377 static void *php_libxml_streams_IO_open_write_wrapper(const char *filename)
378 {
379 	return php_libxml_streams_IO_open_wrapper(filename, "wb", 0);
380 }
381 
php_libxml_streams_IO_read(void * context,char * buffer,int len)382 static int php_libxml_streams_IO_read(void *context, char *buffer, int len)
383 {
384 	return php_stream_read((php_stream*)context, buffer, len);
385 }
386 
php_libxml_streams_IO_write(void * context,const char * buffer,int len)387 static int php_libxml_streams_IO_write(void *context, const char *buffer, int len)
388 {
389 	return php_stream_write((php_stream*)context, buffer, len);
390 }
391 
php_libxml_streams_IO_close(void * context)392 static int php_libxml_streams_IO_close(void *context)
393 {
394 	return php_stream_close((php_stream*)context);
395 }
396 
397 static xmlParserInputBufferPtr
php_libxml_input_buffer_create_filename(const char * URI,xmlCharEncoding enc)398 php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc)
399 {
400 	xmlParserInputBufferPtr ret;
401 	void *context = NULL;
402 
403 	if (LIBXML(entity_loader_disabled)) {
404 		return NULL;
405 	}
406 
407 	if (URI == NULL)
408 		return(NULL);
409 
410 	context = php_libxml_streams_IO_open_read_wrapper(URI);
411 
412 	if (context == NULL) {
413 		return(NULL);
414 	}
415 
416 	/* Check if there's been an external transport protocol with an encoding information */
417 	if (enc == XML_CHAR_ENCODING_NONE) {
418 		php_stream *s  = (php_stream *) context;
419 
420 		if (Z_TYPE(s->wrapperdata) == IS_ARRAY) {
421 			zval *header;
422 
423 			ZEND_HASH_FOREACH_VAL_IND(Z_ARRVAL(s->wrapperdata), header) {
424 				const char buf[] = "Content-Type:";
425 				if (Z_TYPE_P(header) == IS_STRING &&
426 						!zend_binary_strncasecmp(Z_STRVAL_P(header), Z_STRLEN_P(header), buf, sizeof(buf)-1, sizeof(buf)-1)) {
427 					char *needle = estrdup("charset=");
428 					char *haystack = estrndup(Z_STRVAL_P(header), Z_STRLEN_P(header));
429 					char *encoding = php_stristr(haystack, needle, Z_STRLEN_P(header), sizeof("charset=")-1);
430 
431 					if (encoding) {
432 						char *end;
433 
434 						encoding += sizeof("charset=")-1;
435 						if (*encoding == '"') {
436 							encoding++;
437 						}
438 						end = strchr(encoding, ';');
439 						if (end == NULL) {
440 							end = encoding + strlen(encoding);
441 						}
442 						end--; /* end == encoding-1 isn't a buffer underrun */
443 						while (*end == ' ' || *end == '\t') {
444 							end--;
445 						}
446 						if (*end == '"') {
447 							end--;
448 						}
449 						if (encoding >= end) continue;
450 						*(end+1) = '\0';
451 						enc = xmlParseCharEncoding(encoding);
452 						if (enc <= XML_CHAR_ENCODING_NONE) {
453 							enc = XML_CHAR_ENCODING_NONE;
454 						}
455 					}
456 					efree(haystack);
457 					efree(needle);
458 					break; /* found content-type */
459 				}
460 			} ZEND_HASH_FOREACH_END();
461 		}
462 	}
463 
464 	/* Allocate the Input buffer front-end. */
465 	ret = xmlAllocParserInputBuffer(enc);
466 	if (ret != NULL) {
467 		ret->context = context;
468 		ret->readcallback = php_libxml_streams_IO_read;
469 		ret->closecallback = php_libxml_streams_IO_close;
470 	} else
471 		php_libxml_streams_IO_close(context);
472 
473 	return(ret);
474 }
475 
476 static xmlOutputBufferPtr
php_libxml_output_buffer_create_filename(const char * URI,xmlCharEncodingHandlerPtr encoder,int compression ATTRIBUTE_UNUSED)477 php_libxml_output_buffer_create_filename(const char *URI,
478                               xmlCharEncodingHandlerPtr encoder,
479                               int compression ATTRIBUTE_UNUSED)
480 {
481 	xmlOutputBufferPtr ret;
482 	xmlURIPtr puri;
483 	void *context = NULL;
484 	char *unescaped = NULL;
485 
486 	if (URI == NULL)
487 		return(NULL);
488 
489 	if (strstr(URI, "%00")) {
490 		php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes");
491 		return NULL;
492 	}
493 
494 	puri = xmlParseURI(URI);
495 	if (puri != NULL) {
496 		if (puri->scheme != NULL)
497 			unescaped = xmlURIUnescapeString(URI, 0, NULL);
498 		xmlFreeURI(puri);
499 	}
500 
501 	if (unescaped != NULL) {
502 		context = php_libxml_streams_IO_open_write_wrapper(unescaped);
503 		xmlFree(unescaped);
504 	}
505 
506 	/* try with a non-escaped URI this may be a strange filename */
507 	if (context == NULL) {
508 		context = php_libxml_streams_IO_open_write_wrapper(URI);
509 	}
510 
511 	if (context == NULL) {
512 		return(NULL);
513 	}
514 
515 	/* Allocate the Output buffer front-end. */
516 	ret = xmlAllocOutputBuffer(encoder);
517 	if (ret != NULL) {
518 		ret->context = context;
519 		ret->writecallback = php_libxml_streams_IO_write;
520 		ret->closecallback = php_libxml_streams_IO_close;
521 	}
522 
523 	return(ret);
524 }
525 
_php_libxml_free_error(xmlErrorPtr error)526 static int _php_libxml_free_error(xmlErrorPtr error)
527 {
528 	/* This will free the libxml alloc'd memory */
529 	xmlResetError(error);
530 	return 1;
531 }
532 
_php_list_set_error_structure(xmlErrorPtr error,const char * msg)533 static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg)
534 {
535 	xmlError error_copy;
536 	int ret;
537 
538 
539 	memset(&error_copy, 0, sizeof(xmlError));
540 
541 	if (error) {
542 		ret = xmlCopyError(error, &error_copy);
543 	} else {
544 		error_copy.domain = 0;
545 		error_copy.code = XML_ERR_INTERNAL_ERROR;
546 		error_copy.level = XML_ERR_ERROR;
547 		error_copy.line = 0;
548 		error_copy.node = NULL;
549 		error_copy.int1 = 0;
550 		error_copy.int2 = 0;
551 		error_copy.ctxt = NULL;
552 		error_copy.message = (char*)xmlStrdup((xmlChar*)msg);
553 		error_copy.file = NULL;
554 		error_copy.str1 = NULL;
555 		error_copy.str2 = NULL;
556 		error_copy.str3 = NULL;
557 		ret = 0;
558 	}
559 
560 	if (ret == 0) {
561 		zend_llist_add_element(LIBXML(error_list), &error_copy);
562 	}
563 }
564 
php_libxml_ctx_error_level(int level,void * ctx,const char * msg)565 static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg)
566 {
567 	xmlParserCtxtPtr parser;
568 
569 	parser = (xmlParserCtxtPtr) ctx;
570 
571 	if (parser != NULL && parser->input != NULL) {
572 		if (parser->input->filename) {
573 			php_error_docref(NULL, level, "%s in %s, line: %d", msg, parser->input->filename, parser->input->line);
574 		} else {
575 			php_error_docref(NULL, level, "%s in Entity, line: %d", msg, parser->input->line);
576 		}
577 	}
578 }
579 
php_libxml_issue_error(int level,const char * msg)580 void php_libxml_issue_error(int level, const char *msg)
581 {
582 	if (LIBXML(error_list)) {
583 		_php_list_set_error_structure(NULL, msg);
584 	} else {
585 		php_error_docref(NULL, level, "%s", msg);
586 	}
587 }
588 
php_libxml_internal_error_handler(int error_type,void * ctx,const char ** msg,va_list ap)589 static void php_libxml_internal_error_handler(int error_type, void *ctx, const char **msg, va_list ap)
590 {
591 	char *buf;
592 	int len, len_iter, output = 0;
593 
594 
595 	len = vspprintf(&buf, 0, *msg, ap);
596 	len_iter = len;
597 
598 	/* remove any trailing \n */
599 	while (len_iter && buf[--len_iter] == '\n') {
600 		buf[len_iter] = '\0';
601 		output = 1;
602 	}
603 
604 	smart_str_appendl(&LIBXML(error_buffer), buf, len);
605 
606 	efree(buf);
607 
608 	if (output == 1) {
609 		if (LIBXML(error_list)) {
610 			_php_list_set_error_structure(NULL, ZSTR_VAL(LIBXML(error_buffer).s));
611 		} else {
612 			switch (error_type) {
613 				case PHP_LIBXML_CTX_ERROR:
614 					php_libxml_ctx_error_level(E_WARNING, ctx, ZSTR_VAL(LIBXML(error_buffer).s));
615 					break;
616 				case PHP_LIBXML_CTX_WARNING:
617 					php_libxml_ctx_error_level(E_NOTICE, ctx, ZSTR_VAL(LIBXML(error_buffer).s));
618 					break;
619 				default:
620 					php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(LIBXML(error_buffer).s));
621 			}
622 		}
623 		smart_str_free(&LIBXML(error_buffer));
624 	}
625 }
626 
_php_libxml_external_entity_loader(const char * URL,const char * ID,xmlParserCtxtPtr context)627 static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,
628 		const char *ID, xmlParserCtxtPtr context)
629 {
630 	xmlParserInputPtr	ret			= NULL;
631 	const char			*resource	= NULL;
632 	zval 				*ctxzv, retval;
633 	zval				params[3];
634 	int					status;
635 	zend_fcall_info		*fci;
636 
637 	fci = &LIBXML(entity_loader).fci;
638 
639 	if (fci->size == 0) {
640 		/* no custom user-land callback set up; delegate to original loader */
641 		return _php_libxml_default_entity_loader(URL, ID, context);
642 	}
643 
644 	if (ID != NULL) {
645 		ZVAL_STRING(&params[0], ID);
646 	} else {
647 		ZVAL_NULL(&params[0]);
648 	}
649 	if (URL != NULL) {
650 		ZVAL_STRING(&params[1], URL);
651 	} else {
652 		ZVAL_NULL(&params[1]);
653 	}
654 	ctxzv = &params[2];
655 	array_init_size(ctxzv, 4);
656 
657 #define ADD_NULL_OR_STRING_KEY(memb) \
658 	if (context->memb == NULL) { \
659 		add_assoc_null_ex(ctxzv, #memb, sizeof(#memb) - 1); \
660 	} else { \
661 		add_assoc_string_ex(ctxzv, #memb, sizeof(#memb) - 1, \
662 				(char *)context->memb); \
663 	}
664 
665 	ADD_NULL_OR_STRING_KEY(directory)
666 	ADD_NULL_OR_STRING_KEY(intSubName)
667 	ADD_NULL_OR_STRING_KEY(extSubURI)
668 	ADD_NULL_OR_STRING_KEY(extSubSystem)
669 
670 #undef ADD_NULL_OR_STRING_KEY
671 
672 	fci->retval	= &retval;
673 	fci->params	= params;
674 	fci->param_count = sizeof(params)/sizeof(*params);
675 	fci->no_separation	= 1;
676 
677 	status = zend_call_function(fci, &LIBXML(entity_loader).fcc);
678 	if (status != SUCCESS || Z_ISUNDEF(retval)) {
679 		php_libxml_ctx_error(context,
680 				"Call to user entity loader callback '%s' has failed",
681 				Z_STRVAL(fci->function_name));
682 	} else {
683 		/*
684 		retval_ptr = *fci->retval_ptr_ptr;
685 		if (retval_ptr == NULL) {
686 			php_libxml_ctx_error(context,
687 					"Call to user entity loader callback '%s' has failed; "
688 					"probably it has thrown an exception",
689 					fci->function_name);
690 		} else */ if (Z_TYPE(retval) == IS_STRING) {
691 is_string:
692 			resource = Z_STRVAL(retval);
693 		} else if (Z_TYPE(retval) == IS_RESOURCE) {
694 			php_stream *stream;
695 			php_stream_from_zval_no_verify(stream, &retval);
696 			if (stream == NULL) {
697 				php_libxml_ctx_error(context,
698 						"The user entity loader callback '%s' has returned a "
699 						"resource, but it is not a stream",
700 						Z_STRVAL(fci->function_name));
701 			} else {
702 				/* TODO: allow storing the encoding in the stream context? */
703 				xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
704 				xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
705 				if (pib == NULL) {
706 					php_libxml_ctx_error(context, "Could not allocate parser "
707 							"input buffer");
708 				} else {
709 					/* make stream not being closed when the zval is freed */
710 					GC_ADDREF(stream->res);
711 					pib->context = stream;
712 					pib->readcallback = php_libxml_streams_IO_read;
713 					pib->closecallback = php_libxml_streams_IO_close;
714 
715 					ret = xmlNewIOInputStream(context, pib, enc);
716 					if (ret == NULL) {
717 						xmlFreeParserInputBuffer(pib);
718 					}
719 				}
720 			}
721 		} else if (Z_TYPE(retval) != IS_NULL) {
722 			/* retval not string nor resource nor null; convert to string */
723 			if (try_convert_to_string(&retval)) {
724 				goto is_string;
725 			}
726 		} /* else is null; don't try anything */
727 	}
728 
729 	if (ret == NULL) {
730 		if (resource == NULL) {
731 			if (ID == NULL) {
732 				ID = "NULL";
733 			}
734 			php_libxml_ctx_error(context,
735 					"Failed to load external entity \"%s\"\n", ID);
736 		} else {
737 			/* we got the resource in the form of a string; open it */
738 			ret = xmlNewInputFromFile(context, resource);
739 		}
740 	}
741 
742 	zval_ptr_dtor(&params[0]);
743 	zval_ptr_dtor(&params[1]);
744 	zval_ptr_dtor(&params[2]);
745 	zval_ptr_dtor(&retval);
746 	return ret;
747 }
748 
_php_libxml_pre_ext_ent_loader(const char * URL,const char * ID,xmlParserCtxtPtr context)749 static xmlParserInputPtr _php_libxml_pre_ext_ent_loader(const char *URL,
750 		const char *ID, xmlParserCtxtPtr context)
751 {
752 
753 	/* Check whether we're running in a PHP context, since the entity loader
754 	 * we've defined is an application level (true global) setting.
755 	 * If we are, we also want to check whether we've finished activating
756 	 * the modules (RINIT phase). Using our external entity loader during a
757 	 * RINIT should not be problem per se (though during MINIT it is, because
758 	 * we don't even have a resource list by then), but then whether one
759 	 * extension would be using the custom external entity loader or not
760 	 * could depend on extension loading order
761 	 * (if _php_libxml_per_request_initialization */
762 	if (xmlGenericError == php_libxml_error_handler && PG(modules_activated)) {
763 		return _php_libxml_external_entity_loader(URL, ID, context);
764 	} else {
765 		return _php_libxml_default_entity_loader(URL, ID, context);
766 	}
767 }
768 
php_libxml_ctx_error(void * ctx,const char * msg,...)769 PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...)
770 {
771 	va_list args;
772 	va_start(args, msg);
773 	php_libxml_internal_error_handler(PHP_LIBXML_CTX_ERROR, ctx, &msg, args);
774 	va_end(args);
775 }
776 
php_libxml_ctx_warning(void * ctx,const char * msg,...)777 PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...)
778 {
779 	va_list args;
780 	va_start(args, msg);
781 	php_libxml_internal_error_handler(PHP_LIBXML_CTX_WARNING, ctx, &msg, args);
782 	va_end(args);
783 }
784 
php_libxml_structured_error_handler(void * userData,xmlErrorPtr error)785 PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error)
786 {
787 	_php_list_set_error_structure(error, NULL);
788 
789 	return;
790 }
791 
php_libxml_error_handler(void * ctx,const char * msg,...)792 PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...)
793 {
794 	va_list args;
795 	va_start(args, msg);
796 	php_libxml_internal_error_handler(PHP_LIBXML_ERROR, ctx, &msg, args);
797 	va_end(args);
798 }
799 
php_libxml_exports_dtor(zval * zv)800 static void php_libxml_exports_dtor(zval *zv)
801 {
802 	free(Z_PTR_P(zv));
803 }
804 
php_libxml_initialize(void)805 PHP_LIBXML_API void php_libxml_initialize(void)
806 {
807 	if (!_php_libxml_initialized) {
808 		/* we should be the only one's to ever init!! */
809 		ZEND_IGNORE_LEAKS_BEGIN();
810 		xmlInitParser();
811 		ZEND_IGNORE_LEAKS_END();
812 
813 		_php_libxml_default_entity_loader = xmlGetExternalEntityLoader();
814 		xmlSetExternalEntityLoader(_php_libxml_pre_ext_ent_loader);
815 
816 		zend_hash_init(&php_libxml_exports, 0, NULL, php_libxml_exports_dtor, 1);
817 
818 		_php_libxml_initialized = 1;
819 	}
820 }
821 
php_libxml_shutdown(void)822 PHP_LIBXML_API void php_libxml_shutdown(void)
823 {
824 	if (_php_libxml_initialized) {
825 #if defined(LIBXML_SCHEMAS_ENABLED)
826 		xmlRelaxNGCleanupTypes();
827 #endif
828 		/* xmlCleanupParser(); */
829 		zend_hash_destroy(&php_libxml_exports);
830 
831 		xmlSetExternalEntityLoader(_php_libxml_default_entity_loader);
832 		_php_libxml_initialized = 0;
833 	}
834 }
835 
php_libxml_switch_context(zval * context,zval * oldcontext)836 PHP_LIBXML_API void php_libxml_switch_context(zval *context, zval *oldcontext)
837 {
838 	if (oldcontext) {
839 		ZVAL_COPY_VALUE(oldcontext, &LIBXML(stream_context));
840 	}
841 	if (context) {
842 		ZVAL_COPY_VALUE(&LIBXML(stream_context), context);
843 	}
844 }
845 
PHP_MINIT_FUNCTION(libxml)846 static PHP_MINIT_FUNCTION(libxml)
847 {
848 	zend_class_entry ce;
849 
850 	php_libxml_initialize();
851 
852 	REGISTER_LONG_CONSTANT("LIBXML_VERSION",			LIBXML_VERSION,			CONST_CS | CONST_PERSISTENT);
853 	REGISTER_STRING_CONSTANT("LIBXML_DOTTED_VERSION",	LIBXML_DOTTED_VERSION,	CONST_CS | CONST_PERSISTENT);
854 	REGISTER_STRING_CONSTANT("LIBXML_LOADED_VERSION",	(char *)xmlParserVersion,		CONST_CS | CONST_PERSISTENT);
855 
856 	/* For use with loading xml */
857 	REGISTER_LONG_CONSTANT("LIBXML_NOENT",		XML_PARSE_NOENT,		CONST_CS | CONST_PERSISTENT);
858 	REGISTER_LONG_CONSTANT("LIBXML_DTDLOAD",	XML_PARSE_DTDLOAD,		CONST_CS | CONST_PERSISTENT);
859 	REGISTER_LONG_CONSTANT("LIBXML_DTDATTR",	XML_PARSE_DTDATTR,		CONST_CS | CONST_PERSISTENT);
860 	REGISTER_LONG_CONSTANT("LIBXML_DTDVALID",	XML_PARSE_DTDVALID,		CONST_CS | CONST_PERSISTENT);
861 	REGISTER_LONG_CONSTANT("LIBXML_NOERROR",	XML_PARSE_NOERROR,		CONST_CS | CONST_PERSISTENT);
862 	REGISTER_LONG_CONSTANT("LIBXML_NOWARNING",	XML_PARSE_NOWARNING,	CONST_CS | CONST_PERSISTENT);
863 	REGISTER_LONG_CONSTANT("LIBXML_NOBLANKS",	XML_PARSE_NOBLANKS,		CONST_CS | CONST_PERSISTENT);
864 	REGISTER_LONG_CONSTANT("LIBXML_XINCLUDE",	XML_PARSE_XINCLUDE,		CONST_CS | CONST_PERSISTENT);
865 	REGISTER_LONG_CONSTANT("LIBXML_NSCLEAN",	XML_PARSE_NSCLEAN,		CONST_CS | CONST_PERSISTENT);
866 	REGISTER_LONG_CONSTANT("LIBXML_NOCDATA",	XML_PARSE_NOCDATA,		CONST_CS | CONST_PERSISTENT);
867 	REGISTER_LONG_CONSTANT("LIBXML_NONET",		XML_PARSE_NONET,		CONST_CS | CONST_PERSISTENT);
868 	REGISTER_LONG_CONSTANT("LIBXML_PEDANTIC",	XML_PARSE_PEDANTIC,		CONST_CS | CONST_PERSISTENT);
869 	REGISTER_LONG_CONSTANT("LIBXML_COMPACT",	XML_PARSE_COMPACT,		CONST_CS | CONST_PERSISTENT);
870 	REGISTER_LONG_CONSTANT("LIBXML_NOXMLDECL",	XML_SAVE_NO_DECL,		CONST_CS | CONST_PERSISTENT);
871 	REGISTER_LONG_CONSTANT("LIBXML_PARSEHUGE",	XML_PARSE_HUGE,			CONST_CS | CONST_PERSISTENT);
872 #if LIBXML_VERSION >= 20900
873 	REGISTER_LONG_CONSTANT("LIBXML_BIGLINES",	XML_PARSE_BIG_LINES,	CONST_CS | CONST_PERSISTENT);
874 #endif
875 	REGISTER_LONG_CONSTANT("LIBXML_NOEMPTYTAG",	LIBXML_SAVE_NOEMPTYTAG,	CONST_CS | CONST_PERSISTENT);
876 
877 	/* Schema validation options */
878 #if defined(LIBXML_SCHEMAS_ENABLED)
879 	REGISTER_LONG_CONSTANT("LIBXML_SCHEMA_CREATE",	XML_SCHEMA_VAL_VC_I_CREATE,	CONST_CS | CONST_PERSISTENT);
880 #endif
881 
882 	/* Additional constants for use with loading html */
883 #if LIBXML_VERSION >= 20707
884 	REGISTER_LONG_CONSTANT("LIBXML_HTML_NOIMPLIED",	HTML_PARSE_NOIMPLIED,		CONST_CS | CONST_PERSISTENT);
885 #endif
886 
887 #if LIBXML_VERSION >= 20708
888 	REGISTER_LONG_CONSTANT("LIBXML_HTML_NODEFDTD",	HTML_PARSE_NODEFDTD,		CONST_CS | CONST_PERSISTENT);
889 #endif
890 
891 	/* Error levels */
892 	REGISTER_LONG_CONSTANT("LIBXML_ERR_NONE",		XML_ERR_NONE,		CONST_CS | CONST_PERSISTENT);
893 	REGISTER_LONG_CONSTANT("LIBXML_ERR_WARNING",	XML_ERR_WARNING,	CONST_CS | CONST_PERSISTENT);
894 	REGISTER_LONG_CONSTANT("LIBXML_ERR_ERROR",		XML_ERR_ERROR,		CONST_CS | CONST_PERSISTENT);
895 	REGISTER_LONG_CONSTANT("LIBXML_ERR_FATAL",		XML_ERR_FATAL,		CONST_CS | CONST_PERSISTENT);
896 
897 	INIT_CLASS_ENTRY(ce, "LibXMLError", NULL);
898 	libxmlerror_class_entry = zend_register_internal_class(&ce);
899 
900 	if (sapi_module.name) {
901 		static const char * const supported_sapis[] = {
902 			"cgi-fcgi",
903 			"litespeed",
904 			NULL
905 		};
906 		const char * const *sapi_name;
907 
908 		for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
909 			if (strcmp(sapi_module.name, *sapi_name) == 0) {
910 				_php_libxml_per_request_initialization = 0;
911 				break;
912 			}
913 		}
914 	}
915 
916 	if (!_php_libxml_per_request_initialization) {
917 		/* report errors via handler rather than stderr */
918 		xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
919 		xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
920 		xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
921 	}
922 
923 	return SUCCESS;
924 }
925 
926 
PHP_RINIT_FUNCTION(libxml)927 static PHP_RINIT_FUNCTION(libxml)
928 {
929 	if (_php_libxml_per_request_initialization) {
930 		/* report errors via handler rather than stderr */
931 		xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
932 		xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
933 		xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
934 	}
935 
936 	/* Enable the entity loader by default. This ensures that
937 	 * other threads/requests that might have disabled the loader
938 	 * do not affect the current request.
939 	 */
940 	LIBXML(entity_loader_disabled) = 0;
941 
942 	return SUCCESS;
943 }
944 
PHP_RSHUTDOWN_FUNCTION(libxml)945 static PHP_RSHUTDOWN_FUNCTION(libxml)
946 {
947 	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
948 
949 	return SUCCESS;
950 }
951 
PHP_MSHUTDOWN_FUNCTION(libxml)952 static PHP_MSHUTDOWN_FUNCTION(libxml)
953 {
954 	if (!_php_libxml_per_request_initialization) {
955 		xmlSetGenericErrorFunc(NULL, NULL);
956 
957 		xmlParserInputBufferCreateFilenameDefault(NULL);
958 		xmlOutputBufferCreateFilenameDefault(NULL);
959 	}
960 	php_libxml_shutdown();
961 
962 	return SUCCESS;
963 }
964 
php_libxml_post_deactivate(void)965 static int php_libxml_post_deactivate(void)
966 {
967 	/* reset libxml generic error handling */
968 	if (_php_libxml_per_request_initialization) {
969 		xmlSetGenericErrorFunc(NULL, NULL);
970 
971 		xmlParserInputBufferCreateFilenameDefault(NULL);
972 		xmlOutputBufferCreateFilenameDefault(NULL);
973 	}
974 	xmlSetStructuredErrorFunc(NULL, NULL);
975 
976 	/* the steam_context resource will be released by resource list destructor */
977 	ZVAL_UNDEF(&LIBXML(stream_context));
978 	smart_str_free(&LIBXML(error_buffer));
979 	if (LIBXML(error_list)) {
980 		zend_llist_destroy(LIBXML(error_list));
981 		efree(LIBXML(error_list));
982 		LIBXML(error_list) = NULL;
983 	}
984 	xmlResetLastError();
985 
986 	return SUCCESS;
987 }
988 
989 
PHP_MINFO_FUNCTION(libxml)990 static PHP_MINFO_FUNCTION(libxml)
991 {
992 	php_info_print_table_start();
993 	php_info_print_table_row(2, "libXML support", "active");
994 	php_info_print_table_row(2, "libXML Compiled Version", LIBXML_DOTTED_VERSION);
995 	php_info_print_table_row(2, "libXML Loaded Version", (char *)xmlParserVersion);
996 	php_info_print_table_row(2, "libXML streams", "enabled");
997 	php_info_print_table_end();
998 }
999 /* }}} */
1000 
1001 /* {{{ proto void libxml_set_streams_context(resource streams_context)
1002    Set the streams context for the next libxml document load or write */
PHP_FUNCTION(libxml_set_streams_context)1003 static PHP_FUNCTION(libxml_set_streams_context)
1004 {
1005 	zval *arg;
1006 
1007 	ZEND_PARSE_PARAMETERS_START(1, 1)
1008 		Z_PARAM_RESOURCE(arg)
1009 	ZEND_PARSE_PARAMETERS_END();
1010 
1011 	if (!Z_ISUNDEF(LIBXML(stream_context))) {
1012 		zval_ptr_dtor(&LIBXML(stream_context));
1013 		ZVAL_UNDEF(&LIBXML(stream_context));
1014 	}
1015 	ZVAL_COPY(&LIBXML(stream_context), arg);
1016 }
1017 /* }}} */
1018 
1019 /* {{{ proto bool libxml_use_internal_errors([boolean use_errors])
1020    Disable libxml errors and allow user to fetch error information as needed */
PHP_FUNCTION(libxml_use_internal_errors)1021 static PHP_FUNCTION(libxml_use_internal_errors)
1022 {
1023 	xmlStructuredErrorFunc current_handler;
1024 	zend_bool use_errors=0, retval;
1025 
1026 	ZEND_PARSE_PARAMETERS_START(0, 1)
1027 		Z_PARAM_OPTIONAL
1028 		Z_PARAM_BOOL(use_errors)
1029 	ZEND_PARSE_PARAMETERS_END();
1030 
1031 	current_handler = xmlStructuredError;
1032 	if (current_handler && current_handler == php_libxml_structured_error_handler) {
1033 		retval = 1;
1034 	} else {
1035 		retval = 0;
1036 	}
1037 
1038 	if (ZEND_NUM_ARGS() == 0) {
1039 		RETURN_BOOL(retval);
1040 	}
1041 
1042 	if (use_errors == 0) {
1043 		xmlSetStructuredErrorFunc(NULL, NULL);
1044 		if (LIBXML(error_list)) {
1045 			zend_llist_destroy(LIBXML(error_list));
1046 			efree(LIBXML(error_list));
1047 			LIBXML(error_list) = NULL;
1048 		}
1049 	} else {
1050 		xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
1051 		if (LIBXML(error_list) == NULL) {
1052 			LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
1053 			zend_llist_init(LIBXML(error_list), sizeof(xmlError), (llist_dtor_func_t) _php_libxml_free_error, 0);
1054 		}
1055 	}
1056 	RETURN_BOOL(retval);
1057 }
1058 /* }}} */
1059 
1060 /* {{{ proto object libxml_get_last_error()
1061    Retrieve last error from libxml */
PHP_FUNCTION(libxml_get_last_error)1062 static PHP_FUNCTION(libxml_get_last_error)
1063 {
1064 	xmlErrorPtr error;
1065 
1066 	error = xmlGetLastError();
1067 
1068 	if (error) {
1069 		object_init_ex(return_value, libxmlerror_class_entry);
1070 		add_property_long(return_value, "level", error->level);
1071 		add_property_long(return_value, "code", error->code);
1072 		add_property_long(return_value, "column", error->int2);
1073 		if (error->message) {
1074 			add_property_string(return_value, "message", error->message);
1075 		} else {
1076 			add_property_stringl(return_value, "message", "", 0);
1077 		}
1078 		if (error->file) {
1079 			add_property_string(return_value, "file", error->file);
1080 		} else {
1081 			add_property_stringl(return_value, "file", "", 0);
1082 		}
1083 		add_property_long(return_value, "line", error->line);
1084 	} else {
1085 		RETURN_FALSE;
1086 	}
1087 }
1088 /* }}} */
1089 
1090 /* {{{ proto object libxml_get_errors()
1091    Retrieve array of errors */
PHP_FUNCTION(libxml_get_errors)1092 static PHP_FUNCTION(libxml_get_errors)
1093 {
1094 
1095 	xmlErrorPtr error;
1096 
1097 	if (LIBXML(error_list)) {
1098 
1099 		array_init(return_value);
1100 		error = zend_llist_get_first(LIBXML(error_list));
1101 
1102 		while (error != NULL) {
1103 			zval z_error;
1104 
1105 			object_init_ex(&z_error, libxmlerror_class_entry);
1106 			add_property_long_ex(&z_error, "level", sizeof("level") - 1, error->level);
1107 			add_property_long_ex(&z_error, "code", sizeof("code") - 1, error->code);
1108 			add_property_long_ex(&z_error, "column", sizeof("column") - 1, error->int2 );
1109 			if (error->message) {
1110 				add_property_string_ex(&z_error, "message", sizeof("message") - 1, error->message);
1111 			} else {
1112 				add_property_stringl_ex(&z_error, "message", sizeof("message") - 1, "", 0);
1113 			}
1114 			if (error->file) {
1115 				add_property_string_ex(&z_error, "file", sizeof("file") - 1, error->file);
1116 			} else {
1117 				add_property_stringl_ex(&z_error, "file", sizeof("file") - 1, "", 0);
1118 			}
1119 			add_property_long_ex(&z_error, "line", sizeof("line") - 1, error->line);
1120 			add_next_index_zval(return_value, &z_error);
1121 
1122 			error = zend_llist_get_next(LIBXML(error_list));
1123 		}
1124 	} else {
1125 		RETURN_EMPTY_ARRAY();
1126 	}
1127 }
1128 /* }}} */
1129 
1130 /* {{{ proto void libxml_clear_errors()
1131    Clear last error from libxml */
PHP_FUNCTION(libxml_clear_errors)1132 static PHP_FUNCTION(libxml_clear_errors)
1133 {
1134 	xmlResetLastError();
1135 	if (LIBXML(error_list)) {
1136 		zend_llist_clean(LIBXML(error_list));
1137 	}
1138 }
1139 /* }}} */
1140 
php_libxml_disable_entity_loader(zend_bool disable)1141 PHP_LIBXML_API zend_bool php_libxml_disable_entity_loader(zend_bool disable) /* {{{ */
1142 {
1143 	zend_bool old = LIBXML(entity_loader_disabled);
1144 
1145 	LIBXML(entity_loader_disabled) = disable;
1146 	return old;
1147 } /* }}} */
1148 
1149 /* {{{ proto bool libxml_disable_entity_loader([boolean disable])
1150    Disable/Enable ability to load external entities */
PHP_FUNCTION(libxml_disable_entity_loader)1151 static PHP_FUNCTION(libxml_disable_entity_loader)
1152 {
1153 	zend_bool disable = 1;
1154 
1155 	ZEND_PARSE_PARAMETERS_START(0, 1)
1156 		Z_PARAM_OPTIONAL
1157 		Z_PARAM_BOOL(disable)
1158 	ZEND_PARSE_PARAMETERS_END();
1159 
1160 	RETURN_BOOL(php_libxml_disable_entity_loader(disable));
1161 }
1162 /* }}} */
1163 
1164 /* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
1165    Changes the default external entity loader */
PHP_FUNCTION(libxml_set_external_entity_loader)1166 static PHP_FUNCTION(libxml_set_external_entity_loader)
1167 {
1168 	zend_fcall_info			fci;
1169 	zend_fcall_info_cache	fcc;
1170 
1171 	ZEND_PARSE_PARAMETERS_START(1, 1)
1172 		Z_PARAM_FUNC_EX(fci, fcc, 1, 0)
1173 	ZEND_PARSE_PARAMETERS_END();
1174 
1175 	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
1176 
1177 	if (fci.size > 0) { /* argument not null */
1178 		LIBXML(entity_loader).fci = fci;
1179 		Z_ADDREF(fci.function_name);
1180 		if (fci.object != NULL) {
1181 			ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object);
1182 			Z_ADDREF(LIBXML(entity_loader).object);
1183 		}
1184 		LIBXML(entity_loader).fcc = fcc;
1185 	}
1186 
1187 	RETURN_TRUE;
1188 }
1189 /* }}} */
1190 
1191 /* {{{ Common functions shared by extensions */
php_libxml_xmlCheckUTF8(const unsigned char * s)1192 int php_libxml_xmlCheckUTF8(const unsigned char *s)
1193 {
1194 	size_t i;
1195 	unsigned char c;
1196 
1197 	for (i = 0; (c = s[i++]);) {
1198 		if ((c & 0x80) == 0) {
1199 		} else if ((c & 0xe0) == 0xc0) {
1200 			if ((s[i++] & 0xc0) != 0x80) {
1201 				return 0;
1202 			}
1203 		} else if ((c & 0xf0) == 0xe0) {
1204 			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1205 				return 0;
1206 			}
1207 		} else if ((c & 0xf8) == 0xf0) {
1208 			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1209 				return 0;
1210 			}
1211 		} else {
1212 			return 0;
1213 		}
1214 	}
1215 	return 1;
1216 }
1217 
php_libxml_register_export(zend_class_entry * ce,php_libxml_export_node export_function)1218 zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
1219 {
1220 	php_libxml_func_handler export_hnd;
1221 
1222 	/* Initialize in case this module hasn't been loaded yet */
1223 	php_libxml_initialize();
1224 	export_hnd.export_func = export_function;
1225 
1226 	return zend_hash_add_mem(&php_libxml_exports, ce->name, &export_hnd, sizeof(export_hnd));
1227 }
1228 
php_libxml_import_node(zval * object)1229 PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object)
1230 {
1231 	zend_class_entry *ce = NULL;
1232 	xmlNodePtr node = NULL;
1233 	php_libxml_func_handler *export_hnd;
1234 
1235 	if (Z_TYPE_P(object) == IS_OBJECT) {
1236 		ce = Z_OBJCE_P(object);
1237 		while (ce->parent != NULL) {
1238 			ce = ce->parent;
1239 		}
1240 		if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) {
1241 			node = export_hnd->export_func(object);
1242 		}
1243 	}
1244 	return node;
1245 }
1246 
php_libxml_increment_node_ptr(php_libxml_node_object * object,xmlNodePtr node,void * private_data)1247 PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data)
1248 {
1249 	int ret_refcount = -1;
1250 
1251 	if (object != NULL && node != NULL) {
1252 		if (object->node != NULL) {
1253 			if (object->node->node == node) {
1254 				return object->node->refcount;
1255 			} else {
1256 				php_libxml_decrement_node_ptr(object);
1257 			}
1258 		}
1259 		if (node->_private != NULL) {
1260 			object->node = node->_private;
1261 			ret_refcount = ++object->node->refcount;
1262 			/* Only dom uses _private */
1263 			if (object->node->_private == NULL) {
1264 				object->node->_private = private_data;
1265 			}
1266 		} else {
1267 			ret_refcount = 1;
1268 			object->node = emalloc(sizeof(php_libxml_node_ptr));
1269 			object->node->node = node;
1270 			object->node->refcount = 1;
1271 			object->node->_private = private_data;
1272 			node->_private = object->node;
1273 		}
1274 	}
1275 
1276 	return ret_refcount;
1277 }
1278 
php_libxml_decrement_node_ptr(php_libxml_node_object * object)1279 PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object)
1280 {
1281 	int ret_refcount = -1;
1282 	php_libxml_node_ptr *obj_node;
1283 
1284 	if (object != NULL && object->node != NULL) {
1285 		obj_node = (php_libxml_node_ptr *) object->node;
1286 		ret_refcount = --obj_node->refcount;
1287 		if (ret_refcount == 0) {
1288 			if (obj_node->node != NULL) {
1289 				obj_node->node->_private = NULL;
1290 			}
1291 			efree(obj_node);
1292 		}
1293 		object->node = NULL;
1294 	}
1295 
1296 	return ret_refcount;
1297 }
1298 
php_libxml_increment_doc_ref(php_libxml_node_object * object,xmlDocPtr docp)1299 PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp)
1300 {
1301 	int ret_refcount = -1;
1302 
1303 	if (object->document != NULL) {
1304 		object->document->refcount++;
1305 		ret_refcount = object->document->refcount;
1306 	} else if (docp != NULL) {
1307 		ret_refcount = 1;
1308 		object->document = emalloc(sizeof(php_libxml_ref_obj));
1309 		object->document->ptr = docp;
1310 		object->document->refcount = ret_refcount;
1311 		object->document->doc_props = NULL;
1312 	}
1313 
1314 	return ret_refcount;
1315 }
1316 
php_libxml_decrement_doc_ref(php_libxml_node_object * object)1317 PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object)
1318 {
1319 	int ret_refcount = -1;
1320 
1321 	if (object != NULL && object->document != NULL) {
1322 		ret_refcount = --object->document->refcount;
1323 		if (ret_refcount == 0) {
1324 			if (object->document->ptr != NULL) {
1325 				xmlFreeDoc((xmlDoc *) object->document->ptr);
1326 			}
1327 			if (object->document->doc_props != NULL) {
1328 				if (object->document->doc_props->classmap) {
1329 					zend_hash_destroy(object->document->doc_props->classmap);
1330 					FREE_HASHTABLE(object->document->doc_props->classmap);
1331 				}
1332 				efree(object->document->doc_props);
1333 			}
1334 			efree(object->document);
1335 		}
1336 		object->document = NULL;
1337 	}
1338 
1339 	return ret_refcount;
1340 }
1341 
php_libxml_node_free_resource(xmlNodePtr node)1342 PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node)
1343 {
1344 	if (!node) {
1345 		return;
1346 	}
1347 
1348 	switch (node->type) {
1349 		case XML_DOCUMENT_NODE:
1350 		case XML_HTML_DOCUMENT_NODE:
1351 			break;
1352 		default:
1353 			if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1354 				php_libxml_node_free_list((xmlNodePtr) node->children);
1355 				switch (node->type) {
1356 					/* Skip property freeing for the following types */
1357 					case XML_ATTRIBUTE_DECL:
1358 					case XML_DTD_NODE:
1359 					case XML_DOCUMENT_TYPE_NODE:
1360 					case XML_ENTITY_DECL:
1361 					case XML_ATTRIBUTE_NODE:
1362 					case XML_NAMESPACE_DECL:
1363 					case XML_TEXT_NODE:
1364 						break;
1365 					default:
1366 						php_libxml_node_free_list((xmlNodePtr) node->properties);
1367 				}
1368 				if (php_libxml_unregister_node(node) == 0) {
1369 					node->doc = NULL;
1370 				}
1371 				php_libxml_node_free(node);
1372 			} else {
1373 				php_libxml_unregister_node(node);
1374 			}
1375 	}
1376 }
1377 
php_libxml_node_decrement_resource(php_libxml_node_object * object)1378 PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object)
1379 {
1380 	int ret_refcount = -1;
1381 	xmlNodePtr nodep;
1382 	php_libxml_node_ptr *obj_node;
1383 
1384 	if (object != NULL && object->node != NULL) {
1385 		obj_node = (php_libxml_node_ptr *) object->node;
1386 		nodep = object->node->node;
1387 		ret_refcount = php_libxml_decrement_node_ptr(object);
1388 		if (ret_refcount == 0) {
1389 			php_libxml_node_free_resource(nodep);
1390 		} else {
1391 			if (obj_node && object == obj_node->_private) {
1392 				obj_node->_private = NULL;
1393 			}
1394 		}
1395 	}
1396 	if (object != NULL && object->document != NULL) {
1397 		/* Safe to call as if the resource were freed then doc pointer is NULL */
1398 		php_libxml_decrement_doc_ref(object);
1399 	}
1400 }
1401 /* }}} */
1402 
1403 #if defined(PHP_WIN32) && defined(COMPILE_DL_LIBXML)
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)1404 PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1405 {
1406 	return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1407 }
1408 #endif
1409 
1410 #endif
1411