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