xref: /PHP-8.1/ext/libxml/libxml.c (revision 7c0dfc5c)
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 ATTRIBUTE_UNUSED)431 php_libxml_output_buffer_create_filename(const char *URI,
432                               xmlCharEncodingHandlerPtr encoder,
433                               int compression ATTRIBUTE_UNUSED)
434 {
435 	xmlOutputBufferPtr ret;
436 	xmlURIPtr puri;
437 	void *context = NULL;
438 	char *unescaped = NULL;
439 
440 	if (URI == NULL)
441 		return(NULL);
442 
443 	if (strstr(URI, "%00")) {
444 		php_error_docref(NULL, E_WARNING, "URI must not contain percent-encoded NUL bytes");
445 		return NULL;
446 	}
447 
448 	puri = xmlParseURI(URI);
449 	if (puri != NULL) {
450 		if (puri->scheme != NULL)
451 			unescaped = xmlURIUnescapeString(URI, 0, NULL);
452 		xmlFreeURI(puri);
453 	}
454 
455 	if (unescaped != NULL) {
456 		context = php_libxml_streams_IO_open_write_wrapper(unescaped);
457 		xmlFree(unescaped);
458 	}
459 
460 	/* try with a non-escaped URI this may be a strange filename */
461 	if (context == NULL) {
462 		context = php_libxml_streams_IO_open_write_wrapper(URI);
463 	}
464 
465 	if (context == NULL) {
466 		return(NULL);
467 	}
468 
469 	/* Allocate the Output buffer front-end. */
470 	ret = xmlAllocOutputBuffer(encoder);
471 	if (ret != NULL) {
472 		ret->context = context;
473 		ret->writecallback = php_libxml_streams_IO_write;
474 		ret->closecallback = php_libxml_streams_IO_close;
475 	}
476 
477 	return(ret);
478 }
479 
_php_libxml_free_error(void * ptr)480 static void _php_libxml_free_error(void *ptr)
481 {
482 	/* This will free the libxml alloc'd memory */
483 	xmlResetError((xmlErrorPtr) ptr);
484 }
485 
_php_list_set_error_structure(xmlErrorPtr error,const char * msg)486 static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg)
487 {
488 	xmlError error_copy;
489 	int ret;
490 
491 
492 	memset(&error_copy, 0, sizeof(xmlError));
493 
494 	if (error) {
495 		ret = xmlCopyError(error, &error_copy);
496 	} else {
497 		error_copy.domain = 0;
498 		error_copy.code = XML_ERR_INTERNAL_ERROR;
499 		error_copy.level = XML_ERR_ERROR;
500 		error_copy.line = 0;
501 		error_copy.node = NULL;
502 		error_copy.int1 = 0;
503 		error_copy.int2 = 0;
504 		error_copy.ctxt = NULL;
505 		error_copy.message = (char*)xmlStrdup((xmlChar*)msg);
506 		error_copy.file = NULL;
507 		error_copy.str1 = NULL;
508 		error_copy.str2 = NULL;
509 		error_copy.str3 = NULL;
510 		ret = 0;
511 	}
512 
513 	if (ret == 0) {
514 		zend_llist_add_element(LIBXML(error_list), &error_copy);
515 	}
516 }
517 
php_libxml_ctx_error_level(int level,void * ctx,const char * msg)518 static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg)
519 {
520 	xmlParserCtxtPtr parser;
521 
522 	parser = (xmlParserCtxtPtr) ctx;
523 
524 	if (parser != NULL && parser->input != NULL) {
525 		if (parser->input->filename) {
526 			php_error_docref(NULL, level, "%s in %s, line: %d", msg, parser->input->filename, parser->input->line);
527 		} else {
528 			php_error_docref(NULL, level, "%s in Entity, line: %d", msg, parser->input->line);
529 		}
530 	} else {
531 		php_error_docref(NULL, E_WARNING, "%s", msg);
532 	}
533 }
534 
php_libxml_issue_error(int level,const char * msg)535 void php_libxml_issue_error(int level, const char *msg)
536 {
537 	if (LIBXML(error_list)) {
538 		_php_list_set_error_structure(NULL, msg);
539 	} else {
540 		php_error_docref(NULL, level, "%s", msg);
541 	}
542 }
543 
php_libxml_internal_error_handler(int error_type,void * ctx,const char ** msg,va_list ap)544 static void php_libxml_internal_error_handler(int error_type, void *ctx, const char **msg, va_list ap)
545 {
546 	char *buf;
547 	int len, len_iter, output = 0;
548 
549 	len = vspprintf(&buf, 0, *msg, ap);
550 	len_iter = len;
551 
552 	/* remove any trailing \n */
553 	while (len_iter && buf[--len_iter] == '\n') {
554 		buf[len_iter] = '\0';
555 		output = 1;
556 	}
557 
558 	smart_str_appendl(&LIBXML(error_buffer), buf, len);
559 
560 	efree(buf);
561 
562 	if (output == 1) {
563 		if (LIBXML(error_list)) {
564 			_php_list_set_error_structure(NULL, ZSTR_VAL(LIBXML(error_buffer).s));
565 		} else if (!EG(exception)) {
566 			/* Don't throw additional notices/warnings if an exception has already been thrown. */
567 			switch (error_type) {
568 				case PHP_LIBXML_CTX_ERROR:
569 					php_libxml_ctx_error_level(E_WARNING, ctx, ZSTR_VAL(LIBXML(error_buffer).s));
570 					break;
571 				case PHP_LIBXML_CTX_WARNING:
572 					php_libxml_ctx_error_level(E_NOTICE, ctx, ZSTR_VAL(LIBXML(error_buffer).s));
573 					break;
574 				default:
575 					php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(LIBXML(error_buffer).s));
576 			}
577 		}
578 		smart_str_free(&LIBXML(error_buffer));
579 	}
580 }
581 
_php_libxml_external_entity_loader(const char * URL,const char * ID,xmlParserCtxtPtr context)582 static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,
583 		const char *ID, xmlParserCtxtPtr context)
584 {
585 	xmlParserInputPtr	ret			= NULL;
586 	const char			*resource	= NULL;
587 	zval 				*ctxzv, retval;
588 	zval				params[3];
589 	int					status;
590 	zend_fcall_info		*fci;
591 
592 	fci = &LIBXML(entity_loader).fci;
593 
594 	if (fci->size == 0) {
595 		/* no custom user-land callback set up; delegate to original loader */
596 		return _php_libxml_default_entity_loader(URL, ID, context);
597 	}
598 
599 	if (ID != NULL) {
600 		ZVAL_STRING(&params[0], ID);
601 	} else {
602 		ZVAL_NULL(&params[0]);
603 	}
604 	if (URL != NULL) {
605 		ZVAL_STRING(&params[1], URL);
606 	} else {
607 		ZVAL_NULL(&params[1]);
608 	}
609 	ctxzv = &params[2];
610 	array_init_size(ctxzv, 4);
611 
612 #define ADD_NULL_OR_STRING_KEY(memb) \
613 	if (context->memb == NULL) { \
614 		add_assoc_null_ex(ctxzv, #memb, sizeof(#memb) - 1); \
615 	} else { \
616 		add_assoc_string_ex(ctxzv, #memb, sizeof(#memb) - 1, \
617 				(char *)context->memb); \
618 	}
619 
620 	ADD_NULL_OR_STRING_KEY(directory)
621 	ADD_NULL_OR_STRING_KEY(intSubName)
622 	ADD_NULL_OR_STRING_KEY(extSubURI)
623 	ADD_NULL_OR_STRING_KEY(extSubSystem)
624 
625 #undef ADD_NULL_OR_STRING_KEY
626 
627 	fci->retval	= &retval;
628 	fci->params	= params;
629 	fci->param_count = sizeof(params)/sizeof(*params);
630 
631 	status = zend_call_function(fci, &LIBXML(entity_loader).fcc);
632 	if (status != SUCCESS || Z_ISUNDEF(retval)) {
633 		php_libxml_ctx_error(context,
634 				"Call to user entity loader callback '%s' has failed",
635 				Z_STRVAL(fci->function_name));
636 	} else {
637 		/*
638 		retval_ptr = *fci->retval_ptr_ptr;
639 		if (retval_ptr == NULL) {
640 			php_libxml_ctx_error(context,
641 					"Call to user entity loader callback '%s' has failed; "
642 					"probably it has thrown an exception",
643 					fci->function_name);
644 		} else */ if (Z_TYPE(retval) == IS_STRING) {
645 is_string:
646 			resource = Z_STRVAL(retval);
647 		} else if (Z_TYPE(retval) == IS_RESOURCE) {
648 			php_stream *stream;
649 			php_stream_from_zval_no_verify(stream, &retval);
650 			if (stream == NULL) {
651 				php_libxml_ctx_error(context,
652 						"The user entity loader callback '%s' has returned a "
653 						"resource, but it is not a stream",
654 						Z_STRVAL(fci->function_name));
655 			} else {
656 				/* TODO: allow storing the encoding in the stream context? */
657 				xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
658 				xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
659 				if (pib == NULL) {
660 					php_libxml_ctx_error(context, "Could not allocate parser "
661 							"input buffer");
662 				} else {
663 					/* make stream not being closed when the zval is freed */
664 					GC_ADDREF(stream->res);
665 					pib->context = stream;
666 					pib->readcallback = php_libxml_streams_IO_read;
667 					pib->closecallback = php_libxml_streams_IO_close;
668 
669 					ret = xmlNewIOInputStream(context, pib, enc);
670 					if (ret == NULL) {
671 						xmlFreeParserInputBuffer(pib);
672 					}
673 				}
674 			}
675 		} else if (Z_TYPE(retval) != IS_NULL) {
676 			/* retval not string nor resource nor null; convert to string */
677 			if (try_convert_to_string(&retval)) {
678 				goto is_string;
679 			}
680 		} /* else is null; don't try anything */
681 	}
682 
683 	if (ret == NULL) {
684 		if (resource == NULL) {
685 			if (ID == NULL) {
686 				ID = "NULL";
687 			}
688 			php_libxml_ctx_error(context,
689 					"Failed to load external entity \"%s\"\n", ID);
690 		} else {
691 			/* we got the resource in the form of a string; open it */
692 			ret = xmlNewInputFromFile(context, resource);
693 		}
694 	}
695 
696 	zval_ptr_dtor(&params[0]);
697 	zval_ptr_dtor(&params[1]);
698 	zval_ptr_dtor(&params[2]);
699 	zval_ptr_dtor(&retval);
700 	return ret;
701 }
702 
_php_libxml_pre_ext_ent_loader(const char * URL,const char * ID,xmlParserCtxtPtr context)703 static xmlParserInputPtr _php_libxml_pre_ext_ent_loader(const char *URL,
704 		const char *ID, xmlParserCtxtPtr context)
705 {
706 
707 	/* Check whether we're running in a PHP context, since the entity loader
708 	 * we've defined is an application level (true global) setting.
709 	 * If we are, we also want to check whether we've finished activating
710 	 * the modules (RINIT phase). Using our external entity loader during a
711 	 * RINIT should not be problem per se (though during MINIT it is, because
712 	 * we don't even have a resource list by then), but then whether one
713 	 * extension would be using the custom external entity loader or not
714 	 * could depend on extension loading order
715 	 * (if _php_libxml_per_request_initialization */
716 	if (xmlGenericError == php_libxml_error_handler && PG(modules_activated)) {
717 		return _php_libxml_external_entity_loader(URL, ID, context);
718 	} else {
719 		return _php_libxml_default_entity_loader(URL, ID, context);
720 	}
721 }
722 
php_libxml_ctx_error(void * ctx,const char * msg,...)723 PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...)
724 {
725 	va_list args;
726 	va_start(args, msg);
727 	php_libxml_internal_error_handler(PHP_LIBXML_CTX_ERROR, ctx, &msg, args);
728 	va_end(args);
729 }
730 
php_libxml_ctx_warning(void * ctx,const char * msg,...)731 PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...)
732 {
733 	va_list args;
734 	va_start(args, msg);
735 	php_libxml_internal_error_handler(PHP_LIBXML_CTX_WARNING, ctx, &msg, args);
736 	va_end(args);
737 }
738 
php_libxml_structured_error_handler(void * userData,xmlErrorPtr error)739 PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error)
740 {
741 	_php_list_set_error_structure(error, NULL);
742 
743 	return;
744 }
745 
php_libxml_error_handler(void * ctx,const char * msg,...)746 PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...)
747 {
748 	va_list args;
749 	va_start(args, msg);
750 	php_libxml_internal_error_handler(PHP_LIBXML_ERROR, ctx, &msg, args);
751 	va_end(args);
752 }
753 
php_libxml_exports_dtor(zval * zv)754 static void php_libxml_exports_dtor(zval *zv)
755 {
756 	free(Z_PTR_P(zv));
757 }
758 
php_libxml_initialize(void)759 PHP_LIBXML_API void php_libxml_initialize(void)
760 {
761 	if (!_php_libxml_initialized) {
762 		/* we should be the only one's to ever init!! */
763 		ZEND_IGNORE_LEAKS_BEGIN();
764 		xmlInitParser();
765 		ZEND_IGNORE_LEAKS_END();
766 
767 		_php_libxml_default_entity_loader = xmlGetExternalEntityLoader();
768 		xmlSetExternalEntityLoader(_php_libxml_pre_ext_ent_loader);
769 
770 		zend_hash_init(&php_libxml_exports, 0, NULL, php_libxml_exports_dtor, 1);
771 
772 		_php_libxml_initialized = 1;
773 	}
774 }
775 
php_libxml_shutdown(void)776 PHP_LIBXML_API void php_libxml_shutdown(void)
777 {
778 	if (_php_libxml_initialized) {
779 #if defined(LIBXML_SCHEMAS_ENABLED) && LIBXML_VERSION < 21000
780 		xmlRelaxNGCleanupTypes();
781 #endif
782 		/* xmlCleanupParser(); */
783 		zend_hash_destroy(&php_libxml_exports);
784 
785 		xmlSetExternalEntityLoader(_php_libxml_default_entity_loader);
786 		_php_libxml_initialized = 0;
787 	}
788 }
789 
php_libxml_switch_context(zval * context,zval * oldcontext)790 PHP_LIBXML_API void php_libxml_switch_context(zval *context, zval *oldcontext)
791 {
792 	if (oldcontext) {
793 		ZVAL_COPY_VALUE(oldcontext, &LIBXML(stream_context));
794 	}
795 	if (context) {
796 		ZVAL_COPY_VALUE(&LIBXML(stream_context), context);
797 	}
798 }
799 
PHP_MINIT_FUNCTION(libxml)800 static PHP_MINIT_FUNCTION(libxml)
801 {
802 	php_libxml_initialize();
803 
804 	REGISTER_LONG_CONSTANT("LIBXML_VERSION",			LIBXML_VERSION,			CONST_CS | CONST_PERSISTENT);
805 	REGISTER_STRING_CONSTANT("LIBXML_DOTTED_VERSION",	LIBXML_DOTTED_VERSION,	CONST_CS | CONST_PERSISTENT);
806 	REGISTER_STRING_CONSTANT("LIBXML_LOADED_VERSION",	(char *)xmlParserVersion,		CONST_CS | CONST_PERSISTENT);
807 
808 	/* For use with loading xml */
809 	REGISTER_LONG_CONSTANT("LIBXML_NOENT",		XML_PARSE_NOENT,		CONST_CS | CONST_PERSISTENT);
810 	REGISTER_LONG_CONSTANT("LIBXML_DTDLOAD",	XML_PARSE_DTDLOAD,		CONST_CS | CONST_PERSISTENT);
811 	REGISTER_LONG_CONSTANT("LIBXML_DTDATTR",	XML_PARSE_DTDATTR,		CONST_CS | CONST_PERSISTENT);
812 	REGISTER_LONG_CONSTANT("LIBXML_DTDVALID",	XML_PARSE_DTDVALID,		CONST_CS | CONST_PERSISTENT);
813 	REGISTER_LONG_CONSTANT("LIBXML_NOERROR",	XML_PARSE_NOERROR,		CONST_CS | CONST_PERSISTENT);
814 	REGISTER_LONG_CONSTANT("LIBXML_NOWARNING",	XML_PARSE_NOWARNING,	CONST_CS | CONST_PERSISTENT);
815 	REGISTER_LONG_CONSTANT("LIBXML_NOBLANKS",	XML_PARSE_NOBLANKS,		CONST_CS | CONST_PERSISTENT);
816 	REGISTER_LONG_CONSTANT("LIBXML_XINCLUDE",	XML_PARSE_XINCLUDE,		CONST_CS | CONST_PERSISTENT);
817 	REGISTER_LONG_CONSTANT("LIBXML_NSCLEAN",	XML_PARSE_NSCLEAN,		CONST_CS | CONST_PERSISTENT);
818 	REGISTER_LONG_CONSTANT("LIBXML_NOCDATA",	XML_PARSE_NOCDATA,		CONST_CS | CONST_PERSISTENT);
819 	REGISTER_LONG_CONSTANT("LIBXML_NONET",		XML_PARSE_NONET,		CONST_CS | CONST_PERSISTENT);
820 	REGISTER_LONG_CONSTANT("LIBXML_PEDANTIC",	XML_PARSE_PEDANTIC,		CONST_CS | CONST_PERSISTENT);
821 	REGISTER_LONG_CONSTANT("LIBXML_COMPACT",	XML_PARSE_COMPACT,		CONST_CS | CONST_PERSISTENT);
822 	REGISTER_LONG_CONSTANT("LIBXML_NOXMLDECL",	XML_SAVE_NO_DECL,		CONST_CS | CONST_PERSISTENT);
823 	REGISTER_LONG_CONSTANT("LIBXML_PARSEHUGE",	XML_PARSE_HUGE,			CONST_CS | CONST_PERSISTENT);
824 #if LIBXML_VERSION >= 20900
825 	REGISTER_LONG_CONSTANT("LIBXML_BIGLINES",	XML_PARSE_BIG_LINES,	CONST_CS | CONST_PERSISTENT);
826 #endif
827 	REGISTER_LONG_CONSTANT("LIBXML_NOEMPTYTAG",	LIBXML_SAVE_NOEMPTYTAG,	CONST_CS | CONST_PERSISTENT);
828 
829 	/* Schema validation options */
830 #ifdef LIBXML_SCHEMAS_ENABLED
831 	REGISTER_LONG_CONSTANT("LIBXML_SCHEMA_CREATE",	XML_SCHEMA_VAL_VC_I_CREATE,	CONST_CS | CONST_PERSISTENT);
832 #endif
833 
834 	/* Additional constants for use with loading html */
835 #if LIBXML_VERSION >= 20707
836 	REGISTER_LONG_CONSTANT("LIBXML_HTML_NOIMPLIED",	HTML_PARSE_NOIMPLIED,		CONST_CS | CONST_PERSISTENT);
837 #endif
838 
839 #if LIBXML_VERSION >= 20708
840 	REGISTER_LONG_CONSTANT("LIBXML_HTML_NODEFDTD",	HTML_PARSE_NODEFDTD,		CONST_CS | CONST_PERSISTENT);
841 #endif
842 
843 	/* Error levels */
844 	REGISTER_LONG_CONSTANT("LIBXML_ERR_NONE",		XML_ERR_NONE,		CONST_CS | CONST_PERSISTENT);
845 	REGISTER_LONG_CONSTANT("LIBXML_ERR_WARNING",	XML_ERR_WARNING,	CONST_CS | CONST_PERSISTENT);
846 	REGISTER_LONG_CONSTANT("LIBXML_ERR_ERROR",		XML_ERR_ERROR,		CONST_CS | CONST_PERSISTENT);
847 	REGISTER_LONG_CONSTANT("LIBXML_ERR_FATAL",		XML_ERR_FATAL,		CONST_CS | CONST_PERSISTENT);
848 
849 	libxmlerror_class_entry = register_class_LibXMLError();
850 
851 	if (sapi_module.name) {
852 		static const char * const supported_sapis[] = {
853 			"cgi-fcgi",
854 			"litespeed",
855 			NULL
856 		};
857 		const char * const *sapi_name;
858 
859 		for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
860 			if (strcmp(sapi_module.name, *sapi_name) == 0) {
861 				_php_libxml_per_request_initialization = 0;
862 				break;
863 			}
864 		}
865 	}
866 
867 	if (!_php_libxml_per_request_initialization) {
868 		/* report errors via handler rather than stderr */
869 		xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
870 		xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
871 		xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
872 	}
873 
874 	return SUCCESS;
875 }
876 
877 
PHP_RINIT_FUNCTION(libxml)878 static PHP_RINIT_FUNCTION(libxml)
879 {
880 	if (_php_libxml_per_request_initialization) {
881 		/* report errors via handler rather than stderr */
882 		xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
883 		xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
884 		xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
885 	}
886 
887 	/* Enable the entity loader by default. This ensures that
888 	 * other threads/requests that might have disabled the loader
889 	 * do not affect the current request.
890 	 */
891 	LIBXML(entity_loader_disabled) = 0;
892 
893 	return SUCCESS;
894 }
895 
PHP_RSHUTDOWN_FUNCTION(libxml)896 static PHP_RSHUTDOWN_FUNCTION(libxml)
897 {
898 	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
899 
900 	return SUCCESS;
901 }
902 
PHP_MSHUTDOWN_FUNCTION(libxml)903 static PHP_MSHUTDOWN_FUNCTION(libxml)
904 {
905 	if (!_php_libxml_per_request_initialization) {
906 		xmlSetGenericErrorFunc(NULL, NULL);
907 
908 		xmlParserInputBufferCreateFilenameDefault(NULL);
909 		xmlOutputBufferCreateFilenameDefault(NULL);
910 	}
911 	php_libxml_shutdown();
912 
913 	return SUCCESS;
914 }
915 
php_libxml_post_deactivate(void)916 static zend_result php_libxml_post_deactivate(void)
917 {
918 	/* reset libxml generic error handling */
919 	if (_php_libxml_per_request_initialization) {
920 		xmlSetGenericErrorFunc(NULL, NULL);
921 
922 		xmlParserInputBufferCreateFilenameDefault(NULL);
923 		xmlOutputBufferCreateFilenameDefault(NULL);
924 	}
925 	xmlSetStructuredErrorFunc(NULL, NULL);
926 
927 	/* the steam_context resource will be released by resource list destructor */
928 	ZVAL_UNDEF(&LIBXML(stream_context));
929 	smart_str_free(&LIBXML(error_buffer));
930 	if (LIBXML(error_list)) {
931 		zend_llist_destroy(LIBXML(error_list));
932 		efree(LIBXML(error_list));
933 		LIBXML(error_list) = NULL;
934 	}
935 	xmlResetLastError();
936 
937 	return SUCCESS;
938 }
939 
940 
PHP_MINFO_FUNCTION(libxml)941 static PHP_MINFO_FUNCTION(libxml)
942 {
943 	php_info_print_table_start();
944 	php_info_print_table_row(2, "libXML support", "active");
945 	php_info_print_table_row(2, "libXML Compiled Version", LIBXML_DOTTED_VERSION);
946 	php_info_print_table_row(2, "libXML Loaded Version", (char *)xmlParserVersion);
947 	php_info_print_table_row(2, "libXML streams", "enabled");
948 	php_info_print_table_end();
949 }
950 /* }}} */
951 
952 /* {{{ Set the streams context for the next libxml document load or write */
PHP_FUNCTION(libxml_set_streams_context)953 PHP_FUNCTION(libxml_set_streams_context)
954 {
955 	zval *arg;
956 
957 	ZEND_PARSE_PARAMETERS_START(1, 1)
958 		Z_PARAM_RESOURCE(arg)
959 	ZEND_PARSE_PARAMETERS_END();
960 
961 	if (!Z_ISUNDEF(LIBXML(stream_context))) {
962 		zval_ptr_dtor(&LIBXML(stream_context));
963 		ZVAL_UNDEF(&LIBXML(stream_context));
964 	}
965 	ZVAL_COPY(&LIBXML(stream_context), arg);
966 }
967 /* }}} */
968 
969 /* {{{ Disable libxml errors and allow user to fetch error information as needed */
PHP_FUNCTION(libxml_use_internal_errors)970 PHP_FUNCTION(libxml_use_internal_errors)
971 {
972 	xmlStructuredErrorFunc current_handler;
973 	bool use_errors, use_errors_is_null = 1, retval;
974 
975 	ZEND_PARSE_PARAMETERS_START(0, 1)
976 		Z_PARAM_OPTIONAL
977 		Z_PARAM_BOOL_OR_NULL(use_errors, use_errors_is_null)
978 	ZEND_PARSE_PARAMETERS_END();
979 
980 	current_handler = xmlStructuredError;
981 	if (current_handler && current_handler == php_libxml_structured_error_handler) {
982 		retval = 1;
983 	} else {
984 		retval = 0;
985 	}
986 
987 	if (use_errors_is_null) {
988 		RETURN_BOOL(retval);
989 	}
990 
991 	if (use_errors == 0) {
992 		xmlSetStructuredErrorFunc(NULL, NULL);
993 		if (LIBXML(error_list)) {
994 			zend_llist_destroy(LIBXML(error_list));
995 			efree(LIBXML(error_list));
996 			LIBXML(error_list) = NULL;
997 		}
998 	} else {
999 		xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
1000 		if (LIBXML(error_list) == NULL) {
1001 			LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
1002 			zend_llist_init(LIBXML(error_list), sizeof(xmlError), _php_libxml_free_error, 0);
1003 		}
1004 	}
1005 	RETURN_BOOL(retval);
1006 }
1007 /* }}} */
1008 
1009 /* {{{ Retrieve last error from libxml */
PHP_FUNCTION(libxml_get_last_error)1010 PHP_FUNCTION(libxml_get_last_error)
1011 {
1012 	xmlErrorPtr error;
1013 
1014 	ZEND_PARSE_PARAMETERS_NONE();
1015 
1016 	error = xmlGetLastError();
1017 
1018 	if (error) {
1019 		object_init_ex(return_value, libxmlerror_class_entry);
1020 		add_property_long(return_value, "level", error->level);
1021 		add_property_long(return_value, "code", error->code);
1022 		add_property_long(return_value, "column", error->int2);
1023 		if (error->message) {
1024 			add_property_string(return_value, "message", error->message);
1025 		} else {
1026 			add_property_stringl(return_value, "message", "", 0);
1027 		}
1028 		if (error->file) {
1029 			add_property_string(return_value, "file", error->file);
1030 		} else {
1031 			add_property_stringl(return_value, "file", "", 0);
1032 		}
1033 		add_property_long(return_value, "line", error->line);
1034 	} else {
1035 		RETURN_FALSE;
1036 	}
1037 }
1038 /* }}} */
1039 
1040 /* {{{ Retrieve array of errors */
PHP_FUNCTION(libxml_get_errors)1041 PHP_FUNCTION(libxml_get_errors)
1042 {
1043 
1044 	xmlErrorPtr error;
1045 
1046 	ZEND_PARSE_PARAMETERS_NONE();
1047 
1048 	if (LIBXML(error_list)) {
1049 
1050 		array_init(return_value);
1051 		error = zend_llist_get_first(LIBXML(error_list));
1052 
1053 		while (error != NULL) {
1054 			zval z_error;
1055 
1056 			object_init_ex(&z_error, libxmlerror_class_entry);
1057 			add_property_long_ex(&z_error, "level", sizeof("level") - 1, error->level);
1058 			add_property_long_ex(&z_error, "code", sizeof("code") - 1, error->code);
1059 			add_property_long_ex(&z_error, "column", sizeof("column") - 1, error->int2 );
1060 			if (error->message) {
1061 				add_property_string_ex(&z_error, "message", sizeof("message") - 1, error->message);
1062 			} else {
1063 				add_property_stringl_ex(&z_error, "message", sizeof("message") - 1, "", 0);
1064 			}
1065 			if (error->file) {
1066 				add_property_string_ex(&z_error, "file", sizeof("file") - 1, error->file);
1067 			} else {
1068 				add_property_stringl_ex(&z_error, "file", sizeof("file") - 1, "", 0);
1069 			}
1070 			add_property_long_ex(&z_error, "line", sizeof("line") - 1, error->line);
1071 			add_next_index_zval(return_value, &z_error);
1072 
1073 			error = zend_llist_get_next(LIBXML(error_list));
1074 		}
1075 	} else {
1076 		RETURN_EMPTY_ARRAY();
1077 	}
1078 }
1079 /* }}} */
1080 
1081 /* {{{ Clear last error from libxml */
PHP_FUNCTION(libxml_clear_errors)1082 PHP_FUNCTION(libxml_clear_errors)
1083 {
1084 	ZEND_PARSE_PARAMETERS_NONE();
1085 
1086 	xmlResetLastError();
1087 	if (LIBXML(error_list)) {
1088 		zend_llist_clean(LIBXML(error_list));
1089 	}
1090 }
1091 /* }}} */
1092 
php_libxml_disable_entity_loader(bool disable)1093 PHP_LIBXML_API bool php_libxml_disable_entity_loader(bool disable) /* {{{ */
1094 {
1095 	bool old = LIBXML(entity_loader_disabled);
1096 
1097 	LIBXML(entity_loader_disabled) = disable;
1098 	return old;
1099 } /* }}} */
1100 
1101 /* {{{ Disable/Enable ability to load external entities */
PHP_FUNCTION(libxml_disable_entity_loader)1102 PHP_FUNCTION(libxml_disable_entity_loader)
1103 {
1104 	bool disable = 1;
1105 
1106 	ZEND_PARSE_PARAMETERS_START(0, 1)
1107 		Z_PARAM_OPTIONAL
1108 		Z_PARAM_BOOL(disable)
1109 	ZEND_PARSE_PARAMETERS_END();
1110 
1111 	RETURN_BOOL(php_libxml_disable_entity_loader(disable));
1112 }
1113 /* }}} */
1114 
1115 /* {{{ Changes the default external entity loader */
PHP_FUNCTION(libxml_set_external_entity_loader)1116 PHP_FUNCTION(libxml_set_external_entity_loader)
1117 {
1118 	zend_fcall_info			fci;
1119 	zend_fcall_info_cache	fcc;
1120 
1121 	ZEND_PARSE_PARAMETERS_START(1, 1)
1122 		Z_PARAM_FUNC_OR_NULL(fci, fcc)
1123 	ZEND_PARSE_PARAMETERS_END();
1124 
1125 	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
1126 
1127 	if (ZEND_FCI_INITIALIZED(fci)) { /* argument not null */
1128 		LIBXML(entity_loader).fci = fci;
1129 		Z_ADDREF(fci.function_name);
1130 		if (fci.object != NULL) {
1131 			ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object);
1132 			Z_ADDREF(LIBXML(entity_loader).object);
1133 		}
1134 		LIBXML(entity_loader).fcc = fcc;
1135 	}
1136 
1137 	RETURN_TRUE;
1138 }
1139 /* }}} */
1140 
1141 /* {{{ Common functions shared by extensions */
php_libxml_xmlCheckUTF8(const unsigned char * s)1142 int php_libxml_xmlCheckUTF8(const unsigned char *s)
1143 {
1144 	size_t i;
1145 	unsigned char c;
1146 
1147 	for (i = 0; (c = s[i++]);) {
1148 		if ((c & 0x80) == 0) {
1149 		} else if ((c & 0xe0) == 0xc0) {
1150 			if ((s[i++] & 0xc0) != 0x80) {
1151 				return 0;
1152 			}
1153 		} else if ((c & 0xf0) == 0xe0) {
1154 			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1155 				return 0;
1156 			}
1157 		} else if ((c & 0xf8) == 0xf0) {
1158 			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1159 				return 0;
1160 			}
1161 		} else {
1162 			return 0;
1163 		}
1164 	}
1165 	return 1;
1166 }
1167 
php_libxml_register_export(zend_class_entry * ce,php_libxml_export_node export_function)1168 zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
1169 {
1170 	php_libxml_func_handler export_hnd;
1171 
1172 	/* Initialize in case this module hasn't been loaded yet */
1173 	php_libxml_initialize();
1174 	export_hnd.export_func = export_function;
1175 
1176 	return zend_hash_add_mem(&php_libxml_exports, ce->name, &export_hnd, sizeof(export_hnd));
1177 }
1178 
php_libxml_import_node(zval * object)1179 PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object)
1180 {
1181 	zend_class_entry *ce = NULL;
1182 	xmlNodePtr node = NULL;
1183 	php_libxml_func_handler *export_hnd;
1184 
1185 	if (Z_TYPE_P(object) == IS_OBJECT) {
1186 		ce = Z_OBJCE_P(object);
1187 		while (ce->parent != NULL) {
1188 			ce = ce->parent;
1189 		}
1190 		if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) {
1191 			node = export_hnd->export_func(object);
1192 		}
1193 	}
1194 	return node;
1195 }
1196 
php_libxml_increment_node_ptr(php_libxml_node_object * object,xmlNodePtr node,void * private_data)1197 PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data)
1198 {
1199 	int ret_refcount = -1;
1200 
1201 	if (object != NULL && node != NULL) {
1202 		if (object->node != NULL) {
1203 			if (object->node->node == node) {
1204 				return object->node->refcount;
1205 			} else {
1206 				php_libxml_decrement_node_ptr(object);
1207 			}
1208 		}
1209 		if (node->_private != NULL) {
1210 			object->node = node->_private;
1211 			ret_refcount = ++object->node->refcount;
1212 			/* Only dom uses _private */
1213 			if (object->node->_private == NULL) {
1214 				object->node->_private = private_data;
1215 			}
1216 		} else {
1217 			ret_refcount = 1;
1218 			object->node = emalloc(sizeof(php_libxml_node_ptr));
1219 			object->node->node = node;
1220 			object->node->refcount = 1;
1221 			object->node->_private = private_data;
1222 			node->_private = object->node;
1223 		}
1224 	}
1225 
1226 	return ret_refcount;
1227 }
1228 
php_libxml_decrement_node_ptr(php_libxml_node_object * object)1229 PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object)
1230 {
1231 	int ret_refcount = -1;
1232 	php_libxml_node_ptr *obj_node;
1233 
1234 	if (object != NULL && object->node != NULL) {
1235 		obj_node = (php_libxml_node_ptr *) object->node;
1236 		ret_refcount = --obj_node->refcount;
1237 		if (ret_refcount == 0) {
1238 			if (obj_node->node != NULL) {
1239 				obj_node->node->_private = NULL;
1240 			}
1241 			efree(obj_node);
1242 		}
1243 		object->node = NULL;
1244 	}
1245 
1246 	return ret_refcount;
1247 }
1248 
php_libxml_increment_doc_ref(php_libxml_node_object * object,xmlDocPtr docp)1249 PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp)
1250 {
1251 	int ret_refcount = -1;
1252 
1253 	if (object->document != NULL) {
1254 		object->document->refcount++;
1255 		ret_refcount = object->document->refcount;
1256 	} else if (docp != NULL) {
1257 		ret_refcount = 1;
1258 		object->document = emalloc(sizeof(php_libxml_ref_obj));
1259 		object->document->ptr = docp;
1260 		object->document->refcount = ret_refcount;
1261 		object->document->doc_props = NULL;
1262 	}
1263 
1264 	return ret_refcount;
1265 }
1266 
php_libxml_decrement_doc_ref(php_libxml_node_object * object)1267 PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object)
1268 {
1269 	int ret_refcount = -1;
1270 
1271 	if (object != NULL && object->document != NULL) {
1272 		ret_refcount = --object->document->refcount;
1273 		if (ret_refcount == 0) {
1274 			if (object->document->ptr != NULL) {
1275 				xmlFreeDoc((xmlDoc *) object->document->ptr);
1276 			}
1277 			if (object->document->doc_props != NULL) {
1278 				if (object->document->doc_props->classmap) {
1279 					zend_hash_destroy(object->document->doc_props->classmap);
1280 					FREE_HASHTABLE(object->document->doc_props->classmap);
1281 				}
1282 				efree(object->document->doc_props);
1283 			}
1284 			efree(object->document);
1285 		}
1286 		object->document = NULL;
1287 	}
1288 
1289 	return ret_refcount;
1290 }
1291 
php_libxml_node_free_resource(xmlNodePtr node)1292 PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node)
1293 {
1294 	if (!node) {
1295 		return;
1296 	}
1297 
1298 	switch (node->type) {
1299 		case XML_DOCUMENT_NODE:
1300 		case XML_HTML_DOCUMENT_NODE:
1301 			break;
1302 		default:
1303 			if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1304 				php_libxml_node_free_list((xmlNodePtr) node->children);
1305 				switch (node->type) {
1306 					/* Skip property freeing for the following types */
1307 					case XML_ATTRIBUTE_DECL:
1308 					case XML_DTD_NODE:
1309 					case XML_DOCUMENT_TYPE_NODE:
1310 					case XML_ENTITY_DECL:
1311 					case XML_ATTRIBUTE_NODE:
1312 					case XML_NAMESPACE_DECL:
1313 					case XML_TEXT_NODE:
1314 						break;
1315 					default:
1316 						php_libxml_node_free_list((xmlNodePtr) node->properties);
1317 				}
1318 				if (php_libxml_unregister_node(node) == 0) {
1319 					node->doc = NULL;
1320 				}
1321 				php_libxml_node_free(node);
1322 			} else {
1323 				php_libxml_unregister_node(node);
1324 			}
1325 	}
1326 }
1327 
php_libxml_node_decrement_resource(php_libxml_node_object * object)1328 PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object)
1329 {
1330 	int ret_refcount = -1;
1331 	xmlNodePtr nodep;
1332 	php_libxml_node_ptr *obj_node;
1333 
1334 	if (object != NULL && object->node != NULL) {
1335 		obj_node = (php_libxml_node_ptr *) object->node;
1336 		nodep = object->node->node;
1337 		ret_refcount = php_libxml_decrement_node_ptr(object);
1338 		if (ret_refcount == 0) {
1339 			php_libxml_node_free_resource(nodep);
1340 		} else {
1341 			if (obj_node && object == obj_node->_private) {
1342 				obj_node->_private = NULL;
1343 			}
1344 		}
1345 	}
1346 	if (object != NULL && object->document != NULL) {
1347 		/* Safe to call as if the resource were freed then doc pointer is NULL */
1348 		php_libxml_decrement_doc_ref(object);
1349 	}
1350 }
1351 /* }}} */
1352 
1353 #if defined(PHP_WIN32) && defined(COMPILE_DL_LIBXML)
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)1354 PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1355 {
1356 	return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1357 }
1358 #endif
1359 
1360 #endif
1361