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