xref: /PHP-5.3/ext/libxml/libxml.c (revision 8e76d040)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 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 
30 #define PHP_XML_INTERNAL
31 #include "zend_variables.h"
32 #include "ext/standard/php_string.h"
33 #include "ext/standard/info.h"
34 #include "ext/standard/file.h"
35 
36 #if HAVE_LIBXML
37 
38 #include <libxml/parser.h>
39 #include <libxml/parserInternals.h>
40 #include <libxml/tree.h>
41 #include <libxml/uri.h>
42 #include <libxml/xmlerror.h>
43 #include <libxml/xmlsave.h>
44 #ifdef LIBXML_SCHEMAS_ENABLED
45 #include <libxml/relaxng.h>
46 #endif
47 
48 #include "php_libxml.h"
49 
50 #define PHP_LIBXML_ERROR 0
51 #define PHP_LIBXML_CTX_ERROR 1
52 #define PHP_LIBXML_CTX_WARNING 2
53 
54 /* a true global for initialization */
55 static int _php_libxml_initialized = 0;
56 
57 typedef struct _php_libxml_func_handler {
58 	php_libxml_export_node export_func;
59 } php_libxml_func_handler;
60 
61 static HashTable php_libxml_exports;
62 
63 static ZEND_DECLARE_MODULE_GLOBALS(libxml)
64 static PHP_GINIT_FUNCTION(libxml);
65 
66 static PHP_FUNCTION(libxml_set_streams_context);
67 static PHP_FUNCTION(libxml_use_internal_errors);
68 static PHP_FUNCTION(libxml_get_last_error);
69 static PHP_FUNCTION(libxml_clear_errors);
70 static PHP_FUNCTION(libxml_get_errors);
71 static PHP_FUNCTION(libxml_disable_entity_loader);
72 
73 static zend_class_entry *libxmlerror_class_entry;
74 
75 /* {{{ dynamically loadable module stuff */
76 #ifdef COMPILE_DL_LIBXML
77 ZEND_GET_MODULE(libxml)
78 #endif /* COMPILE_DL_LIBXML */
79 /* }}} */
80 
81 /* {{{ function prototypes */
82 static PHP_MINIT_FUNCTION(libxml);
83 static PHP_RINIT_FUNCTION(libxml);
84 static PHP_MSHUTDOWN_FUNCTION(libxml);
85 static PHP_MINFO_FUNCTION(libxml);
86 static int php_libxml_post_deactivate();
87 
88 /* }}} */
89 
90 /* {{{ arginfo */
91 ZEND_BEGIN_ARG_INFO(arginfo_libxml_set_streams_context, 0)
92 	ZEND_ARG_INFO(0, context)
93 ZEND_END_ARG_INFO()
94 
95 ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_use_internal_errors, 0, 0, 0)
96 	ZEND_ARG_INFO(0, use_errors)
97 ZEND_END_ARG_INFO()
98 
99 ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_last_error, 0)
100 ZEND_END_ARG_INFO()
101 
102 ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_errors, 0)
103 ZEND_END_ARG_INFO()
104 
105 ZEND_BEGIN_ARG_INFO(arginfo_libxml_clear_errors, 0)
106 ZEND_END_ARG_INFO()
107 
108 ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_disable_entity_loader, 0, 0, 0)
109 	ZEND_ARG_INFO(0, disable)
110 ZEND_END_ARG_INFO()
111 
112 /* }}} */
113 
114 /* {{{ extension definition structures */
115 static const zend_function_entry libxml_functions[] = {
116 	PHP_FE(libxml_set_streams_context, arginfo_libxml_set_streams_context)
117 	PHP_FE(libxml_use_internal_errors, arginfo_libxml_use_internal_errors)
118 	PHP_FE(libxml_get_last_error, arginfo_libxml_get_last_error)
119 	PHP_FE(libxml_clear_errors, arginfo_libxml_clear_errors)
120 	PHP_FE(libxml_get_errors, arginfo_libxml_get_errors)
121 	PHP_FE(libxml_disable_entity_loader, arginfo_libxml_disable_entity_loader)
122 	PHP_FE_END
123 };
124 
125 zend_module_entry libxml_module_entry = {
126 	STANDARD_MODULE_HEADER,
127 	"libxml",                /* extension name */
128 	libxml_functions,        /* extension function list */
129 	PHP_MINIT(libxml),       /* extension-wide startup function */
130 	PHP_MSHUTDOWN(libxml),   /* extension-wide shutdown function */
131 	PHP_RINIT(libxml),       /* per-request startup function */
132 	NULL,                    /* per-request shutdown function */
133 	PHP_MINFO(libxml),       /* information function */
134 	NO_VERSION_YET,
135 	PHP_MODULE_GLOBALS(libxml), /* globals descriptor */
136 	PHP_GINIT(libxml),          /* globals ctor */
137 	NULL,                       /* globals dtor */
138 	php_libxml_post_deactivate, /* post deactivate */
139 	STANDARD_MODULE_PROPERTIES_EX
140 };
141 
142 /* }}} */
143 
144 /* {{{ internal functions for interoperability */
php_libxml_clear_object(php_libxml_node_object * object TSRMLS_DC)145 static int php_libxml_clear_object(php_libxml_node_object *object TSRMLS_DC)
146 {
147 	if (object->properties) {
148 		object->properties = NULL;
149 	}
150 	php_libxml_decrement_node_ptr(object TSRMLS_CC);
151 	return php_libxml_decrement_doc_ref(object TSRMLS_CC);
152 }
153 
php_libxml_unregister_node(xmlNodePtr nodep TSRMLS_DC)154 static int php_libxml_unregister_node(xmlNodePtr nodep TSRMLS_DC)
155 {
156 	php_libxml_node_object *wrapper;
157 
158 	php_libxml_node_ptr *nodeptr = nodep->_private;
159 
160 	if (nodeptr != NULL) {
161 		wrapper = nodeptr->_private;
162 		if (wrapper) {
163 			php_libxml_clear_object(wrapper TSRMLS_CC);
164 		} else {
165 			if (nodeptr->node != NULL && nodeptr->node->type != XML_DOCUMENT_NODE) {
166 				nodeptr->node->_private = NULL;
167 			}
168 			nodeptr->node = NULL;
169 		}
170 	}
171 
172 	return -1;
173 }
174 
php_libxml_node_free(xmlNodePtr node)175 static void php_libxml_node_free(xmlNodePtr node)
176 {
177 	if(node) {
178 		if (node->_private != NULL) {
179 			((php_libxml_node_ptr *) node->_private)->node = NULL;
180 		}
181 		switch (node->type) {
182 			case XML_ATTRIBUTE_NODE:
183 				xmlFreeProp((xmlAttrPtr) node);
184 				break;
185 			case XML_ENTITY_DECL:
186 			case XML_ELEMENT_DECL:
187 			case XML_ATTRIBUTE_DECL:
188 				break;
189 			case XML_NOTATION_NODE:
190 				/* These require special handling */
191 				if (node->name != NULL) {
192 					xmlFree((char *) node->name);
193 				}
194 				if (((xmlEntityPtr) node)->ExternalID != NULL) {
195 					xmlFree((char *) ((xmlEntityPtr) node)->ExternalID);
196 				}
197 				if (((xmlEntityPtr) node)->SystemID != NULL) {
198 					xmlFree((char *) ((xmlEntityPtr) node)->SystemID);
199 				}
200 				xmlFree(node);
201 				break;
202 			case XML_NAMESPACE_DECL:
203 				if (node->ns) {
204 					xmlFreeNs(node->ns);
205 					node->ns = NULL;
206 				}
207 				node->type = XML_ELEMENT_NODE;
208 			default:
209 				xmlFreeNode(node);
210 		}
211 	}
212 }
213 
php_libxml_node_free_list(xmlNodePtr node TSRMLS_DC)214 static void php_libxml_node_free_list(xmlNodePtr node TSRMLS_DC)
215 {
216 	xmlNodePtr curnode;
217 
218 	if (node != NULL) {
219 		curnode = node;
220 		while (curnode != NULL) {
221 			node = curnode;
222 			switch (node->type) {
223 				/* Skip property freeing for the following types */
224 				case XML_NOTATION_NODE:
225 				case XML_ENTITY_DECL:
226 					break;
227 				case XML_ENTITY_REF_NODE:
228 					php_libxml_node_free_list((xmlNodePtr) node->properties TSRMLS_CC);
229 					break;
230 				case XML_ATTRIBUTE_NODE:
231 						if ((node->doc != NULL) && (((xmlAttrPtr) node)->atype == XML_ATTRIBUTE_ID)) {
232 							xmlRemoveID(node->doc, (xmlAttrPtr) node);
233 						}
234 				case XML_ATTRIBUTE_DECL:
235 				case XML_DTD_NODE:
236 				case XML_DOCUMENT_TYPE_NODE:
237 				case XML_NAMESPACE_DECL:
238 				case XML_TEXT_NODE:
239 					php_libxml_node_free_list(node->children TSRMLS_CC);
240 					break;
241 				default:
242 					php_libxml_node_free_list(node->children TSRMLS_CC);
243 					php_libxml_node_free_list((xmlNodePtr) node->properties TSRMLS_CC);
244 			}
245 
246 			curnode = node->next;
247 			xmlUnlinkNode(node);
248 			if (php_libxml_unregister_node(node TSRMLS_CC) == 0) {
249 				node->doc = NULL;
250 			}
251 			php_libxml_node_free(node);
252 		}
253 	}
254 }
255 
256 /* }}} */
257 
258 /* {{{ startup, shutdown and info functions */
PHP_GINIT_FUNCTION(libxml)259 static PHP_GINIT_FUNCTION(libxml)
260 {
261 	libxml_globals->stream_context = NULL;
262 	libxml_globals->error_buffer.c = NULL;
263 	libxml_globals->error_list = NULL;
264 	libxml_globals->entity_loader_disabled = 0;
265 }
266 
267 /* Channel libxml file io layer through the PHP streams subsystem.
268  * This allows use of ftps:// and https:// urls */
269 
php_libxml_streams_IO_open_wrapper(const char * filename,const char * mode,const int read_only)270 static void *php_libxml_streams_IO_open_wrapper(const char *filename, const char *mode, const int read_only)
271 {
272 	php_stream_statbuf ssbuf;
273 	php_stream_context *context = NULL;
274 	php_stream_wrapper *wrapper = NULL;
275 	char *resolved_path, *path_to_open = NULL;
276 	void *ret_val = NULL;
277 	int isescaped=0;
278 	xmlURI *uri;
279 
280 	TSRMLS_FETCH();
281 
282 	uri = xmlParseURI((xmlChar *)filename);
283 	if (uri && (uri->scheme == NULL || (xmlStrncmp(uri->scheme, "file", 4) == 0))) {
284 		resolved_path = xmlURIUnescapeString(filename, 0, NULL);
285 		isescaped = 1;
286 	} else {
287 		resolved_path = (char *)filename;
288 	}
289 
290 	if (uri) {
291 		xmlFreeURI(uri);
292 	}
293 
294 	if (resolved_path == NULL) {
295 		return NULL;
296 	}
297 
298 	/* logic copied from _php_stream_stat, but we only want to fail
299 	   if the wrapper supports stat, otherwise, figure it out from
300 	   the open.  This logic is only to support hiding warnings
301 	   that the streams layer puts out at times, but for libxml we
302 	   may try to open files that don't exist, but it is not a failure
303 	   in xml processing (eg. DTD files)  */
304 	wrapper = php_stream_locate_url_wrapper(resolved_path, &path_to_open, ENFORCE_SAFE_MODE TSRMLS_CC);
305 	if (wrapper && read_only && wrapper->wops->url_stat) {
306 		if (wrapper->wops->url_stat(wrapper, path_to_open, PHP_STREAM_URL_STAT_QUIET, &ssbuf, NULL TSRMLS_CC) == -1) {
307 			if (isescaped) {
308 				xmlFree(resolved_path);
309 			}
310 			return NULL;
311 		}
312 	}
313 
314 	context = php_stream_context_from_zval(LIBXML(stream_context), 0);
315 
316 	ret_val = php_stream_open_wrapper_ex(path_to_open, (char *)mode, ENFORCE_SAFE_MODE|REPORT_ERRORS, NULL, context);
317 	if (isescaped) {
318 		xmlFree(resolved_path);
319 	}
320 	return ret_val;
321 }
322 
php_libxml_streams_IO_open_read_wrapper(const char * filename)323 static void *php_libxml_streams_IO_open_read_wrapper(const char *filename)
324 {
325 	return php_libxml_streams_IO_open_wrapper(filename, "rb", 1);
326 }
327 
php_libxml_streams_IO_open_write_wrapper(const char * filename)328 static void *php_libxml_streams_IO_open_write_wrapper(const char *filename)
329 {
330 	return php_libxml_streams_IO_open_wrapper(filename, "wb", 0);
331 }
332 
php_libxml_streams_IO_read(void * context,char * buffer,int len)333 static int php_libxml_streams_IO_read(void *context, char *buffer, int len)
334 {
335 	TSRMLS_FETCH();
336 	return php_stream_read((php_stream*)context, buffer, len);
337 }
338 
php_libxml_streams_IO_write(void * context,const char * buffer,int len)339 static int php_libxml_streams_IO_write(void *context, const char *buffer, int len)
340 {
341 	TSRMLS_FETCH();
342 	return php_stream_write((php_stream*)context, buffer, len);
343 }
344 
php_libxml_streams_IO_close(void * context)345 static int php_libxml_streams_IO_close(void *context)
346 {
347 	TSRMLS_FETCH();
348 	return php_stream_close((php_stream*)context);
349 }
350 
351 static xmlParserInputBufferPtr
php_libxml_input_buffer_create_filename(const char * URI,xmlCharEncoding enc)352 php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc)
353 {
354 	xmlParserInputBufferPtr ret;
355 	void *context = NULL;
356 	TSRMLS_FETCH();
357 
358 	if (LIBXML(entity_loader_disabled)) {
359 		return NULL;
360 	}
361 
362 	if (URI == NULL)
363 		return(NULL);
364 
365 	context = php_libxml_streams_IO_open_read_wrapper(URI);
366 
367 	if (context == NULL) {
368 		return(NULL);
369 	}
370 
371 	/* Allocate the Input buffer front-end. */
372 	ret = xmlAllocParserInputBuffer(enc);
373 	if (ret != NULL) {
374 		ret->context = context;
375 		ret->readcallback = php_libxml_streams_IO_read;
376 		ret->closecallback = php_libxml_streams_IO_close;
377 	} else
378 		php_libxml_streams_IO_close(context);
379 
380 	return(ret);
381 }
382 
383 static xmlOutputBufferPtr
php_libxml_output_buffer_create_filename(const char * URI,xmlCharEncodingHandlerPtr encoder,int compression ATTRIBUTE_UNUSED)384 php_libxml_output_buffer_create_filename(const char *URI,
385                               xmlCharEncodingHandlerPtr encoder,
386                               int compression ATTRIBUTE_UNUSED)
387 {
388 	xmlOutputBufferPtr ret;
389 	xmlURIPtr puri;
390 	void *context = NULL;
391 	char *unescaped = NULL;
392 
393 	if (URI == NULL)
394 		return(NULL);
395 
396 	puri = xmlParseURI(URI);
397 	if (puri != NULL) {
398 		if (puri->scheme != NULL)
399 			unescaped = xmlURIUnescapeString(URI, 0, NULL);
400 		xmlFreeURI(puri);
401 	}
402 
403 	if (unescaped != NULL) {
404 		context = php_libxml_streams_IO_open_write_wrapper(unescaped);
405 		xmlFree(unescaped);
406 	}
407 
408 	/* try with a non-escaped URI this may be a strange filename */
409 	if (context == NULL) {
410 		context = php_libxml_streams_IO_open_write_wrapper(URI);
411 	}
412 
413 	if (context == NULL) {
414 		return(NULL);
415 	}
416 
417 	/* Allocate the Output buffer front-end. */
418 	ret = xmlAllocOutputBuffer(encoder);
419 	if (ret != NULL) {
420 		ret->context = context;
421 		ret->writecallback = php_libxml_streams_IO_write;
422 		ret->closecallback = php_libxml_streams_IO_close;
423 	}
424 
425 	return(ret);
426 }
427 
_php_libxml_free_error(xmlErrorPtr error)428 static int _php_libxml_free_error(xmlErrorPtr error)
429 {
430 	/* This will free the libxml alloc'd memory */
431 	xmlResetError(error);
432 	return 1;
433 }
434 
_php_list_set_error_structure(xmlErrorPtr error,const char * msg)435 static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg)
436 {
437 	xmlError error_copy;
438 	int ret;
439 
440 	TSRMLS_FETCH();
441 
442 	memset(&error_copy, 0, sizeof(xmlError));
443 
444 	if (error) {
445 		ret = xmlCopyError(error, &error_copy);
446 	} else {
447 		error_copy.domain = 0;
448 		error_copy.code = XML_ERR_INTERNAL_ERROR;
449 		error_copy.level = XML_ERR_ERROR;
450 		error_copy.line = 0;
451 		error_copy.node = NULL;
452 		error_copy.int1 = 0;
453 		error_copy.int2 = 0;
454 		error_copy.ctxt = NULL;
455 		error_copy.message = xmlStrdup(msg);
456 		error_copy.file = NULL;
457 		error_copy.str1 = NULL;
458 		error_copy.str2 = NULL;
459 		error_copy.str3 = NULL;
460 		ret = 0;
461 	}
462 
463 	if (ret == 0) {
464 		zend_llist_add_element(LIBXML(error_list), &error_copy);
465 	}
466 }
467 
php_libxml_ctx_error_level(int level,void * ctx,const char * msg TSRMLS_DC)468 static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg TSRMLS_DC)
469 {
470 	xmlParserCtxtPtr parser;
471 
472 	parser = (xmlParserCtxtPtr) ctx;
473 
474 	if (parser != NULL && parser->input != NULL) {
475 		if (parser->input->filename) {
476 			php_error_docref(NULL TSRMLS_CC, level, "%s in %s, line: %d", msg, parser->input->filename, parser->input->line);
477 		} else {
478 			php_error_docref(NULL TSRMLS_CC, level, "%s in Entity, line: %d", msg, parser->input->line);
479 		}
480 	}
481 }
482 
php_libxml_issue_error(int level,const char * msg TSRMLS_DC)483 void php_libxml_issue_error(int level, const char *msg TSRMLS_DC)
484 {
485 	if (LIBXML(error_list)) {
486 		_php_list_set_error_structure(NULL, msg);
487 	} else {
488 		php_error_docref(NULL TSRMLS_CC, level, "%s", msg);
489 	}
490 }
491 
php_libxml_internal_error_handler(int error_type,void * ctx,const char ** msg,va_list ap)492 static void php_libxml_internal_error_handler(int error_type, void *ctx, const char **msg, va_list ap)
493 {
494 	char *buf;
495 	int len, len_iter, output = 0;
496 
497 	TSRMLS_FETCH();
498 
499 	len = vspprintf(&buf, 0, *msg, ap);
500 	len_iter = len;
501 
502 	/* remove any trailing \n */
503 	while (len_iter && buf[--len_iter] == '\n') {
504 		buf[len_iter] = '\0';
505 		output = 1;
506 	}
507 
508 	smart_str_appendl(&LIBXML(error_buffer), buf, len);
509 
510 	efree(buf);
511 
512 	if (output == 1) {
513 		if (LIBXML(error_list)) {
514 			_php_list_set_error_structure(NULL, LIBXML(error_buffer).c);
515 		} else {
516 			switch (error_type) {
517 				case PHP_LIBXML_CTX_ERROR:
518 					php_libxml_ctx_error_level(E_WARNING, ctx, LIBXML(error_buffer).c TSRMLS_CC);
519 					break;
520 				case PHP_LIBXML_CTX_WARNING:
521 					php_libxml_ctx_error_level(E_NOTICE, ctx, LIBXML(error_buffer).c TSRMLS_CC);
522 					break;
523 				default:
524 					php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", LIBXML(error_buffer).c);
525 			}
526 		}
527 		smart_str_free(&LIBXML(error_buffer));
528 	}
529 }
530 
php_libxml_ctx_error(void * ctx,const char * msg,...)531 PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...)
532 {
533 	va_list args;
534 	va_start(args, msg);
535 	php_libxml_internal_error_handler(PHP_LIBXML_CTX_ERROR, ctx, &msg, args);
536 	va_end(args);
537 }
538 
php_libxml_ctx_warning(void * ctx,const char * msg,...)539 PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...)
540 {
541 	va_list args;
542 	va_start(args, msg);
543 	php_libxml_internal_error_handler(PHP_LIBXML_CTX_WARNING, ctx, &msg, args);
544 	va_end(args);
545 }
546 
php_libxml_structured_error_handler(void * userData,xmlErrorPtr error)547 PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error)
548 {
549 	_php_list_set_error_structure(error, NULL);
550 
551 	return;
552 }
553 
php_libxml_error_handler(void * ctx,const char * msg,...)554 PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...)
555 {
556 	va_list args;
557 	va_start(args, msg);
558 	php_libxml_internal_error_handler(PHP_LIBXML_ERROR, ctx, &msg, args);
559 	va_end(args);
560 }
561 
562 
php_libxml_initialize(void)563 PHP_LIBXML_API void php_libxml_initialize(void)
564 {
565 	if (!_php_libxml_initialized) {
566 		/* we should be the only one's to ever init!! */
567 		xmlInitParser();
568 
569 		zend_hash_init(&php_libxml_exports, 0, NULL, NULL, 1);
570 
571 		_php_libxml_initialized = 1;
572 	}
573 }
574 
php_libxml_shutdown(void)575 PHP_LIBXML_API void php_libxml_shutdown(void)
576 {
577 	if (_php_libxml_initialized) {
578 #if defined(LIBXML_SCHEMAS_ENABLED)
579 		xmlRelaxNGCleanupTypes();
580 #endif
581 		xmlCleanupParser();
582 		zend_hash_destroy(&php_libxml_exports);
583 		_php_libxml_initialized = 0;
584 	}
585 }
586 
php_libxml_switch_context(zval * context TSRMLS_DC)587 PHP_LIBXML_API zval *php_libxml_switch_context(zval *context TSRMLS_DC)
588 {
589 	zval *oldcontext;
590 
591 	oldcontext = LIBXML(stream_context);
592 	LIBXML(stream_context) = context;
593 	return oldcontext;
594 
595 }
596 
PHP_MINIT_FUNCTION(libxml)597 static PHP_MINIT_FUNCTION(libxml)
598 {
599 	zend_class_entry ce;
600 
601 	php_libxml_initialize();
602 
603 	REGISTER_LONG_CONSTANT("LIBXML_VERSION",			LIBXML_VERSION,			CONST_CS | CONST_PERSISTENT);
604 	REGISTER_STRING_CONSTANT("LIBXML_DOTTED_VERSION",	LIBXML_DOTTED_VERSION,	CONST_CS | CONST_PERSISTENT);
605 	REGISTER_STRING_CONSTANT("LIBXML_LOADED_VERSION",	(char *)xmlParserVersion,		CONST_CS | CONST_PERSISTENT);
606 
607 	/* For use with loading xml */
608 	REGISTER_LONG_CONSTANT("LIBXML_NOENT",		XML_PARSE_NOENT,		CONST_CS | CONST_PERSISTENT);
609 	REGISTER_LONG_CONSTANT("LIBXML_DTDLOAD",	XML_PARSE_DTDLOAD,		CONST_CS | CONST_PERSISTENT);
610 	REGISTER_LONG_CONSTANT("LIBXML_DTDATTR",	XML_PARSE_DTDATTR,		CONST_CS | CONST_PERSISTENT);
611 	REGISTER_LONG_CONSTANT("LIBXML_DTDVALID",	XML_PARSE_DTDVALID,		CONST_CS | CONST_PERSISTENT);
612 	REGISTER_LONG_CONSTANT("LIBXML_NOERROR",	XML_PARSE_NOERROR,		CONST_CS | CONST_PERSISTENT);
613 	REGISTER_LONG_CONSTANT("LIBXML_NOWARNING",	XML_PARSE_NOWARNING,	CONST_CS | CONST_PERSISTENT);
614 	REGISTER_LONG_CONSTANT("LIBXML_NOBLANKS",	XML_PARSE_NOBLANKS,		CONST_CS | CONST_PERSISTENT);
615 	REGISTER_LONG_CONSTANT("LIBXML_XINCLUDE",	XML_PARSE_XINCLUDE,		CONST_CS | CONST_PERSISTENT);
616 	REGISTER_LONG_CONSTANT("LIBXML_NSCLEAN",	XML_PARSE_NSCLEAN,		CONST_CS | CONST_PERSISTENT);
617 	REGISTER_LONG_CONSTANT("LIBXML_NOCDATA",	XML_PARSE_NOCDATA,		CONST_CS | CONST_PERSISTENT);
618 	REGISTER_LONG_CONSTANT("LIBXML_NONET",		XML_PARSE_NONET,		CONST_CS | CONST_PERSISTENT);
619 #if LIBXML_VERSION >= 20621
620 	REGISTER_LONG_CONSTANT("LIBXML_COMPACT",	XML_PARSE_COMPACT,		CONST_CS | CONST_PERSISTENT);
621 	REGISTER_LONG_CONSTANT("LIBXML_NOXMLDECL",	XML_SAVE_NO_DECL,		CONST_CS | CONST_PERSISTENT);
622 #endif
623 #if LIBXML_VERSION >= 20703
624 	REGISTER_LONG_CONSTANT("LIBXML_PARSEHUGE",	XML_PARSE_HUGE,			CONST_CS | CONST_PERSISTENT);
625 #endif
626 	REGISTER_LONG_CONSTANT("LIBXML_NOEMPTYTAG",	LIBXML_SAVE_NOEMPTYTAG,	CONST_CS | CONST_PERSISTENT);
627 
628 	/* Error levels */
629 	REGISTER_LONG_CONSTANT("LIBXML_ERR_NONE",		XML_ERR_NONE,		CONST_CS | CONST_PERSISTENT);
630 	REGISTER_LONG_CONSTANT("LIBXML_ERR_WARNING",	XML_ERR_WARNING,	CONST_CS | CONST_PERSISTENT);
631 	REGISTER_LONG_CONSTANT("LIBXML_ERR_ERROR",		XML_ERR_ERROR,		CONST_CS | CONST_PERSISTENT);
632 	REGISTER_LONG_CONSTANT("LIBXML_ERR_FATAL",		XML_ERR_FATAL,		CONST_CS | CONST_PERSISTENT);
633 
634 	INIT_CLASS_ENTRY(ce, "LibXMLError", NULL);
635 	libxmlerror_class_entry = zend_register_internal_class(&ce TSRMLS_CC);
636 
637 	return SUCCESS;
638 }
639 
640 
PHP_RINIT_FUNCTION(libxml)641 static PHP_RINIT_FUNCTION(libxml)
642 {
643 	/* report errors via handler rather than stderr */
644 	xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
645 	xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
646 	xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
647 	return SUCCESS;
648 }
649 
650 
PHP_MSHUTDOWN_FUNCTION(libxml)651 static PHP_MSHUTDOWN_FUNCTION(libxml)
652 {
653 	php_libxml_shutdown();
654 
655 	return SUCCESS;
656 }
657 
php_libxml_post_deactivate()658 static int php_libxml_post_deactivate()
659 {
660 	TSRMLS_FETCH();
661 	/* reset libxml generic error handling */
662 	xmlSetGenericErrorFunc(NULL, NULL);
663 	xmlSetStructuredErrorFunc(NULL, NULL);
664 
665 	xmlParserInputBufferCreateFilenameDefault(NULL);
666 	xmlOutputBufferCreateFilenameDefault(NULL);
667 
668 	if (LIBXML(stream_context)) {
669 		/* the steam_context resource will be released by resource list destructor */
670 		efree(LIBXML(stream_context));
671 		LIBXML(stream_context) = NULL;
672 	}
673 	smart_str_free(&LIBXML(error_buffer));
674 	if (LIBXML(error_list)) {
675 		zend_llist_destroy(LIBXML(error_list));
676 		efree(LIBXML(error_list));
677 		LIBXML(error_list) = NULL;
678 	}
679 	xmlResetLastError();
680 
681 	return SUCCESS;
682 }
683 
684 
PHP_MINFO_FUNCTION(libxml)685 static PHP_MINFO_FUNCTION(libxml)
686 {
687 	php_info_print_table_start();
688 	php_info_print_table_row(2, "libXML support", "active");
689 	php_info_print_table_row(2, "libXML Compiled Version", LIBXML_DOTTED_VERSION);
690 	php_info_print_table_row(2, "libXML Loaded Version", (char *)xmlParserVersion);
691 	php_info_print_table_row(2, "libXML streams", "enabled");
692 	php_info_print_table_end();
693 }
694 /* }}} */
695 
696 /* {{{ proto void libxml_set_streams_context(resource streams_context)
697    Set the streams context for the next libxml document load or write */
PHP_FUNCTION(libxml_set_streams_context)698 static PHP_FUNCTION(libxml_set_streams_context)
699 {
700 	zval *arg;
701 
702 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &arg) == FAILURE) {
703 		return;
704 	}
705 	if (LIBXML(stream_context)) {
706 		zval_ptr_dtor(&LIBXML(stream_context));
707 		LIBXML(stream_context) = NULL;
708 	}
709 	Z_ADDREF_P(arg);
710 	LIBXML(stream_context) = arg;
711 }
712 /* }}} */
713 
714 /* {{{ proto bool libxml_use_internal_errors([boolean use_errors])
715    Disable libxml errors and allow user to fetch error information as needed */
PHP_FUNCTION(libxml_use_internal_errors)716 static PHP_FUNCTION(libxml_use_internal_errors)
717 {
718 	xmlStructuredErrorFunc current_handler;
719 	zend_bool use_errors=0, retval;
720 
721 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &use_errors) == FAILURE) {
722 		return;
723 	}
724 
725 	current_handler = xmlStructuredError;
726 	if (current_handler && current_handler == php_libxml_structured_error_handler) {
727 		retval = 1;
728 	} else {
729 		retval = 0;
730 	}
731 
732 	if (ZEND_NUM_ARGS() == 0) {
733 		RETURN_BOOL(retval);
734 	}
735 
736 	if (use_errors == 0) {
737 		xmlSetStructuredErrorFunc(NULL, NULL);
738 		if (LIBXML(error_list)) {
739 			zend_llist_destroy(LIBXML(error_list));
740 			efree(LIBXML(error_list));
741 			LIBXML(error_list) = NULL;
742 		}
743 	} else {
744 		xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
745 		if (LIBXML(error_list) == NULL) {
746 			LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
747 			zend_llist_init(LIBXML(error_list), sizeof(xmlError), (llist_dtor_func_t) _php_libxml_free_error, 0);
748 		}
749 	}
750 	RETURN_BOOL(retval);
751 }
752 /* }}} */
753 
754 /* {{{ proto object libxml_get_last_error()
755    Retrieve last error from libxml */
PHP_FUNCTION(libxml_get_last_error)756 static PHP_FUNCTION(libxml_get_last_error)
757 {
758 	xmlErrorPtr error;
759 
760 	error = xmlGetLastError();
761 
762 	if (error) {
763 		object_init_ex(return_value, libxmlerror_class_entry);
764 		add_property_long(return_value, "level", error->level);
765 		add_property_long(return_value, "code", error->code);
766 		add_property_long(return_value, "column", error->int2);
767 		if (error->message) {
768 			add_property_string(return_value, "message", error->message, 1);
769 		} else {
770 			add_property_stringl(return_value, "message", "", 0, 1);
771 		}
772 		if (error->file) {
773 			add_property_string(return_value, "file", error->file, 1);
774 		} else {
775 			add_property_stringl(return_value, "file", "", 0, 1);
776 		}
777 		add_property_long(return_value, "line", error->line);
778 	} else {
779 		RETURN_FALSE;
780 	}
781 }
782 /* }}} */
783 
784 /* {{{ proto object libxml_get_errors()
785    Retrieve array of errors */
PHP_FUNCTION(libxml_get_errors)786 static PHP_FUNCTION(libxml_get_errors)
787 {
788 
789 	xmlErrorPtr error;
790 
791 	if (array_init(return_value) == FAILURE) {
792 		RETURN_FALSE;
793 	}
794 
795 	if (LIBXML(error_list)) {
796 
797 		error = zend_llist_get_first(LIBXML(error_list));
798 
799 		while (error != NULL) {
800 			zval *z_error;
801 			MAKE_STD_ZVAL(z_error);
802 
803 			object_init_ex(z_error, libxmlerror_class_entry);
804 			add_property_long(z_error, "level", error->level);
805 			add_property_long(z_error, "code", error->code);
806 			add_property_long(z_error, "column", error->int2);
807 			if (error->message) {
808 				add_property_string(z_error, "message", error->message, 1);
809 			} else {
810 				add_property_stringl(z_error, "message", "", 0, 1);
811 			}
812 			if (error->file) {
813 				add_property_string(z_error, "file", error->file, 1);
814 			} else {
815 				add_property_stringl(z_error, "file", "", 0, 1);
816 			}
817 			add_property_long(z_error, "line", error->line);
818 			add_next_index_zval(return_value, z_error);
819 
820 			error = zend_llist_get_next(LIBXML(error_list));
821 		}
822 	}
823 }
824 /* }}} */
825 
826 /* {{{ proto void libxml_clear_errors()
827    Clear last error from libxml */
PHP_FUNCTION(libxml_clear_errors)828 static PHP_FUNCTION(libxml_clear_errors)
829 {
830 	xmlResetLastError();
831 	if (LIBXML(error_list)) {
832 		zend_llist_clean(LIBXML(error_list));
833 	}
834 }
835 /* }}} */
836 
php_libxml_disable_entity_loader(zend_bool disable TSRMLS_DC)837 PHP_LIBXML_API zend_bool php_libxml_disable_entity_loader(zend_bool disable TSRMLS_DC)
838 {
839 	zend_bool old = LIBXML(entity_loader_disabled);
840 
841 	LIBXML(entity_loader_disabled) = disable;
842 	return old;
843 }
844 
845 /* {{{ proto bool libxml_disable_entity_loader([boolean disable])
846    Disable/Enable ability to load external entities */
PHP_FUNCTION(libxml_disable_entity_loader)847 static PHP_FUNCTION(libxml_disable_entity_loader)
848 {
849 	zend_bool disable = 1;
850 
851 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &disable) == FAILURE) {
852 		return;
853 	}
854 
855 	RETURN_BOOL(php_libxml_disable_entity_loader(disable TSRMLS_CC));
856 }
857 /* }}} */
858 
859 /* {{{ Common functions shared by extensions */
php_libxml_xmlCheckUTF8(const unsigned char * s)860 int php_libxml_xmlCheckUTF8(const unsigned char *s)
861 {
862 	int i;
863 	unsigned char c;
864 
865 	for (i = 0; (c = s[i++]);) {
866 		if ((c & 0x80) == 0) {
867 		} else if ((c & 0xe0) == 0xc0) {
868 			if ((s[i++] & 0xc0) != 0x80) {
869 				return 0;
870 			}
871 		} else if ((c & 0xf0) == 0xe0) {
872 			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
873 				return 0;
874 			}
875 		} else if ((c & 0xf8) == 0xf0) {
876 			if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
877 				return 0;
878 			}
879 		} else {
880 			return 0;
881 		}
882 	}
883 	return 1;
884 }
885 
php_libxml_register_export(zend_class_entry * ce,php_libxml_export_node export_function)886 int php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
887 {
888 	php_libxml_func_handler export_hnd;
889 
890 	/* Initialize in case this module hasnt been loaded yet */
891 	php_libxml_initialize();
892 	export_hnd.export_func = export_function;
893 
894 	return zend_hash_add(&php_libxml_exports, ce->name, ce->name_length + 1, &export_hnd, sizeof(export_hnd), NULL);
895 }
896 
php_libxml_import_node(zval * object TSRMLS_DC)897 PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object TSRMLS_DC)
898 {
899 	zend_class_entry *ce = NULL;
900 	xmlNodePtr node = NULL;
901 	php_libxml_func_handler *export_hnd;
902 
903 	if (object->type == IS_OBJECT) {
904 		ce = Z_OBJCE_P(object);
905 		while (ce->parent != NULL) {
906 			ce = ce->parent;
907 		}
908 		if (zend_hash_find(&php_libxml_exports, ce->name, ce->name_length + 1, (void **) &export_hnd)  == SUCCESS) {
909 			node = export_hnd->export_func(object TSRMLS_CC);
910 		}
911 	}
912 	return node;
913 }
914 
php_libxml_increment_node_ptr(php_libxml_node_object * object,xmlNodePtr node,void * private_data TSRMLS_DC)915 PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data TSRMLS_DC)
916 {
917 	int ret_refcount = -1;
918 
919 	if (object != NULL && node != NULL) {
920 		if (object->node != NULL) {
921 			if (object->node->node == node) {
922 				return object->node->refcount;
923 			} else {
924 				php_libxml_decrement_node_ptr(object TSRMLS_CC);
925 			}
926 		}
927 		if (node->_private != NULL) {
928 			object->node = node->_private;
929 			ret_refcount = ++object->node->refcount;
930 			/* Only dom uses _private */
931 			if (object->node->_private == NULL) {
932 				object->node->_private = private_data;
933 			}
934 		} else {
935 			ret_refcount = 1;
936 			object->node = emalloc(sizeof(php_libxml_node_ptr));
937 			object->node->node = node;
938 			object->node->refcount = 1;
939 			object->node->_private = private_data;
940 			node->_private = object->node;
941 		}
942 	}
943 
944 	return ret_refcount;
945 }
946 
php_libxml_decrement_node_ptr(php_libxml_node_object * object TSRMLS_DC)947 PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object TSRMLS_DC)
948 {
949 	int ret_refcount = -1;
950 	php_libxml_node_ptr *obj_node;
951 
952 	if (object != NULL && object->node != NULL) {
953 		obj_node = (php_libxml_node_ptr *) object->node;
954 		ret_refcount = --obj_node->refcount;
955 		if (ret_refcount == 0) {
956 			if (obj_node->node != NULL) {
957 				obj_node->node->_private = NULL;
958 			}
959 			efree(obj_node);
960 		}
961 		object->node = NULL;
962 	}
963 
964 	return ret_refcount;
965 }
966 
php_libxml_increment_doc_ref(php_libxml_node_object * object,xmlDocPtr docp TSRMLS_DC)967 PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp TSRMLS_DC)
968 {
969 	int ret_refcount = -1;
970 
971 	if (object->document != NULL) {
972 		object->document->refcount++;
973 		ret_refcount = object->document->refcount;
974 	} else if (docp != NULL) {
975 		ret_refcount = 1;
976 		object->document = emalloc(sizeof(php_libxml_ref_obj));
977 		object->document->ptr = docp;
978 		object->document->refcount = ret_refcount;
979 		object->document->doc_props = NULL;
980 	}
981 
982 	return ret_refcount;
983 }
984 
php_libxml_decrement_doc_ref(php_libxml_node_object * object TSRMLS_DC)985 PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object TSRMLS_DC)
986 {
987 	int ret_refcount = -1;
988 
989 	if (object != NULL && object->document != NULL) {
990 		ret_refcount = --object->document->refcount;
991 		if (ret_refcount == 0) {
992 			if (object->document->ptr != NULL) {
993 				xmlFreeDoc((xmlDoc *) object->document->ptr);
994 			}
995 			if (object->document->doc_props != NULL) {
996 				if (object->document->doc_props->classmap) {
997 					zend_hash_destroy(object->document->doc_props->classmap);
998 					FREE_HASHTABLE(object->document->doc_props->classmap);
999 				}
1000 				efree(object->document->doc_props);
1001 			}
1002 			efree(object->document);
1003 			object->document = NULL;
1004 		}
1005 	}
1006 
1007 	return ret_refcount;
1008 }
1009 
php_libxml_node_free_resource(xmlNodePtr node TSRMLS_DC)1010 PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node TSRMLS_DC)
1011 {
1012 	if (!node) {
1013 		return;
1014 	}
1015 
1016 	switch (node->type) {
1017 		case XML_DOCUMENT_NODE:
1018 		case XML_HTML_DOCUMENT_NODE:
1019 			break;
1020 		default:
1021 			if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1022 				php_libxml_node_free_list((xmlNodePtr) node->children TSRMLS_CC);
1023 				switch (node->type) {
1024 					/* Skip property freeing for the following types */
1025 					case XML_ATTRIBUTE_DECL:
1026 					case XML_DTD_NODE:
1027 					case XML_DOCUMENT_TYPE_NODE:
1028 					case XML_ENTITY_DECL:
1029 					case XML_ATTRIBUTE_NODE:
1030 					case XML_NAMESPACE_DECL:
1031 					case XML_TEXT_NODE:
1032 						break;
1033 					default:
1034 						php_libxml_node_free_list((xmlNodePtr) node->properties TSRMLS_CC);
1035 				}
1036 				if (php_libxml_unregister_node(node TSRMLS_CC) == 0) {
1037 					node->doc = NULL;
1038 				}
1039 				php_libxml_node_free(node);
1040 			} else {
1041 				php_libxml_unregister_node(node TSRMLS_CC);
1042 			}
1043 	}
1044 }
1045 
php_libxml_node_decrement_resource(php_libxml_node_object * object TSRMLS_DC)1046 PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object TSRMLS_DC)
1047 {
1048 	int ret_refcount = -1;
1049 	xmlNodePtr nodep;
1050 	php_libxml_node_ptr *obj_node;
1051 
1052 	if (object != NULL && object->node != NULL) {
1053 		obj_node = (php_libxml_node_ptr *) object->node;
1054 		nodep = object->node->node;
1055 		ret_refcount = php_libxml_decrement_node_ptr(object TSRMLS_CC);
1056 		if (ret_refcount == 0) {
1057 			php_libxml_node_free_resource(nodep TSRMLS_CC);
1058 		} else {
1059 			if (obj_node && object == obj_node->_private) {
1060 				obj_node->_private = NULL;
1061 			}
1062 		}
1063 	}
1064 	if (object != NULL && object->document != NULL) {
1065 		/* Safe to call as if the resource were freed then doc pointer is NULL */
1066 		php_libxml_decrement_doc_ref(object TSRMLS_CC);
1067 	}
1068 }
1069 /* }}} */
1070 
1071 #ifdef PHP_WIN32
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)1072 PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1073 {
1074 	return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1075 }
1076 #endif
1077 
1078 #endif
1079 
1080 /*
1081  * Local variables:
1082  * tab-width: 4
1083  * c-basic-offset: 4
1084  * End:
1085  * vim600: sw=4 ts=4 fdm=marker
1086  * vim<600: sw=4 ts=4
1087  */
1088