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