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