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