1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2016 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 */
57static int _php_libxml_initialized = 0;
58static int _php_libxml_per_request_initialization = 1;
59static xmlExternalEntityLoader _php_libxml_default_entity_loader;
60
61typedef struct _php_libxml_func_handler {
62	php_libxml_export_node export_func;
63} php_libxml_func_handler;
64
65static HashTable php_libxml_exports;
66
67static ZEND_DECLARE_MODULE_GLOBALS(libxml)
68static PHP_GINIT_FUNCTION(libxml);
69
70static PHP_FUNCTION(libxml_set_streams_context);
71static PHP_FUNCTION(libxml_use_internal_errors);
72static PHP_FUNCTION(libxml_get_last_error);
73static PHP_FUNCTION(libxml_clear_errors);
74static PHP_FUNCTION(libxml_get_errors);
75static PHP_FUNCTION(libxml_set_external_entity_loader);
76static PHP_FUNCTION(libxml_disable_entity_loader);
77
78static zend_class_entry *libxmlerror_class_entry;
79
80/* {{{ dynamically loadable module stuff */
81#ifdef COMPILE_DL_LIBXML
82#ifdef ZTS
83ZEND_TSRMLS_CACHE_DEFINE()
84#endif
85ZEND_GET_MODULE(libxml)
86#endif /* COMPILE_DL_LIBXML */
87/* }}} */
88
89/* {{{ function prototypes */
90static PHP_MINIT_FUNCTION(libxml);
91static PHP_RINIT_FUNCTION(libxml);
92static PHP_RSHUTDOWN_FUNCTION(libxml);
93static PHP_MSHUTDOWN_FUNCTION(libxml);
94static PHP_MINFO_FUNCTION(libxml);
95static int php_libxml_post_deactivate(void);
96
97/* }}} */
98
99/* {{{ arginfo */
100ZEND_BEGIN_ARG_INFO(arginfo_libxml_set_streams_context, 0)
101	ZEND_ARG_INFO(0, context)
102ZEND_END_ARG_INFO()
103
104ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_use_internal_errors, 0, 0, 0)
105	ZEND_ARG_INFO(0, use_errors)
106ZEND_END_ARG_INFO()
107
108ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_last_error, 0)
109ZEND_END_ARG_INFO()
110
111ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_errors, 0)
112ZEND_END_ARG_INFO()
113
114ZEND_BEGIN_ARG_INFO(arginfo_libxml_clear_errors, 0)
115ZEND_END_ARG_INFO()
116
117ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_disable_entity_loader, 0, 0, 0)
118	ZEND_ARG_INFO(0, disable)
119ZEND_END_ARG_INFO()
120
121ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_set_external_entity_loader, 0, 0, 1)
122	ZEND_ARG_INFO(0, resolver_function)
123ZEND_END_ARG_INFO()
124/* }}} */
125
126/* {{{ extension definition structures */
127static 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
138zend_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 */
158static 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
167static 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
188static 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
227static 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 */
272static 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
285static 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
300static 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
369static 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
374static 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
379static 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
384static 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
392static int php_libxml_streams_IO_close(void *context)
393{
394	return php_stream_close((php_stream*)context);
395}
396
397static xmlParserInputBufferPtr
398php_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
428static xmlOutputBufferPtr
429php_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
473static 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
480static 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
512static 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
527void 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
536static 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
574static 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) {
638is_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
695static 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
715PHP_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
723PHP_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
731PHP_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
738PHP_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
746static void php_libxml_exports_dtor(zval *zv)
747{
748	free(Z_PTR_P(zv));
749}
750
751PHP_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
766PHP_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
780PHP_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
790static 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			"fpm-fcgi",
852			"litespeed",
853			NULL
854		};
855		const char * const *sapi_name;
856
857		for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
858			if (strcmp(sapi_module.name, *sapi_name) == 0) {
859				_php_libxml_per_request_initialization = 0;
860				break;
861			}
862		}
863	}
864
865	if (!_php_libxml_per_request_initialization) {
866		/* report errors via handler rather than stderr */
867		xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
868		xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
869		xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
870	}
871
872	return SUCCESS;
873}
874
875
876static PHP_RINIT_FUNCTION(libxml)
877{
878	if (_php_libxml_per_request_initialization) {
879		/* report errors via handler rather than stderr */
880		xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
881		xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
882		xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
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
893static PHP_RSHUTDOWN_FUNCTION(libxml)
894{
895	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
896
897	return SUCCESS;
898}
899
900static 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
913static 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
938static 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 */
951static PHP_FUNCTION(libxml_set_streams_context)
952{
953	zval *arg;
954
955	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg) == FAILURE) {
956		return;
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	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &use_errors) == FAILURE) {
974		return;
975	}
976
977	current_handler = xmlStructuredError;
978	if (current_handler && current_handler == php_libxml_structured_error_handler) {
979		retval = 1;
980	} else {
981		retval = 0;
982	}
983
984	if (ZEND_NUM_ARGS() == 0) {
985		RETURN_BOOL(retval);
986	}
987
988	if (use_errors == 0) {
989		xmlSetStructuredErrorFunc(NULL, NULL);
990		if (LIBXML(error_list)) {
991			zend_llist_destroy(LIBXML(error_list));
992			efree(LIBXML(error_list));
993			LIBXML(error_list) = NULL;
994		}
995	} else {
996		xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
997		if (LIBXML(error_list) == NULL) {
998			LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
999			zend_llist_init(LIBXML(error_list), sizeof(xmlError), (llist_dtor_func_t) _php_libxml_free_error, 0);
1000		}
1001	}
1002	RETURN_BOOL(retval);
1003}
1004/* }}} */
1005
1006/* {{{ proto object libxml_get_last_error()
1007   Retrieve last error from libxml */
1008static PHP_FUNCTION(libxml_get_last_error)
1009{
1010	xmlErrorPtr error;
1011
1012	error = xmlGetLastError();
1013
1014	if (error) {
1015		object_init_ex(return_value, libxmlerror_class_entry);
1016		add_property_long(return_value, "level", error->level);
1017		add_property_long(return_value, "code", error->code);
1018		add_property_long(return_value, "column", error->int2);
1019		if (error->message) {
1020			add_property_string(return_value, "message", error->message);
1021		} else {
1022			add_property_stringl(return_value, "message", "", 0);
1023		}
1024		if (error->file) {
1025			add_property_string(return_value, "file", error->file);
1026		} else {
1027			add_property_stringl(return_value, "file", "", 0);
1028		}
1029		add_property_long(return_value, "line", error->line);
1030	} else {
1031		RETURN_FALSE;
1032	}
1033}
1034/* }}} */
1035
1036/* {{{ proto object libxml_get_errors()
1037   Retrieve array of errors */
1038static PHP_FUNCTION(libxml_get_errors)
1039{
1040
1041	xmlErrorPtr error;
1042
1043	if (array_init(return_value) == FAILURE) {
1044		RETURN_FALSE;
1045	}
1046
1047	if (LIBXML(error_list)) {
1048
1049		error = zend_llist_get_first(LIBXML(error_list));
1050
1051		while (error != NULL) {
1052			zval z_error;
1053
1054			object_init_ex(&z_error, libxmlerror_class_entry);
1055			add_property_long_ex(&z_error, "level", sizeof("level") - 1, error->level);
1056			add_property_long_ex(&z_error, "code", sizeof("code") - 1, error->code);
1057			add_property_long_ex(&z_error, "column", sizeof("column") - 1, error->int2 );
1058			if (error->message) {
1059				add_property_string_ex(&z_error, "message", sizeof("message") - 1, error->message);
1060			} else {
1061				add_property_stringl_ex(&z_error, "message", sizeof("message") - 1, "", 0);
1062			}
1063			if (error->file) {
1064				add_property_string_ex(&z_error, "file", sizeof("file") - 1, error->file);
1065			} else {
1066				add_property_stringl_ex(&z_error, "file", sizeof("file") - 1, "", 0);
1067			}
1068			add_property_long_ex(&z_error, "line", sizeof("line") - 1, error->line);
1069			add_next_index_zval(return_value, &z_error);
1070
1071			error = zend_llist_get_next(LIBXML(error_list));
1072		}
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	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &disable) == FAILURE) {
1103		return;
1104	}
1105
1106	RETURN_BOOL(php_libxml_disable_entity_loader(disable));
1107}
1108/* }}} */
1109
1110/* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
1111   Changes the default external entity loader */
1112static PHP_FUNCTION(libxml_set_external_entity_loader)
1113{
1114	zend_fcall_info			fci;
1115	zend_fcall_info_cache	fcc;
1116	if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!", &fci, &fcc)
1117			== FAILURE) {
1118		return;
1119	}
1120
1121	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
1122
1123	if (fci.size > 0) { /* argument not null */
1124		LIBXML(entity_loader).fci = fci;
1125		Z_ADDREF(fci.function_name);
1126		if (fci.object != NULL) {
1127			ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object);
1128			Z_ADDREF(LIBXML(entity_loader).object);
1129		}
1130		LIBXML(entity_loader).fcc = fcc;
1131	}
1132
1133	RETURN_TRUE;
1134}
1135/* }}} */
1136
1137/* {{{ Common functions shared by extensions */
1138int php_libxml_xmlCheckUTF8(const unsigned char *s)
1139{
1140	int i;
1141	unsigned char c;
1142
1143	for (i = 0; (c = s[i++]);) {
1144		if ((c & 0x80) == 0) {
1145		} else if ((c & 0xe0) == 0xc0) {
1146			if ((s[i++] & 0xc0) != 0x80) {
1147				return 0;
1148			}
1149		} else if ((c & 0xf0) == 0xe0) {
1150			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1151				return 0;
1152			}
1153		} else if ((c & 0xf8) == 0xf0) {
1154			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1155				return 0;
1156			}
1157		} else {
1158			return 0;
1159		}
1160	}
1161	return 1;
1162}
1163
1164zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
1165{
1166	php_libxml_func_handler export_hnd;
1167
1168	/* Initialize in case this module hasn't been loaded yet */
1169	php_libxml_initialize();
1170	export_hnd.export_func = export_function;
1171
1172	return zend_hash_add_mem(&php_libxml_exports, ce->name, &export_hnd, sizeof(export_hnd));
1173}
1174
1175PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object)
1176{
1177	zend_class_entry *ce = NULL;
1178	xmlNodePtr node = NULL;
1179	php_libxml_func_handler *export_hnd;
1180
1181	if (Z_TYPE_P(object) == IS_OBJECT) {
1182		ce = Z_OBJCE_P(object);
1183		while (ce->parent != NULL) {
1184			ce = ce->parent;
1185		}
1186		if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) {
1187			node = export_hnd->export_func(object);
1188		}
1189	}
1190	return node;
1191}
1192
1193PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data)
1194{
1195	int ret_refcount = -1;
1196
1197	if (object != NULL && node != NULL) {
1198		if (object->node != NULL) {
1199			if (object->node->node == node) {
1200				return object->node->refcount;
1201			} else {
1202				php_libxml_decrement_node_ptr(object);
1203			}
1204		}
1205		if (node->_private != NULL) {
1206			object->node = node->_private;
1207			ret_refcount = ++object->node->refcount;
1208			/* Only dom uses _private */
1209			if (object->node->_private == NULL) {
1210				object->node->_private = private_data;
1211			}
1212		} else {
1213			ret_refcount = 1;
1214			object->node = emalloc(sizeof(php_libxml_node_ptr));
1215			object->node->node = node;
1216			object->node->refcount = 1;
1217			object->node->_private = private_data;
1218			node->_private = object->node;
1219		}
1220	}
1221
1222	return ret_refcount;
1223}
1224
1225PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object)
1226{
1227	int ret_refcount = -1;
1228	php_libxml_node_ptr *obj_node;
1229
1230	if (object != NULL && object->node != NULL) {
1231		obj_node = (php_libxml_node_ptr *) object->node;
1232		ret_refcount = --obj_node->refcount;
1233		if (ret_refcount == 0) {
1234			if (obj_node->node != NULL) {
1235				obj_node->node->_private = NULL;
1236			}
1237			efree(obj_node);
1238		}
1239		object->node = NULL;
1240	}
1241
1242	return ret_refcount;
1243}
1244
1245PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp)
1246{
1247	int ret_refcount = -1;
1248
1249	if (object->document != NULL) {
1250		object->document->refcount++;
1251		ret_refcount = object->document->refcount;
1252	} else if (docp != NULL) {
1253		ret_refcount = 1;
1254		object->document = emalloc(sizeof(php_libxml_ref_obj));
1255		object->document->ptr = docp;
1256		object->document->refcount = ret_refcount;
1257		object->document->doc_props = NULL;
1258	}
1259
1260	return ret_refcount;
1261}
1262
1263PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object)
1264{
1265	int ret_refcount = -1;
1266
1267	if (object != NULL && object->document != NULL) {
1268		ret_refcount = --object->document->refcount;
1269		if (ret_refcount == 0) {
1270			if (object->document->ptr != NULL) {
1271				xmlFreeDoc((xmlDoc *) object->document->ptr);
1272			}
1273			if (object->document->doc_props != NULL) {
1274				if (object->document->doc_props->classmap) {
1275					zend_hash_destroy(object->document->doc_props->classmap);
1276					FREE_HASHTABLE(object->document->doc_props->classmap);
1277				}
1278				efree(object->document->doc_props);
1279			}
1280			efree(object->document);
1281			object->document = NULL;
1282		}
1283	}
1284
1285	return ret_refcount;
1286}
1287
1288PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node)
1289{
1290	if (!node) {
1291		return;
1292	}
1293
1294	switch (node->type) {
1295		case XML_DOCUMENT_NODE:
1296		case XML_HTML_DOCUMENT_NODE:
1297			break;
1298		default:
1299			if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1300				php_libxml_node_free_list((xmlNodePtr) node->children);
1301				switch (node->type) {
1302					/* Skip property freeing for the following types */
1303					case XML_ATTRIBUTE_DECL:
1304					case XML_DTD_NODE:
1305					case XML_DOCUMENT_TYPE_NODE:
1306					case XML_ENTITY_DECL:
1307					case XML_ATTRIBUTE_NODE:
1308					case XML_NAMESPACE_DECL:
1309					case XML_TEXT_NODE:
1310						break;
1311					default:
1312						php_libxml_node_free_list((xmlNodePtr) node->properties);
1313				}
1314				if (php_libxml_unregister_node(node) == 0) {
1315					node->doc = NULL;
1316				}
1317				php_libxml_node_free(node);
1318			} else {
1319				php_libxml_unregister_node(node);
1320			}
1321	}
1322}
1323
1324PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object)
1325{
1326	int ret_refcount = -1;
1327	xmlNodePtr nodep;
1328	php_libxml_node_ptr *obj_node;
1329
1330	if (object != NULL && object->node != NULL) {
1331		obj_node = (php_libxml_node_ptr *) object->node;
1332		nodep = object->node->node;
1333		ret_refcount = php_libxml_decrement_node_ptr(object);
1334		if (ret_refcount == 0) {
1335			php_libxml_node_free_resource(nodep);
1336		} else {
1337			if (obj_node && object == obj_node->_private) {
1338				obj_node->_private = NULL;
1339			}
1340		}
1341	}
1342	if (object != NULL && object->document != NULL) {
1343		/* Safe to call as if the resource were freed then doc pointer is NULL */
1344		php_libxml_decrement_doc_ref(object);
1345	}
1346}
1347/* }}} */
1348
1349#if defined(PHP_WIN32) && defined(COMPILE_DL_LIBXML)
1350PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1351{
1352	return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1353}
1354#endif
1355
1356#endif
1357
1358/*
1359 * Local variables:
1360 * tab-width: 4
1361 * c-basic-offset: 4
1362 * End:
1363 * vim600: sw=4 ts=4 fdm=marker
1364 * vim<600: sw=4 ts=4
1365 */
1366