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