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