xref: /PHP-7.0/ext/libxml/libxml.c (revision 8e5b9532)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2017 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_UNDEF(&params[0]);
595 	}
596 	if (URL != NULL) {
597 		ZVAL_STRING(&params[1], URL);
598 	} else {
599 		ZVAL_UNDEF(&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 		/* Enable the entity loader by default. This ensures that
884 		 * other threads/requests that might have disabled the loader
885 		 * do not affect the current request.
886 		 */
887 		LIBXML(entity_loader_disabled) = 0;
888 	}
889 	return SUCCESS;
890 }
891 
PHP_RSHUTDOWN_FUNCTION(libxml)892 static PHP_RSHUTDOWN_FUNCTION(libxml)
893 {
894 	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
895 
896 	return SUCCESS;
897 }
898 
PHP_MSHUTDOWN_FUNCTION(libxml)899 static PHP_MSHUTDOWN_FUNCTION(libxml)
900 {
901 	if (!_php_libxml_per_request_initialization) {
902 		xmlSetGenericErrorFunc(NULL, NULL);
903 
904 		xmlParserInputBufferCreateFilenameDefault(NULL);
905 		xmlOutputBufferCreateFilenameDefault(NULL);
906 	}
907 	php_libxml_shutdown();
908 
909 	return SUCCESS;
910 }
911 
php_libxml_post_deactivate(void)912 static int php_libxml_post_deactivate(void)
913 {
914 	/* reset libxml generic error handling */
915 	if (_php_libxml_per_request_initialization) {
916 		xmlSetGenericErrorFunc(NULL, NULL);
917 
918 		xmlParserInputBufferCreateFilenameDefault(NULL);
919 		xmlOutputBufferCreateFilenameDefault(NULL);
920 	}
921 	xmlSetStructuredErrorFunc(NULL, NULL);
922 
923 	/* the steam_context resource will be released by resource list destructor */
924 	ZVAL_UNDEF(&LIBXML(stream_context));
925 	smart_str_free(&LIBXML(error_buffer));
926 	if (LIBXML(error_list)) {
927 		zend_llist_destroy(LIBXML(error_list));
928 		efree(LIBXML(error_list));
929 		LIBXML(error_list) = NULL;
930 	}
931 	xmlResetLastError();
932 
933 	return SUCCESS;
934 }
935 
936 
PHP_MINFO_FUNCTION(libxml)937 static PHP_MINFO_FUNCTION(libxml)
938 {
939 	php_info_print_table_start();
940 	php_info_print_table_row(2, "libXML support", "active");
941 	php_info_print_table_row(2, "libXML Compiled Version", LIBXML_DOTTED_VERSION);
942 	php_info_print_table_row(2, "libXML Loaded Version", (char *)xmlParserVersion);
943 	php_info_print_table_row(2, "libXML streams", "enabled");
944 	php_info_print_table_end();
945 }
946 /* }}} */
947 
948 /* {{{ proto void libxml_set_streams_context(resource streams_context)
949    Set the streams context for the next libxml document load or write */
PHP_FUNCTION(libxml_set_streams_context)950 static PHP_FUNCTION(libxml_set_streams_context)
951 {
952 	zval *arg;
953 
954 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg) == FAILURE) {
955 		return;
956 	}
957 	if (!Z_ISUNDEF(LIBXML(stream_context))) {
958 		zval_ptr_dtor(&LIBXML(stream_context));
959 		ZVAL_UNDEF(&LIBXML(stream_context));
960 	}
961 	ZVAL_COPY(&LIBXML(stream_context), arg);
962 }
963 /* }}} */
964 
965 /* {{{ proto bool libxml_use_internal_errors([boolean use_errors])
966    Disable libxml errors and allow user to fetch error information as needed */
PHP_FUNCTION(libxml_use_internal_errors)967 static PHP_FUNCTION(libxml_use_internal_errors)
968 {
969 	xmlStructuredErrorFunc current_handler;
970 	zend_bool use_errors=0, retval;
971 
972 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &use_errors) == FAILURE) {
973 		return;
974 	}
975 
976 	current_handler = xmlStructuredError;
977 	if (current_handler && current_handler == php_libxml_structured_error_handler) {
978 		retval = 1;
979 	} else {
980 		retval = 0;
981 	}
982 
983 	if (ZEND_NUM_ARGS() == 0) {
984 		RETURN_BOOL(retval);
985 	}
986 
987 	if (use_errors == 0) {
988 		xmlSetStructuredErrorFunc(NULL, NULL);
989 		if (LIBXML(error_list)) {
990 			zend_llist_destroy(LIBXML(error_list));
991 			efree(LIBXML(error_list));
992 			LIBXML(error_list) = NULL;
993 		}
994 	} else {
995 		xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
996 		if (LIBXML(error_list) == NULL) {
997 			LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
998 			zend_llist_init(LIBXML(error_list), sizeof(xmlError), (llist_dtor_func_t) _php_libxml_free_error, 0);
999 		}
1000 	}
1001 	RETURN_BOOL(retval);
1002 }
1003 /* }}} */
1004 
1005 /* {{{ proto object libxml_get_last_error()
1006    Retrieve last error from libxml */
PHP_FUNCTION(libxml_get_last_error)1007 static PHP_FUNCTION(libxml_get_last_error)
1008 {
1009 	xmlErrorPtr error;
1010 
1011 	error = xmlGetLastError();
1012 
1013 	if (error) {
1014 		object_init_ex(return_value, libxmlerror_class_entry);
1015 		add_property_long(return_value, "level", error->level);
1016 		add_property_long(return_value, "code", error->code);
1017 		add_property_long(return_value, "column", error->int2);
1018 		if (error->message) {
1019 			add_property_string(return_value, "message", error->message);
1020 		} else {
1021 			add_property_stringl(return_value, "message", "", 0);
1022 		}
1023 		if (error->file) {
1024 			add_property_string(return_value, "file", error->file);
1025 		} else {
1026 			add_property_stringl(return_value, "file", "", 0);
1027 		}
1028 		add_property_long(return_value, "line", error->line);
1029 	} else {
1030 		RETURN_FALSE;
1031 	}
1032 }
1033 /* }}} */
1034 
1035 /* {{{ proto object libxml_get_errors()
1036    Retrieve array of errors */
PHP_FUNCTION(libxml_get_errors)1037 static PHP_FUNCTION(libxml_get_errors)
1038 {
1039 
1040 	xmlErrorPtr error;
1041 
1042 	if (array_init(return_value) == FAILURE) {
1043 		RETURN_FALSE;
1044 	}
1045 
1046 	if (LIBXML(error_list)) {
1047 
1048 		error = zend_llist_get_first(LIBXML(error_list));
1049 
1050 		while (error != NULL) {
1051 			zval z_error;
1052 
1053 			object_init_ex(&z_error, libxmlerror_class_entry);
1054 			add_property_long_ex(&z_error, "level", sizeof("level") - 1, error->level);
1055 			add_property_long_ex(&z_error, "code", sizeof("code") - 1, error->code);
1056 			add_property_long_ex(&z_error, "column", sizeof("column") - 1, error->int2 );
1057 			if (error->message) {
1058 				add_property_string_ex(&z_error, "message", sizeof("message") - 1, error->message);
1059 			} else {
1060 				add_property_stringl_ex(&z_error, "message", sizeof("message") - 1, "", 0);
1061 			}
1062 			if (error->file) {
1063 				add_property_string_ex(&z_error, "file", sizeof("file") - 1, error->file);
1064 			} else {
1065 				add_property_stringl_ex(&z_error, "file", sizeof("file") - 1, "", 0);
1066 			}
1067 			add_property_long_ex(&z_error, "line", sizeof("line") - 1, error->line);
1068 			add_next_index_zval(return_value, &z_error);
1069 
1070 			error = zend_llist_get_next(LIBXML(error_list));
1071 		}
1072 	}
1073 }
1074 /* }}} */
1075 
1076 /* {{{ proto void libxml_clear_errors()
1077    Clear last error from libxml */
PHP_FUNCTION(libxml_clear_errors)1078 static PHP_FUNCTION(libxml_clear_errors)
1079 {
1080 	xmlResetLastError();
1081 	if (LIBXML(error_list)) {
1082 		zend_llist_clean(LIBXML(error_list));
1083 	}
1084 }
1085 /* }}} */
1086 
php_libxml_disable_entity_loader(zend_bool disable)1087 PHP_LIBXML_API zend_bool php_libxml_disable_entity_loader(zend_bool disable) /* {{{ */
1088 {
1089 	zend_bool old = LIBXML(entity_loader_disabled);
1090 
1091 	LIBXML(entity_loader_disabled) = disable;
1092 	return old;
1093 } /* }}} */
1094 
1095 /* {{{ proto bool libxml_disable_entity_loader([boolean disable])
1096    Disable/Enable ability to load external entities */
PHP_FUNCTION(libxml_disable_entity_loader)1097 static PHP_FUNCTION(libxml_disable_entity_loader)
1098 {
1099 	zend_bool disable = 1;
1100 
1101 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &disable) == FAILURE) {
1102 		return;
1103 	}
1104 
1105 	RETURN_BOOL(php_libxml_disable_entity_loader(disable));
1106 }
1107 /* }}} */
1108 
1109 /* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
1110    Changes the default external entity loader */
PHP_FUNCTION(libxml_set_external_entity_loader)1111 static PHP_FUNCTION(libxml_set_external_entity_loader)
1112 {
1113 	zend_fcall_info			fci;
1114 	zend_fcall_info_cache	fcc;
1115 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!", &fci, &fcc)
1116 			== FAILURE) {
1117 		return;
1118 	}
1119 
1120 	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
1121 
1122 	if (fci.size > 0) { /* argument not null */
1123 		LIBXML(entity_loader).fci = fci;
1124 		Z_ADDREF(fci.function_name);
1125 		if (fci.object != NULL) {
1126 			ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object);
1127 			Z_ADDREF(LIBXML(entity_loader).object);
1128 		}
1129 		LIBXML(entity_loader).fcc = fcc;
1130 	}
1131 
1132 	RETURN_TRUE;
1133 }
1134 /* }}} */
1135 
1136 /* {{{ Common functions shared by extensions */
php_libxml_xmlCheckUTF8(const unsigned char * s)1137 int php_libxml_xmlCheckUTF8(const unsigned char *s)
1138 {
1139 	int i;
1140 	unsigned char c;
1141 
1142 	for (i = 0; (c = s[i++]);) {
1143 		if ((c & 0x80) == 0) {
1144 		} else if ((c & 0xe0) == 0xc0) {
1145 			if ((s[i++] & 0xc0) != 0x80) {
1146 				return 0;
1147 			}
1148 		} else if ((c & 0xf0) == 0xe0) {
1149 			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1150 				return 0;
1151 			}
1152 		} else if ((c & 0xf8) == 0xf0) {
1153 			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1154 				return 0;
1155 			}
1156 		} else {
1157 			return 0;
1158 		}
1159 	}
1160 	return 1;
1161 }
1162 
php_libxml_register_export(zend_class_entry * ce,php_libxml_export_node export_function)1163 zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
1164 {
1165 	php_libxml_func_handler export_hnd;
1166 
1167 	/* Initialize in case this module hasn't been loaded yet */
1168 	php_libxml_initialize();
1169 	export_hnd.export_func = export_function;
1170 
1171 	return zend_hash_add_mem(&php_libxml_exports, ce->name, &export_hnd, sizeof(export_hnd));
1172 }
1173 
php_libxml_import_node(zval * object)1174 PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object)
1175 {
1176 	zend_class_entry *ce = NULL;
1177 	xmlNodePtr node = NULL;
1178 	php_libxml_func_handler *export_hnd;
1179 
1180 	if (Z_TYPE_P(object) == IS_OBJECT) {
1181 		ce = Z_OBJCE_P(object);
1182 		while (ce->parent != NULL) {
1183 			ce = ce->parent;
1184 		}
1185 		if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) {
1186 			node = export_hnd->export_func(object);
1187 		}
1188 	}
1189 	return node;
1190 }
1191 
php_libxml_increment_node_ptr(php_libxml_node_object * object,xmlNodePtr node,void * private_data)1192 PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data)
1193 {
1194 	int ret_refcount = -1;
1195 
1196 	if (object != NULL && node != NULL) {
1197 		if (object->node != NULL) {
1198 			if (object->node->node == node) {
1199 				return object->node->refcount;
1200 			} else {
1201 				php_libxml_decrement_node_ptr(object);
1202 			}
1203 		}
1204 		if (node->_private != NULL) {
1205 			object->node = node->_private;
1206 			ret_refcount = ++object->node->refcount;
1207 			/* Only dom uses _private */
1208 			if (object->node->_private == NULL) {
1209 				object->node->_private = private_data;
1210 			}
1211 		} else {
1212 			ret_refcount = 1;
1213 			object->node = emalloc(sizeof(php_libxml_node_ptr));
1214 			object->node->node = node;
1215 			object->node->refcount = 1;
1216 			object->node->_private = private_data;
1217 			node->_private = object->node;
1218 		}
1219 	}
1220 
1221 	return ret_refcount;
1222 }
1223 
php_libxml_decrement_node_ptr(php_libxml_node_object * object)1224 PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object)
1225 {
1226 	int ret_refcount = -1;
1227 	php_libxml_node_ptr *obj_node;
1228 
1229 	if (object != NULL && object->node != NULL) {
1230 		obj_node = (php_libxml_node_ptr *) object->node;
1231 		ret_refcount = --obj_node->refcount;
1232 		if (ret_refcount == 0) {
1233 			if (obj_node->node != NULL) {
1234 				obj_node->node->_private = NULL;
1235 			}
1236 			efree(obj_node);
1237 		}
1238 		object->node = NULL;
1239 	}
1240 
1241 	return ret_refcount;
1242 }
1243 
php_libxml_increment_doc_ref(php_libxml_node_object * object,xmlDocPtr docp)1244 PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp)
1245 {
1246 	int ret_refcount = -1;
1247 
1248 	if (object->document != NULL) {
1249 		object->document->refcount++;
1250 		ret_refcount = object->document->refcount;
1251 	} else if (docp != NULL) {
1252 		ret_refcount = 1;
1253 		object->document = emalloc(sizeof(php_libxml_ref_obj));
1254 		object->document->ptr = docp;
1255 		object->document->refcount = ret_refcount;
1256 		object->document->doc_props = NULL;
1257 	}
1258 
1259 	return ret_refcount;
1260 }
1261 
php_libxml_decrement_doc_ref(php_libxml_node_object * object)1262 PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object)
1263 {
1264 	int ret_refcount = -1;
1265 
1266 	if (object != NULL && object->document != NULL) {
1267 		ret_refcount = --object->document->refcount;
1268 		if (ret_refcount == 0) {
1269 			if (object->document->ptr != NULL) {
1270 				xmlFreeDoc((xmlDoc *) object->document->ptr);
1271 			}
1272 			if (object->document->doc_props != NULL) {
1273 				if (object->document->doc_props->classmap) {
1274 					zend_hash_destroy(object->document->doc_props->classmap);
1275 					FREE_HASHTABLE(object->document->doc_props->classmap);
1276 				}
1277 				efree(object->document->doc_props);
1278 			}
1279 			efree(object->document);
1280 		}
1281 		object->document = NULL;
1282 	}
1283 
1284 	return ret_refcount;
1285 }
1286 
php_libxml_node_free_resource(xmlNodePtr node)1287 PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node)
1288 {
1289 	if (!node) {
1290 		return;
1291 	}
1292 
1293 	switch (node->type) {
1294 		case XML_DOCUMENT_NODE:
1295 		case XML_HTML_DOCUMENT_NODE:
1296 			break;
1297 		default:
1298 			if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1299 				php_libxml_node_free_list((xmlNodePtr) node->children);
1300 				switch (node->type) {
1301 					/* Skip property freeing for the following types */
1302 					case XML_ATTRIBUTE_DECL:
1303 					case XML_DTD_NODE:
1304 					case XML_DOCUMENT_TYPE_NODE:
1305 					case XML_ENTITY_DECL:
1306 					case XML_ATTRIBUTE_NODE:
1307 					case XML_NAMESPACE_DECL:
1308 					case XML_TEXT_NODE:
1309 						break;
1310 					default:
1311 						php_libxml_node_free_list((xmlNodePtr) node->properties);
1312 				}
1313 				if (php_libxml_unregister_node(node) == 0) {
1314 					node->doc = NULL;
1315 				}
1316 				php_libxml_node_free(node);
1317 			} else {
1318 				php_libxml_unregister_node(node);
1319 			}
1320 	}
1321 }
1322 
php_libxml_node_decrement_resource(php_libxml_node_object * object)1323 PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object)
1324 {
1325 	int ret_refcount = -1;
1326 	xmlNodePtr nodep;
1327 	php_libxml_node_ptr *obj_node;
1328 
1329 	if (object != NULL && object->node != NULL) {
1330 		obj_node = (php_libxml_node_ptr *) object->node;
1331 		nodep = object->node->node;
1332 		ret_refcount = php_libxml_decrement_node_ptr(object);
1333 		if (ret_refcount == 0) {
1334 			php_libxml_node_free_resource(nodep);
1335 		} else {
1336 			if (obj_node && object == obj_node->_private) {
1337 				obj_node->_private = NULL;
1338 			}
1339 		}
1340 	}
1341 	if (object != NULL && object->document != NULL) {
1342 		/* Safe to call as if the resource were freed then doc pointer is NULL */
1343 		php_libxml_decrement_doc_ref(object);
1344 	}
1345 }
1346 /* }}} */
1347 
1348 #if defined(PHP_WIN32) && defined(COMPILE_DL_LIBXML)
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)1349 PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1350 {
1351 	return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1352 }
1353 #endif
1354 
1355 #endif
1356 
1357 /*
1358  * Local variables:
1359  * tab-width: 4
1360  * c-basic-offset: 4
1361  * End:
1362  * vim600: sw=4 ts=4 fdm=marker
1363  * vim<600: sw=4 ts=4
1364  */
1365