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 */ 55static int _php_libxml_initialized = 0; 56 57typedef struct _php_libxml_func_handler { 58 php_libxml_export_node export_func; 59} php_libxml_func_handler; 60 61static HashTable php_libxml_exports; 62 63static ZEND_DECLARE_MODULE_GLOBALS(libxml) 64static PHP_GINIT_FUNCTION(libxml); 65 66static PHP_FUNCTION(libxml_set_streams_context); 67static PHP_FUNCTION(libxml_use_internal_errors); 68static PHP_FUNCTION(libxml_get_last_error); 69static PHP_FUNCTION(libxml_clear_errors); 70static PHP_FUNCTION(libxml_get_errors); 71static PHP_FUNCTION(libxml_disable_entity_loader); 72 73static zend_class_entry *libxmlerror_class_entry; 74 75/* {{{ dynamically loadable module stuff */ 76#ifdef COMPILE_DL_LIBXML 77ZEND_GET_MODULE(libxml) 78#endif /* COMPILE_DL_LIBXML */ 79/* }}} */ 80 81/* {{{ function prototypes */ 82static PHP_MINIT_FUNCTION(libxml); 83static PHP_RINIT_FUNCTION(libxml); 84static PHP_MSHUTDOWN_FUNCTION(libxml); 85static PHP_MINFO_FUNCTION(libxml); 86static int php_libxml_post_deactivate(); 87 88/* }}} */ 89 90/* {{{ arginfo */ 91ZEND_BEGIN_ARG_INFO(arginfo_libxml_set_streams_context, 0) 92 ZEND_ARG_INFO(0, context) 93ZEND_END_ARG_INFO() 94 95ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_use_internal_errors, 0, 0, 0) 96 ZEND_ARG_INFO(0, use_errors) 97ZEND_END_ARG_INFO() 98 99ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_last_error, 0) 100ZEND_END_ARG_INFO() 101 102ZEND_BEGIN_ARG_INFO(arginfo_libxml_get_errors, 0) 103ZEND_END_ARG_INFO() 104 105ZEND_BEGIN_ARG_INFO(arginfo_libxml_clear_errors, 0) 106ZEND_END_ARG_INFO() 107 108ZEND_BEGIN_ARG_INFO_EX(arginfo_libxml_disable_entity_loader, 0, 0, 0) 109 ZEND_ARG_INFO(0, disable) 110ZEND_END_ARG_INFO() 111 112/* }}} */ 113 114/* {{{ extension definition structures */ 115static 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 125zend_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 */ 145static 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 154static 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 175static 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 214static 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 */ 259static 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 270static 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 323static 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 328static 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 333static 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 339static 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 345static int php_libxml_streams_IO_close(void *context) 346{ 347 TSRMLS_FETCH(); 348 return php_stream_close((php_stream*)context); 349} 350 351static xmlParserInputBufferPtr 352php_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 383static xmlOutputBufferPtr 384php_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 428static 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 435static 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 468static 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 483void 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 492static 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 531PHP_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 539PHP_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 547PHP_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 554PHP_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 563PHP_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 575PHP_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 587PHP_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 597static 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 641static 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 651static PHP_MSHUTDOWN_FUNCTION(libxml) 652{ 653 php_libxml_shutdown(); 654 655 return SUCCESS; 656} 657 658static 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 685static 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 */ 698static 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 */ 716static 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 */ 756static 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 */ 786static 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 */ 828static PHP_FUNCTION(libxml_clear_errors) 829{ 830 xmlResetLastError(); 831 if (LIBXML(error_list)) { 832 zend_llist_clean(LIBXML(error_list)); 833 } 834} 835/* }}} */ 836 837PHP_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 */ 847static 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 */ 860int 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 886int 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 897PHP_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 915PHP_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 947PHP_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 967PHP_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 985PHP_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 1010PHP_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 1046PHP_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 1072PHP_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