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