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	return php_stream_write((php_stream*)context, buffer, len);
387}
388
389static int php_libxml_streams_IO_close(void *context)
390{
391	return php_stream_close((php_stream*)context);
392}
393
394static xmlParserInputBufferPtr
395php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc)
396{
397	xmlParserInputBufferPtr ret;
398	void *context = NULL;
399
400	if (LIBXML(entity_loader_disabled)) {
401		return NULL;
402	}
403
404	if (URI == NULL)
405		return(NULL);
406
407	context = php_libxml_streams_IO_open_read_wrapper(URI);
408
409	if (context == NULL) {
410		return(NULL);
411	}
412
413	/* Allocate the Input buffer front-end. */
414	ret = xmlAllocParserInputBuffer(enc);
415	if (ret != NULL) {
416		ret->context = context;
417		ret->readcallback = php_libxml_streams_IO_read;
418		ret->closecallback = php_libxml_streams_IO_close;
419	} else
420		php_libxml_streams_IO_close(context);
421
422	return(ret);
423}
424
425static xmlOutputBufferPtr
426php_libxml_output_buffer_create_filename(const char *URI,
427                              xmlCharEncodingHandlerPtr encoder,
428                              int compression ATTRIBUTE_UNUSED)
429{
430	xmlOutputBufferPtr ret;
431	xmlURIPtr puri;
432	void *context = NULL;
433	char *unescaped = NULL;
434
435	if (URI == NULL)
436		return(NULL);
437
438	puri = xmlParseURI(URI);
439	if (puri != NULL) {
440		if (puri->scheme != NULL)
441			unescaped = xmlURIUnescapeString(URI, 0, NULL);
442		xmlFreeURI(puri);
443	}
444
445	if (unescaped != NULL) {
446		context = php_libxml_streams_IO_open_write_wrapper(unescaped);
447		xmlFree(unescaped);
448	}
449
450	/* try with a non-escaped URI this may be a strange filename */
451	if (context == NULL) {
452		context = php_libxml_streams_IO_open_write_wrapper(URI);
453	}
454
455	if (context == NULL) {
456		return(NULL);
457	}
458
459	/* Allocate the Output buffer front-end. */
460	ret = xmlAllocOutputBuffer(encoder);
461	if (ret != NULL) {
462		ret->context = context;
463		ret->writecallback = php_libxml_streams_IO_write;
464		ret->closecallback = php_libxml_streams_IO_close;
465	}
466
467	return(ret);
468}
469
470static int _php_libxml_free_error(xmlErrorPtr error)
471{
472	/* This will free the libxml alloc'd memory */
473	xmlResetError(error);
474	return 1;
475}
476
477static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg)
478{
479	xmlError error_copy;
480	int ret;
481
482
483	memset(&error_copy, 0, sizeof(xmlError));
484
485	if (error) {
486		ret = xmlCopyError(error, &error_copy);
487	} else {
488		error_copy.domain = 0;
489		error_copy.code = XML_ERR_INTERNAL_ERROR;
490		error_copy.level = XML_ERR_ERROR;
491		error_copy.line = 0;
492		error_copy.node = NULL;
493		error_copy.int1 = 0;
494		error_copy.int2 = 0;
495		error_copy.ctxt = NULL;
496		error_copy.message = (char*)xmlStrdup((xmlChar*)msg);
497		error_copy.file = NULL;
498		error_copy.str1 = NULL;
499		error_copy.str2 = NULL;
500		error_copy.str3 = NULL;
501		ret = 0;
502	}
503
504	if (ret == 0) {
505		zend_llist_add_element(LIBXML(error_list), &error_copy);
506	}
507}
508
509static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg)
510{
511	xmlParserCtxtPtr parser;
512
513	parser = (xmlParserCtxtPtr) ctx;
514
515	if (parser != NULL && parser->input != NULL) {
516		if (parser->input->filename) {
517			php_error_docref(NULL, level, "%s in %s, line: %d", msg, parser->input->filename, parser->input->line);
518		} else {
519			php_error_docref(NULL, level, "%s in Entity, line: %d", msg, parser->input->line);
520		}
521	}
522}
523
524void php_libxml_issue_error(int level, const char *msg)
525{
526	if (LIBXML(error_list)) {
527		_php_list_set_error_structure(NULL, msg);
528	} else {
529		php_error_docref(NULL, level, "%s", msg);
530	}
531}
532
533static void php_libxml_internal_error_handler(int error_type, void *ctx, const char **msg, va_list ap)
534{
535	char *buf;
536	int len, len_iter, output = 0;
537
538
539	len = vspprintf(&buf, 0, *msg, ap);
540	len_iter = len;
541
542	/* remove any trailing \n */
543	while (len_iter && buf[--len_iter] == '\n') {
544		buf[len_iter] = '\0';
545		output = 1;
546	}
547
548	smart_str_appendl(&LIBXML(error_buffer), buf, len);
549
550	efree(buf);
551
552	if (output == 1) {
553		if (LIBXML(error_list)) {
554			_php_list_set_error_structure(NULL, ZSTR_VAL(LIBXML(error_buffer).s));
555		} else {
556			switch (error_type) {
557				case PHP_LIBXML_CTX_ERROR:
558					php_libxml_ctx_error_level(E_WARNING, ctx, ZSTR_VAL(LIBXML(error_buffer).s));
559					break;
560				case PHP_LIBXML_CTX_WARNING:
561					php_libxml_ctx_error_level(E_NOTICE, ctx, ZSTR_VAL(LIBXML(error_buffer).s));
562					break;
563				default:
564					php_error_docref(NULL, E_WARNING, "%s", ZSTR_VAL(LIBXML(error_buffer).s));
565			}
566		}
567		smart_str_free(&LIBXML(error_buffer));
568	}
569}
570
571static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,
572		const char *ID, xmlParserCtxtPtr context)
573{
574	xmlParserInputPtr	ret			= NULL;
575	const char			*resource	= NULL;
576	zval 				*ctxzv, retval;
577	zval				params[3];
578	int					status;
579	zend_fcall_info		*fci;
580
581	fci = &LIBXML(entity_loader).fci;
582
583	if (fci->size == 0) {
584		/* no custom user-land callback set up; delegate to original loader */
585		return _php_libxml_default_entity_loader(URL, ID, context);
586	}
587
588	if (ID != NULL) {
589		ZVAL_STRING(&params[0], ID);
590	} else {
591		ZVAL_UNDEF(&params[0]);
592	}
593	if (URL != NULL) {
594		ZVAL_STRING(&params[1], URL);
595	} else {
596		ZVAL_UNDEF(&params[1]);
597	}
598	ctxzv = &params[2];
599	array_init_size(ctxzv, 4);
600
601#define ADD_NULL_OR_STRING_KEY(memb) \
602	if (context->memb == NULL) { \
603		add_assoc_null_ex(ctxzv, #memb, sizeof(#memb) - 1); \
604	} else { \
605		add_assoc_string_ex(ctxzv, #memb, sizeof(#memb) - 1, \
606				(char *)context->memb); \
607	}
608
609	ADD_NULL_OR_STRING_KEY(directory)
610	ADD_NULL_OR_STRING_KEY(intSubName)
611	ADD_NULL_OR_STRING_KEY(extSubURI)
612	ADD_NULL_OR_STRING_KEY(extSubSystem)
613
614#undef ADD_NULL_OR_STRING_KEY
615
616	fci->retval	= &retval;
617	fci->params	= params;
618	fci->param_count = sizeof(params)/sizeof(*params);
619	fci->no_separation	= 1;
620
621	status = zend_call_function(fci, &LIBXML(entity_loader).fcc);
622	if (status != SUCCESS || Z_ISUNDEF(retval)) {
623		php_libxml_ctx_error(context,
624				"Call to user entity loader callback '%s' has failed",
625				Z_STRVAL(fci->function_name));
626	} else {
627		/*
628		retval_ptr = *fci->retval_ptr_ptr;
629		if (retval_ptr == NULL) {
630			php_libxml_ctx_error(context,
631					"Call to user entity loader callback '%s' has failed; "
632					"probably it has thrown an exception",
633					fci->function_name);
634		} else */ if (Z_TYPE(retval) == IS_STRING) {
635is_string:
636			resource = Z_STRVAL(retval);
637		} else if (Z_TYPE(retval) == IS_RESOURCE) {
638			php_stream *stream;
639			php_stream_from_zval_no_verify(stream, &retval);
640			if (stream == NULL) {
641				php_libxml_ctx_error(context,
642						"The user entity loader callback '%s' has returned a "
643						"resource, but it is not a stream",
644						Z_STRVAL(fci->function_name));
645			} else {
646				/* TODO: allow storing the encoding in the stream context? */
647				xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
648				xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
649				if (pib == NULL) {
650					php_libxml_ctx_error(context, "Could not allocate parser "
651							"input buffer");
652				} else {
653					/* make stream not being closed when the zval is freed */
654					++GC_REFCOUNT(stream->res);
655					pib->context = stream;
656					pib->readcallback = php_libxml_streams_IO_read;
657					pib->closecallback = php_libxml_streams_IO_close;
658
659					ret = xmlNewIOInputStream(context, pib, enc);
660					if (ret == NULL) {
661						xmlFreeParserInputBuffer(pib);
662					}
663				}
664			}
665		} else if (Z_TYPE(retval) != IS_NULL) {
666			/* retval not string nor resource nor null; convert to string */
667			convert_to_string(&retval);
668			goto is_string;
669		} /* else is null; don't try anything */
670	}
671
672	if (ret == NULL) {
673		if (resource == NULL) {
674			if (ID == NULL) {
675				ID = "NULL";
676			}
677			php_libxml_ctx_error(context,
678					"Failed to load external entity \"%s\"\n", ID);
679		} else {
680			/* we got the resource in the form of a string; open it */
681			ret = xmlNewInputFromFile(context, resource);
682		}
683	}
684
685	zval_ptr_dtor(&params[0]);
686	zval_ptr_dtor(&params[1]);
687	zval_ptr_dtor(&params[2]);
688	zval_ptr_dtor(&retval);
689	return ret;
690}
691
692static xmlParserInputPtr _php_libxml_pre_ext_ent_loader(const char *URL,
693		const char *ID, xmlParserCtxtPtr context)
694{
695
696	/* Check whether we're running in a PHP context, since the entity loader
697	 * we've defined is an application level (true global) setting.
698	 * If we are, we also want to check whether we've finished activating
699	 * the modules (RINIT phase). Using our external entity loader during a
700	 * RINIT should not be problem per se (though during MINIT it is, because
701	 * we don't even have a resource list by then), but then whether one
702	 * extension would be using the custom external entity loader or not
703	 * could depend on extension loading order
704	 * (if _php_libxml_per_request_initialization */
705	if (xmlGenericError == php_libxml_error_handler && PG(modules_activated)) {
706		return _php_libxml_external_entity_loader(URL, ID, context);
707	} else {
708		return _php_libxml_default_entity_loader(URL, ID, context);
709	}
710}
711
712PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...)
713{
714	va_list args;
715	va_start(args, msg);
716	php_libxml_internal_error_handler(PHP_LIBXML_CTX_ERROR, ctx, &msg, args);
717	va_end(args);
718}
719
720PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...)
721{
722	va_list args;
723	va_start(args, msg);
724	php_libxml_internal_error_handler(PHP_LIBXML_CTX_WARNING, ctx, &msg, args);
725	va_end(args);
726}
727
728PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error)
729{
730	_php_list_set_error_structure(error, NULL);
731
732	return;
733}
734
735PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...)
736{
737	va_list args;
738	va_start(args, msg);
739	php_libxml_internal_error_handler(PHP_LIBXML_ERROR, ctx, &msg, args);
740	va_end(args);
741}
742
743static void php_libxml_exports_dtor(zval *zv)
744{
745	free(Z_PTR_P(zv));
746}
747
748PHP_LIBXML_API void php_libxml_initialize(void)
749{
750	if (!_php_libxml_initialized) {
751		/* we should be the only one's to ever init!! */
752		xmlInitParser();
753
754		_php_libxml_default_entity_loader = xmlGetExternalEntityLoader();
755		xmlSetExternalEntityLoader(_php_libxml_pre_ext_ent_loader);
756
757		zend_hash_init(&php_libxml_exports, 0, NULL, php_libxml_exports_dtor, 1);
758
759		_php_libxml_initialized = 1;
760	}
761}
762
763PHP_LIBXML_API void php_libxml_shutdown(void)
764{
765	if (_php_libxml_initialized) {
766#if defined(LIBXML_SCHEMAS_ENABLED)
767		xmlRelaxNGCleanupTypes();
768#endif
769		/* xmlCleanupParser(); */
770		zend_hash_destroy(&php_libxml_exports);
771
772		xmlSetExternalEntityLoader(_php_libxml_default_entity_loader);
773		_php_libxml_initialized = 0;
774	}
775}
776
777PHP_LIBXML_API void php_libxml_switch_context(zval *context, zval *oldcontext)
778{
779	if (oldcontext) {
780		ZVAL_COPY_VALUE(oldcontext, &LIBXML(stream_context));
781	}
782	if (context) {
783		ZVAL_COPY_VALUE(&LIBXML(stream_context), context);
784	}
785}
786
787static PHP_MINIT_FUNCTION(libxml)
788{
789	zend_class_entry ce;
790
791	php_libxml_initialize();
792
793	REGISTER_LONG_CONSTANT("LIBXML_VERSION",			LIBXML_VERSION,			CONST_CS | CONST_PERSISTENT);
794	REGISTER_STRING_CONSTANT("LIBXML_DOTTED_VERSION",	LIBXML_DOTTED_VERSION,	CONST_CS | CONST_PERSISTENT);
795	REGISTER_STRING_CONSTANT("LIBXML_LOADED_VERSION",	(char *)xmlParserVersion,		CONST_CS | CONST_PERSISTENT);
796
797	/* For use with loading xml */
798	REGISTER_LONG_CONSTANT("LIBXML_NOENT",		XML_PARSE_NOENT,		CONST_CS | CONST_PERSISTENT);
799	REGISTER_LONG_CONSTANT("LIBXML_DTDLOAD",	XML_PARSE_DTDLOAD,		CONST_CS | CONST_PERSISTENT);
800	REGISTER_LONG_CONSTANT("LIBXML_DTDATTR",	XML_PARSE_DTDATTR,		CONST_CS | CONST_PERSISTENT);
801	REGISTER_LONG_CONSTANT("LIBXML_DTDVALID",	XML_PARSE_DTDVALID,		CONST_CS | CONST_PERSISTENT);
802	REGISTER_LONG_CONSTANT("LIBXML_NOERROR",	XML_PARSE_NOERROR,		CONST_CS | CONST_PERSISTENT);
803	REGISTER_LONG_CONSTANT("LIBXML_NOWARNING",	XML_PARSE_NOWARNING,	CONST_CS | CONST_PERSISTENT);
804	REGISTER_LONG_CONSTANT("LIBXML_NOBLANKS",	XML_PARSE_NOBLANKS,		CONST_CS | CONST_PERSISTENT);
805	REGISTER_LONG_CONSTANT("LIBXML_XINCLUDE",	XML_PARSE_XINCLUDE,		CONST_CS | CONST_PERSISTENT);
806	REGISTER_LONG_CONSTANT("LIBXML_NSCLEAN",	XML_PARSE_NSCLEAN,		CONST_CS | CONST_PERSISTENT);
807	REGISTER_LONG_CONSTANT("LIBXML_NOCDATA",	XML_PARSE_NOCDATA,		CONST_CS | CONST_PERSISTENT);
808	REGISTER_LONG_CONSTANT("LIBXML_NONET",		XML_PARSE_NONET,		CONST_CS | CONST_PERSISTENT);
809	REGISTER_LONG_CONSTANT("LIBXML_PEDANTIC",	XML_PARSE_PEDANTIC,		CONST_CS | CONST_PERSISTENT);
810#if LIBXML_VERSION >= 20621
811	REGISTER_LONG_CONSTANT("LIBXML_COMPACT",	XML_PARSE_COMPACT,		CONST_CS | CONST_PERSISTENT);
812	REGISTER_LONG_CONSTANT("LIBXML_NOXMLDECL",	XML_SAVE_NO_DECL,		CONST_CS | CONST_PERSISTENT);
813#endif
814#if LIBXML_VERSION >= 20703
815	REGISTER_LONG_CONSTANT("LIBXML_PARSEHUGE",	XML_PARSE_HUGE,			CONST_CS | CONST_PERSISTENT);
816#endif
817#if LIBXML_VERSION >= 20900
818	REGISTER_LONG_CONSTANT("LIBXML_BIGLINES",	XML_PARSE_BIG_LINES,	CONST_CS | CONST_PERSISTENT);
819#endif
820	REGISTER_LONG_CONSTANT("LIBXML_NOEMPTYTAG",	LIBXML_SAVE_NOEMPTYTAG,	CONST_CS | CONST_PERSISTENT);
821
822	/* Schema validation options */
823#if defined(LIBXML_SCHEMAS_ENABLED) && LIBXML_VERSION >= 20614
824	REGISTER_LONG_CONSTANT("LIBXML_SCHEMA_CREATE",	XML_SCHEMA_VAL_VC_I_CREATE,	CONST_CS | CONST_PERSISTENT);
825#endif
826
827	/* Additional constants for use with loading html */
828#if LIBXML_VERSION >= 20707
829	REGISTER_LONG_CONSTANT("LIBXML_HTML_NOIMPLIED",	HTML_PARSE_NOIMPLIED,		CONST_CS | CONST_PERSISTENT);
830#endif
831
832#if LIBXML_VERSION >= 20708
833	REGISTER_LONG_CONSTANT("LIBXML_HTML_NODEFDTD",	HTML_PARSE_NODEFDTD,		CONST_CS | CONST_PERSISTENT);
834#endif
835
836	/* Error levels */
837	REGISTER_LONG_CONSTANT("LIBXML_ERR_NONE",		XML_ERR_NONE,		CONST_CS | CONST_PERSISTENT);
838	REGISTER_LONG_CONSTANT("LIBXML_ERR_WARNING",	XML_ERR_WARNING,	CONST_CS | CONST_PERSISTENT);
839	REGISTER_LONG_CONSTANT("LIBXML_ERR_ERROR",		XML_ERR_ERROR,		CONST_CS | CONST_PERSISTENT);
840	REGISTER_LONG_CONSTANT("LIBXML_ERR_FATAL",		XML_ERR_FATAL,		CONST_CS | CONST_PERSISTENT);
841
842	INIT_CLASS_ENTRY(ce, "LibXMLError", NULL);
843	libxmlerror_class_entry = zend_register_internal_class(&ce);
844
845	if (sapi_module.name) {
846		static const char * const supported_sapis[] = {
847			"cgi-fcgi",
848			"fpm-fcgi",
849			"litespeed",
850			NULL
851		};
852		const char * const *sapi_name;
853
854		for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
855			if (strcmp(sapi_module.name, *sapi_name) == 0) {
856				_php_libxml_per_request_initialization = 0;
857				break;
858			}
859		}
860	}
861
862	if (!_php_libxml_per_request_initialization) {
863		/* report errors via handler rather than stderr */
864		xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
865		xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
866		xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
867	}
868
869	return SUCCESS;
870}
871
872
873static PHP_RINIT_FUNCTION(libxml)
874{
875	if (_php_libxml_per_request_initialization) {
876		/* report errors via handler rather than stderr */
877		xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
878		xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
879		xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
880
881		/* Enable the entity loader by default. This ensures that
882		 * other threads/requests that might have disabled the loader
883		 * do not affect the current request.
884		 */
885		LIBXML(entity_loader_disabled) = 0;
886	}
887	return SUCCESS;
888}
889
890static PHP_RSHUTDOWN_FUNCTION(libxml)
891{
892	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
893
894	return SUCCESS;
895}
896
897static PHP_MSHUTDOWN_FUNCTION(libxml)
898{
899	if (!_php_libxml_per_request_initialization) {
900		xmlSetGenericErrorFunc(NULL, NULL);
901
902		xmlParserInputBufferCreateFilenameDefault(NULL);
903		xmlOutputBufferCreateFilenameDefault(NULL);
904	}
905	php_libxml_shutdown();
906
907	return SUCCESS;
908}
909
910static int php_libxml_post_deactivate(void)
911{
912	/* reset libxml generic error handling */
913	if (_php_libxml_per_request_initialization) {
914		xmlSetGenericErrorFunc(NULL, NULL);
915
916		xmlParserInputBufferCreateFilenameDefault(NULL);
917		xmlOutputBufferCreateFilenameDefault(NULL);
918	}
919	xmlSetStructuredErrorFunc(NULL, NULL);
920
921	/* the steam_context resource will be released by resource list destructor */
922	ZVAL_UNDEF(&LIBXML(stream_context));
923	smart_str_free(&LIBXML(error_buffer));
924	if (LIBXML(error_list)) {
925		zend_llist_destroy(LIBXML(error_list));
926		efree(LIBXML(error_list));
927		LIBXML(error_list) = NULL;
928	}
929	xmlResetLastError();
930
931	return SUCCESS;
932}
933
934
935static PHP_MINFO_FUNCTION(libxml)
936{
937	php_info_print_table_start();
938	php_info_print_table_row(2, "libXML support", "active");
939	php_info_print_table_row(2, "libXML Compiled Version", LIBXML_DOTTED_VERSION);
940	php_info_print_table_row(2, "libXML Loaded Version", (char *)xmlParserVersion);
941	php_info_print_table_row(2, "libXML streams", "enabled");
942	php_info_print_table_end();
943}
944/* }}} */
945
946/* {{{ proto void libxml_set_streams_context(resource streams_context)
947   Set the streams context for the next libxml document load or write */
948static PHP_FUNCTION(libxml_set_streams_context)
949{
950	zval *arg;
951
952	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg) == FAILURE) {
953		return;
954	}
955	if (!Z_ISUNDEF(LIBXML(stream_context))) {
956		zval_ptr_dtor(&LIBXML(stream_context));
957		ZVAL_UNDEF(&LIBXML(stream_context));
958	}
959	ZVAL_COPY(&LIBXML(stream_context), arg);
960}
961/* }}} */
962
963/* {{{ proto bool libxml_use_internal_errors([boolean use_errors])
964   Disable libxml errors and allow user to fetch error information as needed */
965static PHP_FUNCTION(libxml_use_internal_errors)
966{
967	xmlStructuredErrorFunc current_handler;
968	zend_bool use_errors=0, retval;
969
970	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &use_errors) == FAILURE) {
971		return;
972	}
973
974	current_handler = xmlStructuredError;
975	if (current_handler && current_handler == php_libxml_structured_error_handler) {
976		retval = 1;
977	} else {
978		retval = 0;
979	}
980
981	if (ZEND_NUM_ARGS() == 0) {
982		RETURN_BOOL(retval);
983	}
984
985	if (use_errors == 0) {
986		xmlSetStructuredErrorFunc(NULL, NULL);
987		if (LIBXML(error_list)) {
988			zend_llist_destroy(LIBXML(error_list));
989			efree(LIBXML(error_list));
990			LIBXML(error_list) = NULL;
991		}
992	} else {
993		xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
994		if (LIBXML(error_list) == NULL) {
995			LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
996			zend_llist_init(LIBXML(error_list), sizeof(xmlError), (llist_dtor_func_t) _php_libxml_free_error, 0);
997		}
998	}
999	RETURN_BOOL(retval);
1000}
1001/* }}} */
1002
1003/* {{{ proto object libxml_get_last_error()
1004   Retrieve last error from libxml */
1005static PHP_FUNCTION(libxml_get_last_error)
1006{
1007	xmlErrorPtr error;
1008
1009	error = xmlGetLastError();
1010
1011	if (error) {
1012		object_init_ex(return_value, libxmlerror_class_entry);
1013		add_property_long(return_value, "level", error->level);
1014		add_property_long(return_value, "code", error->code);
1015		add_property_long(return_value, "column", error->int2);
1016		if (error->message) {
1017			add_property_string(return_value, "message", error->message);
1018		} else {
1019			add_property_stringl(return_value, "message", "", 0);
1020		}
1021		if (error->file) {
1022			add_property_string(return_value, "file", error->file);
1023		} else {
1024			add_property_stringl(return_value, "file", "", 0);
1025		}
1026		add_property_long(return_value, "line", error->line);
1027	} else {
1028		RETURN_FALSE;
1029	}
1030}
1031/* }}} */
1032
1033/* {{{ proto object libxml_get_errors()
1034   Retrieve array of errors */
1035static PHP_FUNCTION(libxml_get_errors)
1036{
1037
1038	xmlErrorPtr error;
1039
1040	if (array_init(return_value) == FAILURE) {
1041		RETURN_FALSE;
1042	}
1043
1044	if (LIBXML(error_list)) {
1045
1046		error = zend_llist_get_first(LIBXML(error_list));
1047
1048		while (error != NULL) {
1049			zval z_error;
1050
1051			object_init_ex(&z_error, libxmlerror_class_entry);
1052			add_property_long_ex(&z_error, "level", sizeof("level") - 1, error->level);
1053			add_property_long_ex(&z_error, "code", sizeof("code") - 1, error->code);
1054			add_property_long_ex(&z_error, "column", sizeof("column") - 1, error->int2 );
1055			if (error->message) {
1056				add_property_string_ex(&z_error, "message", sizeof("message") - 1, error->message);
1057			} else {
1058				add_property_stringl_ex(&z_error, "message", sizeof("message") - 1, "", 0);
1059			}
1060			if (error->file) {
1061				add_property_string_ex(&z_error, "file", sizeof("file") - 1, error->file);
1062			} else {
1063				add_property_stringl_ex(&z_error, "file", sizeof("file") - 1, "", 0);
1064			}
1065			add_property_long_ex(&z_error, "line", sizeof("line") - 1, error->line);
1066			add_next_index_zval(return_value, &z_error);
1067
1068			error = zend_llist_get_next(LIBXML(error_list));
1069		}
1070	}
1071}
1072/* }}} */
1073
1074/* {{{ proto void libxml_clear_errors()
1075   Clear last error from libxml */
1076static PHP_FUNCTION(libxml_clear_errors)
1077{
1078	xmlResetLastError();
1079	if (LIBXML(error_list)) {
1080		zend_llist_clean(LIBXML(error_list));
1081	}
1082}
1083/* }}} */
1084
1085PHP_LIBXML_API zend_bool php_libxml_disable_entity_loader(zend_bool disable) /* {{{ */
1086{
1087	zend_bool old = LIBXML(entity_loader_disabled);
1088
1089	LIBXML(entity_loader_disabled) = disable;
1090	return old;
1091} /* }}} */
1092
1093/* {{{ proto bool libxml_disable_entity_loader([boolean disable])
1094   Disable/Enable ability to load external entities */
1095static PHP_FUNCTION(libxml_disable_entity_loader)
1096{
1097	zend_bool disable = 1;
1098
1099	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &disable) == FAILURE) {
1100		return;
1101	}
1102
1103	RETURN_BOOL(php_libxml_disable_entity_loader(disable));
1104}
1105/* }}} */
1106
1107/* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
1108   Changes the default external entity loader */
1109static PHP_FUNCTION(libxml_set_external_entity_loader)
1110{
1111	zend_fcall_info			fci;
1112	zend_fcall_info_cache	fcc;
1113	if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!", &fci, &fcc)
1114			== FAILURE) {
1115		return;
1116	}
1117
1118	_php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
1119
1120	if (fci.size > 0) { /* argument not null */
1121		LIBXML(entity_loader).fci = fci;
1122		Z_ADDREF(fci.function_name);
1123		if (fci.object != NULL) {
1124			ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object);
1125			Z_ADDREF(LIBXML(entity_loader).object);
1126		}
1127		LIBXML(entity_loader).fcc = fcc;
1128	}
1129
1130	RETURN_TRUE;
1131}
1132/* }}} */
1133
1134/* {{{ Common functions shared by extensions */
1135int php_libxml_xmlCheckUTF8(const unsigned char *s)
1136{
1137	int i;
1138	unsigned char c;
1139
1140	for (i = 0; (c = s[i++]);) {
1141		if ((c & 0x80) == 0) {
1142		} else if ((c & 0xe0) == 0xc0) {
1143			if ((s[i++] & 0xc0) != 0x80) {
1144				return 0;
1145			}
1146		} else if ((c & 0xf0) == 0xe0) {
1147			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1148				return 0;
1149			}
1150		} else if ((c & 0xf8) == 0xf0) {
1151			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1152				return 0;
1153			}
1154		} else {
1155			return 0;
1156		}
1157	}
1158	return 1;
1159}
1160
1161zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
1162{
1163	php_libxml_func_handler export_hnd;
1164
1165	/* Initialize in case this module hasn't been loaded yet */
1166	php_libxml_initialize();
1167	export_hnd.export_func = export_function;
1168
1169	return zend_hash_add_mem(&php_libxml_exports, ce->name, &export_hnd, sizeof(export_hnd));
1170}
1171
1172PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object)
1173{
1174	zend_class_entry *ce = NULL;
1175	xmlNodePtr node = NULL;
1176	php_libxml_func_handler *export_hnd;
1177
1178	if (Z_TYPE_P(object) == IS_OBJECT) {
1179		ce = Z_OBJCE_P(object);
1180		while (ce->parent != NULL) {
1181			ce = ce->parent;
1182		}
1183		if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) {
1184			node = export_hnd->export_func(object);
1185		}
1186	}
1187	return node;
1188}
1189
1190PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data)
1191{
1192	int ret_refcount = -1;
1193
1194	if (object != NULL && node != NULL) {
1195		if (object->node != NULL) {
1196			if (object->node->node == node) {
1197				return object->node->refcount;
1198			} else {
1199				php_libxml_decrement_node_ptr(object);
1200			}
1201		}
1202		if (node->_private != NULL) {
1203			object->node = node->_private;
1204			ret_refcount = ++object->node->refcount;
1205			/* Only dom uses _private */
1206			if (object->node->_private == NULL) {
1207				object->node->_private = private_data;
1208			}
1209		} else {
1210			ret_refcount = 1;
1211			object->node = emalloc(sizeof(php_libxml_node_ptr));
1212			object->node->node = node;
1213			object->node->refcount = 1;
1214			object->node->_private = private_data;
1215			node->_private = object->node;
1216		}
1217	}
1218
1219	return ret_refcount;
1220}
1221
1222PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object)
1223{
1224	int ret_refcount = -1;
1225	php_libxml_node_ptr *obj_node;
1226
1227	if (object != NULL && object->node != NULL) {
1228		obj_node = (php_libxml_node_ptr *) object->node;
1229		ret_refcount = --obj_node->refcount;
1230		if (ret_refcount == 0) {
1231			if (obj_node->node != NULL) {
1232				obj_node->node->_private = NULL;
1233			}
1234			efree(obj_node);
1235		}
1236		object->node = NULL;
1237	}
1238
1239	return ret_refcount;
1240}
1241
1242PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp)
1243{
1244	int ret_refcount = -1;
1245
1246	if (object->document != NULL) {
1247		object->document->refcount++;
1248		ret_refcount = object->document->refcount;
1249	} else if (docp != NULL) {
1250		ret_refcount = 1;
1251		object->document = emalloc(sizeof(php_libxml_ref_obj));
1252		object->document->ptr = docp;
1253		object->document->refcount = ret_refcount;
1254		object->document->doc_props = NULL;
1255	}
1256
1257	return ret_refcount;
1258}
1259
1260PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object)
1261{
1262	int ret_refcount = -1;
1263
1264	if (object != NULL && object->document != NULL) {
1265		ret_refcount = --object->document->refcount;
1266		if (ret_refcount == 0) {
1267			if (object->document->ptr != NULL) {
1268				xmlFreeDoc((xmlDoc *) object->document->ptr);
1269			}
1270			if (object->document->doc_props != NULL) {
1271				if (object->document->doc_props->classmap) {
1272					zend_hash_destroy(object->document->doc_props->classmap);
1273					FREE_HASHTABLE(object->document->doc_props->classmap);
1274				}
1275				efree(object->document->doc_props);
1276			}
1277			efree(object->document);
1278			object->document = NULL;
1279		}
1280	}
1281
1282	return ret_refcount;
1283}
1284
1285PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node)
1286{
1287	if (!node) {
1288		return;
1289	}
1290
1291	switch (node->type) {
1292		case XML_DOCUMENT_NODE:
1293		case XML_HTML_DOCUMENT_NODE:
1294			break;
1295		default:
1296			if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1297				php_libxml_node_free_list((xmlNodePtr) node->children);
1298				switch (node->type) {
1299					/* Skip property freeing for the following types */
1300					case XML_ATTRIBUTE_DECL:
1301					case XML_DTD_NODE:
1302					case XML_DOCUMENT_TYPE_NODE:
1303					case XML_ENTITY_DECL:
1304					case XML_ATTRIBUTE_NODE:
1305					case XML_NAMESPACE_DECL:
1306					case XML_TEXT_NODE:
1307						break;
1308					default:
1309						php_libxml_node_free_list((xmlNodePtr) node->properties);
1310				}
1311				if (php_libxml_unregister_node(node) == 0) {
1312					node->doc = NULL;
1313				}
1314				php_libxml_node_free(node);
1315			} else {
1316				php_libxml_unregister_node(node);
1317			}
1318	}
1319}
1320
1321PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object)
1322{
1323	int ret_refcount = -1;
1324	xmlNodePtr nodep;
1325	php_libxml_node_ptr *obj_node;
1326
1327	if (object != NULL && object->node != NULL) {
1328		obj_node = (php_libxml_node_ptr *) object->node;
1329		nodep = object->node->node;
1330		ret_refcount = php_libxml_decrement_node_ptr(object);
1331		if (ret_refcount == 0) {
1332			php_libxml_node_free_resource(nodep);
1333		} else {
1334			if (obj_node && object == obj_node->_private) {
1335				obj_node->_private = NULL;
1336			}
1337		}
1338	}
1339	if (object != NULL && object->document != NULL) {
1340		/* Safe to call as if the resource were freed then doc pointer is NULL */
1341		php_libxml_decrement_doc_ref(object);
1342	}
1343}
1344/* }}} */
1345
1346#if defined(PHP_WIN32) && defined(COMPILE_DL_LIBXML)
1347PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1348{
1349	return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1350}
1351#endif
1352
1353#endif
1354
1355/*
1356 * Local variables:
1357 * tab-width: 4
1358 * c-basic-offset: 4
1359 * End:
1360 * vim600: sw=4 ts=4 fdm=marker
1361 * vim<600: sw=4 ts=4
1362 */
1363