1/*
2   +----------------------------------------------------------------------+
3   | PHP Version 7                                                        |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1997-2015 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        /* Enable the entity loader by default. This ensures that
865         * other threads/requests that might have disabled the loader
866         * do not affect the current request.
867         */
868        LIBXML(entity_loader_disabled) = 0;
869    }
870    return SUCCESS;
871}
872
873static PHP_RSHUTDOWN_FUNCTION(libxml)
874{
875    _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
876
877    return SUCCESS;
878}
879
880static PHP_MSHUTDOWN_FUNCTION(libxml)
881{
882    if (!_php_libxml_per_request_initialization) {
883        xmlSetGenericErrorFunc(NULL, NULL);
884
885        xmlParserInputBufferCreateFilenameDefault(NULL);
886        xmlOutputBufferCreateFilenameDefault(NULL);
887    }
888    php_libxml_shutdown();
889
890    return SUCCESS;
891}
892
893static int php_libxml_post_deactivate(void)
894{
895    /* reset libxml generic error handling */
896    if (_php_libxml_per_request_initialization) {
897        xmlSetGenericErrorFunc(NULL, NULL);
898
899        xmlParserInputBufferCreateFilenameDefault(NULL);
900        xmlOutputBufferCreateFilenameDefault(NULL);
901    }
902    xmlSetStructuredErrorFunc(NULL, NULL);
903
904    /* the steam_context resource will be released by resource list destructor */
905    ZVAL_UNDEF(&LIBXML(stream_context));
906    smart_str_free(&LIBXML(error_buffer));
907    if (LIBXML(error_list)) {
908        zend_llist_destroy(LIBXML(error_list));
909        efree(LIBXML(error_list));
910        LIBXML(error_list) = NULL;
911    }
912    xmlResetLastError();
913
914    return SUCCESS;
915}
916
917
918static PHP_MINFO_FUNCTION(libxml)
919{
920    php_info_print_table_start();
921    php_info_print_table_row(2, "libXML support", "active");
922    php_info_print_table_row(2, "libXML Compiled Version", LIBXML_DOTTED_VERSION);
923    php_info_print_table_row(2, "libXML Loaded Version", (char *)xmlParserVersion);
924    php_info_print_table_row(2, "libXML streams", "enabled");
925    php_info_print_table_end();
926}
927/* }}} */
928
929/* {{{ proto void libxml_set_streams_context(resource streams_context)
930   Set the streams context for the next libxml document load or write */
931static PHP_FUNCTION(libxml_set_streams_context)
932{
933    zval *arg;
934
935    if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg) == FAILURE) {
936        return;
937    }
938    if (!Z_ISUNDEF(LIBXML(stream_context))) {
939        zval_ptr_dtor(&LIBXML(stream_context));
940        ZVAL_UNDEF(&LIBXML(stream_context));
941    }
942    ZVAL_COPY(&LIBXML(stream_context), arg);
943}
944/* }}} */
945
946/* {{{ proto bool libxml_use_internal_errors([boolean use_errors])
947   Disable libxml errors and allow user to fetch error information as needed */
948static PHP_FUNCTION(libxml_use_internal_errors)
949{
950    xmlStructuredErrorFunc current_handler;
951    zend_bool use_errors=0, retval;
952
953    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &use_errors) == FAILURE) {
954        return;
955    }
956
957    current_handler = xmlStructuredError;
958    if (current_handler && current_handler == php_libxml_structured_error_handler) {
959        retval = 1;
960    } else {
961        retval = 0;
962    }
963
964    if (ZEND_NUM_ARGS() == 0) {
965        RETURN_BOOL(retval);
966    }
967
968    if (use_errors == 0) {
969        xmlSetStructuredErrorFunc(NULL, NULL);
970        if (LIBXML(error_list)) {
971            zend_llist_destroy(LIBXML(error_list));
972            efree(LIBXML(error_list));
973            LIBXML(error_list) = NULL;
974        }
975    } else {
976        xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
977        if (LIBXML(error_list) == NULL) {
978            LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
979            zend_llist_init(LIBXML(error_list), sizeof(xmlError), (llist_dtor_func_t) _php_libxml_free_error, 0);
980        }
981    }
982    RETURN_BOOL(retval);
983}
984/* }}} */
985
986/* {{{ proto object libxml_get_last_error()
987   Retrieve last error from libxml */
988static PHP_FUNCTION(libxml_get_last_error)
989{
990    xmlErrorPtr error;
991
992    error = xmlGetLastError();
993
994    if (error) {
995        object_init_ex(return_value, libxmlerror_class_entry);
996        add_property_long(return_value, "level", error->level);
997        add_property_long(return_value, "code", error->code);
998        add_property_long(return_value, "column", error->int2);
999        if (error->message) {
1000            add_property_string(return_value, "message", error->message);
1001        } else {
1002            add_property_stringl(return_value, "message", "", 0);
1003        }
1004        if (error->file) {
1005            add_property_string(return_value, "file", error->file);
1006        } else {
1007            add_property_stringl(return_value, "file", "", 0);
1008        }
1009        add_property_long(return_value, "line", error->line);
1010    } else {
1011        RETURN_FALSE;
1012    }
1013}
1014/* }}} */
1015
1016/* {{{ proto object libxml_get_errors()
1017   Retrieve array of errors */
1018static PHP_FUNCTION(libxml_get_errors)
1019{
1020
1021    xmlErrorPtr error;
1022
1023    if (array_init(return_value) == FAILURE) {
1024        RETURN_FALSE;
1025    }
1026
1027    if (LIBXML(error_list)) {
1028
1029        error = zend_llist_get_first(LIBXML(error_list));
1030
1031        while (error != NULL) {
1032            zval z_error;
1033
1034            object_init_ex(&z_error, libxmlerror_class_entry);
1035            add_property_long_ex(&z_error, "level", sizeof("level") - 1, error->level);
1036            add_property_long_ex(&z_error, "code", sizeof("code") - 1, error->code);
1037            add_property_long_ex(&z_error, "column", sizeof("column") - 1, error->int2 );
1038            if (error->message) {
1039                add_property_string_ex(&z_error, "message", sizeof("message") - 1, error->message);
1040            } else {
1041                add_property_stringl_ex(&z_error, "message", sizeof("message") - 1, "", 0);
1042            }
1043            if (error->file) {
1044                add_property_string_ex(&z_error, "file", sizeof("file") - 1, error->file);
1045            } else {
1046                add_property_stringl_ex(&z_error, "file", sizeof("file") - 1, "", 0);
1047            }
1048            add_property_long_ex(&z_error, "line", sizeof("line") - 1, error->line);
1049            add_next_index_zval(return_value, &z_error);
1050
1051            error = zend_llist_get_next(LIBXML(error_list));
1052        }
1053    }
1054}
1055/* }}} */
1056
1057/* {{{ proto void libxml_clear_errors()
1058   Clear last error from libxml */
1059static PHP_FUNCTION(libxml_clear_errors)
1060{
1061    xmlResetLastError();
1062    if (LIBXML(error_list)) {
1063        zend_llist_clean(LIBXML(error_list));
1064    }
1065}
1066/* }}} */
1067
1068PHP_LIBXML_API zend_bool php_libxml_disable_entity_loader(zend_bool disable) /* {{{ */
1069{
1070    zend_bool old = LIBXML(entity_loader_disabled);
1071
1072    LIBXML(entity_loader_disabled) = disable;
1073    return old;
1074} /* }}} */
1075
1076/* {{{ proto bool libxml_disable_entity_loader([boolean disable])
1077   Disable/Enable ability to load external entities */
1078static PHP_FUNCTION(libxml_disable_entity_loader)
1079{
1080    zend_bool disable = 1;
1081
1082    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &disable) == FAILURE) {
1083        return;
1084    }
1085
1086    RETURN_BOOL(php_libxml_disable_entity_loader(disable));
1087}
1088/* }}} */
1089
1090/* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
1091   Changes the default external entity loader */
1092static PHP_FUNCTION(libxml_set_external_entity_loader)
1093{
1094    zend_fcall_info         fci;
1095    zend_fcall_info_cache   fcc;
1096    if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!", &fci, &fcc)
1097            == FAILURE) {
1098        return;
1099    }
1100
1101    _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
1102
1103    if (fci.size > 0) { /* argument not null */
1104        LIBXML(entity_loader).fci = fci;
1105        Z_ADDREF(fci.function_name);
1106        if (fci.object != NULL) {
1107            ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object);
1108            Z_ADDREF(LIBXML(entity_loader).object);
1109        }
1110        LIBXML(entity_loader).fcc = fcc;
1111    }
1112
1113    RETURN_TRUE;
1114}
1115/* }}} */
1116
1117/* {{{ Common functions shared by extensions */
1118int php_libxml_xmlCheckUTF8(const unsigned char *s)
1119{
1120    int i;
1121    unsigned char c;
1122
1123    for (i = 0; (c = s[i++]);) {
1124        if ((c & 0x80) == 0) {
1125        } else if ((c & 0xe0) == 0xc0) {
1126            if ((s[i++] & 0xc0) != 0x80) {
1127                return 0;
1128            }
1129        } else if ((c & 0xf0) == 0xe0) {
1130            if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1131                return 0;
1132            }
1133        } else if ((c & 0xf8) == 0xf0) {
1134            if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1135                return 0;
1136            }
1137        } else {
1138            return 0;
1139        }
1140    }
1141    return 1;
1142}
1143
1144zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
1145{
1146    php_libxml_func_handler export_hnd;
1147
1148    /* Initialize in case this module hasn't been loaded yet */
1149    php_libxml_initialize();
1150    export_hnd.export_func = export_function;
1151
1152    return zend_hash_add_mem(&php_libxml_exports, ce->name, &export_hnd, sizeof(export_hnd));
1153}
1154
1155PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object)
1156{
1157    zend_class_entry *ce = NULL;
1158    xmlNodePtr node = NULL;
1159    php_libxml_func_handler *export_hnd;
1160
1161    if (Z_TYPE_P(object) == IS_OBJECT) {
1162        ce = Z_OBJCE_P(object);
1163        while (ce->parent != NULL) {
1164            ce = ce->parent;
1165        }
1166        if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) {
1167            node = export_hnd->export_func(object);
1168        }
1169    }
1170    return node;
1171}
1172
1173PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data)
1174{
1175    int ret_refcount = -1;
1176
1177    if (object != NULL && node != NULL) {
1178        if (object->node != NULL) {
1179            if (object->node->node == node) {
1180                return object->node->refcount;
1181            } else {
1182                php_libxml_decrement_node_ptr(object);
1183            }
1184        }
1185        if (node->_private != NULL) {
1186            object->node = node->_private;
1187            ret_refcount = ++object->node->refcount;
1188            /* Only dom uses _private */
1189            if (object->node->_private == NULL) {
1190                object->node->_private = private_data;
1191            }
1192        } else {
1193            ret_refcount = 1;
1194            object->node = emalloc(sizeof(php_libxml_node_ptr));
1195            object->node->node = node;
1196            object->node->refcount = 1;
1197            object->node->_private = private_data;
1198            node->_private = object->node;
1199        }
1200    }
1201
1202    return ret_refcount;
1203}
1204
1205PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object)
1206{
1207    int ret_refcount = -1;
1208    php_libxml_node_ptr *obj_node;
1209
1210    if (object != NULL && object->node != NULL) {
1211        obj_node = (php_libxml_node_ptr *) object->node;
1212        ret_refcount = --obj_node->refcount;
1213        if (ret_refcount == 0) {
1214            if (obj_node->node != NULL) {
1215                obj_node->node->_private = NULL;
1216            }
1217            efree(obj_node);
1218        }
1219        object->node = NULL;
1220    }
1221
1222    return ret_refcount;
1223}
1224
1225PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp)
1226{
1227    int ret_refcount = -1;
1228
1229    if (object->document != NULL) {
1230        object->document->refcount++;
1231        ret_refcount = object->document->refcount;
1232    } else if (docp != NULL) {
1233        ret_refcount = 1;
1234        object->document = emalloc(sizeof(php_libxml_ref_obj));
1235        object->document->ptr = docp;
1236        object->document->refcount = ret_refcount;
1237        object->document->doc_props = NULL;
1238    }
1239
1240    return ret_refcount;
1241}
1242
1243PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object)
1244{
1245    int ret_refcount = -1;
1246
1247    if (object != NULL && object->document != NULL) {
1248        ret_refcount = --object->document->refcount;
1249        if (ret_refcount == 0) {
1250            if (object->document->ptr != NULL) {
1251                xmlFreeDoc((xmlDoc *) object->document->ptr);
1252            }
1253            if (object->document->doc_props != NULL) {
1254                if (object->document->doc_props->classmap) {
1255                    zend_hash_destroy(object->document->doc_props->classmap);
1256                    FREE_HASHTABLE(object->document->doc_props->classmap);
1257                }
1258                efree(object->document->doc_props);
1259            }
1260            efree(object->document);
1261            object->document = NULL;
1262        }
1263    }
1264
1265    return ret_refcount;
1266}
1267
1268PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node)
1269{
1270    if (!node) {
1271        return;
1272    }
1273
1274    switch (node->type) {
1275        case XML_DOCUMENT_NODE:
1276        case XML_HTML_DOCUMENT_NODE:
1277            break;
1278        default:
1279            if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1280                php_libxml_node_free_list((xmlNodePtr) node->children);
1281                switch (node->type) {
1282                    /* Skip property freeing for the following types */
1283                    case XML_ATTRIBUTE_DECL:
1284                    case XML_DTD_NODE:
1285                    case XML_DOCUMENT_TYPE_NODE:
1286                    case XML_ENTITY_DECL:
1287                    case XML_ATTRIBUTE_NODE:
1288                    case XML_NAMESPACE_DECL:
1289                    case XML_TEXT_NODE:
1290                        break;
1291                    default:
1292                        php_libxml_node_free_list((xmlNodePtr) node->properties);
1293                }
1294                if (php_libxml_unregister_node(node) == 0) {
1295                    node->doc = NULL;
1296                }
1297                php_libxml_node_free(node);
1298            } else {
1299                php_libxml_unregister_node(node);
1300            }
1301    }
1302}
1303
1304PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object)
1305{
1306    int ret_refcount = -1;
1307    xmlNodePtr nodep;
1308    php_libxml_node_ptr *obj_node;
1309
1310    if (object != NULL && object->node != NULL) {
1311        obj_node = (php_libxml_node_ptr *) object->node;
1312        nodep = object->node->node;
1313        ret_refcount = php_libxml_decrement_node_ptr(object);
1314        if (ret_refcount == 0) {
1315            php_libxml_node_free_resource(nodep);
1316        } else {
1317            if (obj_node && object == obj_node->_private) {
1318                obj_node->_private = NULL;
1319            }
1320        }
1321    }
1322    if (object != NULL && object->document != NULL) {
1323        /* Safe to call as if the resource were freed then doc pointer is NULL */
1324        php_libxml_decrement_doc_ref(object);
1325    }
1326}
1327/* }}} */
1328
1329#ifdef PHP_WIN32
1330PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1331{
1332    return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1333}
1334#endif
1335
1336#endif
1337
1338/*
1339 * Local variables:
1340 * tab-width: 4
1341 * c-basic-offset: 4
1342 * End:
1343 * vim600: sw=4 ts=4 fdm=marker
1344 * vim<600: sw=4 ts=4
1345 */
1346