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