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