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