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