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