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    PHP_LIBXML_VERSION,
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#if LIBXML_VERSION >= 20902 && defined(PHP_WIN32)
318        /* Libxml 2.9.2 prefixes local paths with file:/ instead of file://,
319            thus the php stream wrapper will fail on a valid case. For this
320            reason the prefix is rather better cut off. */
321        {
322            size_t pre_len = sizeof("file:/") - 1;
323
324            if (strncasecmp(resolved_path, "file:/", pre_len) == 0
325                && '/' != resolved_path[pre_len]) {
326                xmlChar *tmp = xmlStrdup(resolved_path + pre_len);
327                xmlFree(resolved_path);
328                resolved_path = tmp;
329            }
330        }
331#endif
332    } else {
333        resolved_path = (char *)filename;
334    }
335
336    if (uri) {
337        xmlFreeURI(uri);
338    }
339
340    if (resolved_path == NULL) {
341        return NULL;
342    }
343
344    /* logic copied from _php_stream_stat, but we only want to fail
345       if the wrapper supports stat, otherwise, figure it out from
346       the open.  This logic is only to support hiding warnings
347       that the streams layer puts out at times, but for libxml we
348       may try to open files that don't exist, but it is not a failure
349       in xml processing (eg. DTD files)  */
350    wrapper = php_stream_locate_url_wrapper(resolved_path, &path_to_open, 0);
351    if (wrapper && read_only && wrapper->wops->url_stat) {
352        if (wrapper->wops->url_stat(wrapper, path_to_open, PHP_STREAM_URL_STAT_QUIET, &ssbuf, NULL) == -1) {
353            if (isescaped) {
354                xmlFree(resolved_path);
355            }
356            return NULL;
357        }
358    }
359
360    context = php_stream_context_from_zval(Z_ISUNDEF(LIBXML(stream_context))? NULL : &LIBXML(stream_context), 0);
361
362    ret_val = php_stream_open_wrapper_ex(path_to_open, (char *)mode, REPORT_ERRORS, NULL, context);
363    if (isescaped) {
364        xmlFree(resolved_path);
365    }
366    return ret_val;
367}
368
369static void *php_libxml_streams_IO_open_read_wrapper(const char *filename)
370{
371    return php_libxml_streams_IO_open_wrapper(filename, "rb", 1);
372}
373
374static void *php_libxml_streams_IO_open_write_wrapper(const char *filename)
375{
376    return php_libxml_streams_IO_open_wrapper(filename, "wb", 0);
377}
378
379static int php_libxml_streams_IO_read(void *context, char *buffer, int len)
380{
381    return php_stream_read((php_stream*)context, buffer, len);
382}
383
384static int php_libxml_streams_IO_write(void *context, const char *buffer, int len)
385{
386    return php_stream_write((php_stream*)context, buffer, len);
387}
388
389static int php_libxml_streams_IO_close(void *context)
390{
391    return php_stream_close((php_stream*)context);
392}
393
394static xmlParserInputBufferPtr
395php_libxml_input_buffer_create_filename(const char *URI, xmlCharEncoding enc)
396{
397    xmlParserInputBufferPtr ret;
398    void *context = NULL;
399
400    if (LIBXML(entity_loader_disabled)) {
401        return NULL;
402    }
403
404    if (URI == NULL)
405        return(NULL);
406
407    context = php_libxml_streams_IO_open_read_wrapper(URI);
408
409    if (context == NULL) {
410        return(NULL);
411    }
412
413    /* Allocate the Input buffer front-end. */
414    ret = xmlAllocParserInputBuffer(enc);
415    if (ret != NULL) {
416        ret->context = context;
417        ret->readcallback = php_libxml_streams_IO_read;
418        ret->closecallback = php_libxml_streams_IO_close;
419    } else
420        php_libxml_streams_IO_close(context);
421
422    return(ret);
423}
424
425static xmlOutputBufferPtr
426php_libxml_output_buffer_create_filename(const char *URI,
427                              xmlCharEncodingHandlerPtr encoder,
428                              int compression ATTRIBUTE_UNUSED)
429{
430    xmlOutputBufferPtr ret;
431    xmlURIPtr puri;
432    void *context = NULL;
433    char *unescaped = NULL;
434
435    if (URI == NULL)
436        return(NULL);
437
438    puri = xmlParseURI(URI);
439    if (puri != NULL) {
440        if (puri->scheme != NULL)
441            unescaped = xmlURIUnescapeString(URI, 0, NULL);
442        xmlFreeURI(puri);
443    }
444
445    if (unescaped != NULL) {
446        context = php_libxml_streams_IO_open_write_wrapper(unescaped);
447        xmlFree(unescaped);
448    }
449
450    /* try with a non-escaped URI this may be a strange filename */
451    if (context == NULL) {
452        context = php_libxml_streams_IO_open_write_wrapper(URI);
453    }
454
455    if (context == NULL) {
456        return(NULL);
457    }
458
459    /* Allocate the Output buffer front-end. */
460    ret = xmlAllocOutputBuffer(encoder);
461    if (ret != NULL) {
462        ret->context = context;
463        ret->writecallback = php_libxml_streams_IO_write;
464        ret->closecallback = php_libxml_streams_IO_close;
465    }
466
467    return(ret);
468}
469
470static int _php_libxml_free_error(xmlErrorPtr error)
471{
472    /* This will free the libxml alloc'd memory */
473    xmlResetError(error);
474    return 1;
475}
476
477static void _php_list_set_error_structure(xmlErrorPtr error, const char *msg)
478{
479    xmlError error_copy;
480    int ret;
481
482
483    memset(&error_copy, 0, sizeof(xmlError));
484
485    if (error) {
486        ret = xmlCopyError(error, &error_copy);
487    } else {
488        error_copy.domain = 0;
489        error_copy.code = XML_ERR_INTERNAL_ERROR;
490        error_copy.level = XML_ERR_ERROR;
491        error_copy.line = 0;
492        error_copy.node = NULL;
493        error_copy.int1 = 0;
494        error_copy.int2 = 0;
495        error_copy.ctxt = NULL;
496        error_copy.message = (char*)xmlStrdup((xmlChar*)msg);
497        error_copy.file = NULL;
498        error_copy.str1 = NULL;
499        error_copy.str2 = NULL;
500        error_copy.str3 = NULL;
501        ret = 0;
502    }
503
504    if (ret == 0) {
505        zend_llist_add_element(LIBXML(error_list), &error_copy);
506    }
507}
508
509static void php_libxml_ctx_error_level(int level, void *ctx, const char *msg)
510{
511    xmlParserCtxtPtr parser;
512
513    parser = (xmlParserCtxtPtr) ctx;
514
515    if (parser != NULL && parser->input != NULL) {
516        if (parser->input->filename) {
517            php_error_docref(NULL, level, "%s in %s, line: %d", msg, parser->input->filename, parser->input->line);
518        } else {
519            php_error_docref(NULL, level, "%s in Entity, line: %d", msg, parser->input->line);
520        }
521    }
522}
523
524void php_libxml_issue_error(int level, const char *msg)
525{
526    if (LIBXML(error_list)) {
527        _php_list_set_error_structure(NULL, msg);
528    } else {
529        php_error_docref(NULL, level, "%s", msg);
530    }
531}
532
533static void php_libxml_internal_error_handler(int error_type, void *ctx, const char **msg, va_list ap)
534{
535    char *buf;
536    int len, len_iter, output = 0;
537
538
539    len = vspprintf(&buf, 0, *msg, ap);
540    len_iter = len;
541
542    /* remove any trailing \n */
543    while (len_iter && buf[--len_iter] == '\n') {
544        buf[len_iter] = '\0';
545        output = 1;
546    }
547
548    smart_str_appendl(&LIBXML(error_buffer), buf, len);
549
550    efree(buf);
551
552    if (output == 1) {
553        if (LIBXML(error_list)) {
554            _php_list_set_error_structure(NULL, LIBXML(error_buffer).s->val);
555        } else {
556            switch (error_type) {
557                case PHP_LIBXML_CTX_ERROR:
558                    php_libxml_ctx_error_level(E_WARNING, ctx, LIBXML(error_buffer).s->val);
559                    break;
560                case PHP_LIBXML_CTX_WARNING:
561                    php_libxml_ctx_error_level(E_NOTICE, ctx, LIBXML(error_buffer).s->val);
562                    break;
563                default:
564                    php_error_docref(NULL, E_WARNING, "%s", LIBXML(error_buffer).s->val);
565            }
566        }
567        smart_str_free(&LIBXML(error_buffer));
568    }
569}
570
571static xmlParserInputPtr _php_libxml_external_entity_loader(const char *URL,
572        const char *ID, xmlParserCtxtPtr context)
573{
574    xmlParserInputPtr   ret         = NULL;
575    const char          *resource   = NULL;
576    zval                *ctxzv, retval;
577    zval                params[3];
578    int                 status;
579    zend_fcall_info     *fci;
580
581    fci = &LIBXML(entity_loader).fci;
582
583    if (fci->size == 0) {
584        /* no custom user-land callback set up; delegate to original loader */
585        return _php_libxml_default_entity_loader(URL, ID, context);
586    }
587
588    if (ID != NULL) {
589        ZVAL_STRING(&params[0], ID);
590    } else {
591        ZVAL_UNDEF(&params[0]);
592    }
593    if (URL != NULL) {
594        ZVAL_STRING(&params[1], URL);
595    } else {
596        ZVAL_UNDEF(&params[1]);
597    }
598    ctxzv = &params[2];
599    array_init_size(ctxzv, 4);
600
601#define ADD_NULL_OR_STRING_KEY(memb) \
602    if (context->memb == NULL) { \
603        add_assoc_null_ex(ctxzv, #memb, sizeof(#memb) - 1); \
604    } else { \
605        add_assoc_string_ex(ctxzv, #memb, sizeof(#memb) - 1, \
606                (char *)context->memb); \
607    }
608
609    ADD_NULL_OR_STRING_KEY(directory)
610    ADD_NULL_OR_STRING_KEY(intSubName)
611    ADD_NULL_OR_STRING_KEY(extSubURI)
612    ADD_NULL_OR_STRING_KEY(extSubSystem)
613
614#undef ADD_NULL_OR_STRING_KEY
615
616    fci->retval = &retval;
617    fci->params = params;
618    fci->param_count = sizeof(params)/sizeof(*params);
619    fci->no_separation  = 1;
620
621    status = zend_call_function(fci, &LIBXML(entity_loader).fcc);
622    if (status != SUCCESS || Z_ISUNDEF(retval)) {
623        php_libxml_ctx_error(context,
624                "Call to user entity loader callback '%s' has failed",
625                Z_STRVAL(fci->function_name));
626    } else {
627        /*
628        retval_ptr = *fci->retval_ptr_ptr;
629        if (retval_ptr == NULL) {
630            php_libxml_ctx_error(context,
631                    "Call to user entity loader callback '%s' has failed; "
632                    "probably it has thrown an exception",
633                    fci->function_name);
634        } else */ if (Z_TYPE(retval) == IS_STRING) {
635is_string:
636            resource = Z_STRVAL(retval);
637        } else if (Z_TYPE(retval) == IS_RESOURCE) {
638            php_stream *stream;
639            php_stream_from_zval_no_verify(stream, &retval);
640            if (stream == NULL) {
641                php_libxml_ctx_error(context,
642                        "The user entity loader callback '%s' has returned a "
643                        "resource, but it is not a stream",
644                        Z_STRVAL(fci->function_name));
645            } else {
646                /* TODO: allow storing the encoding in the stream context? */
647                xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;
648                xmlParserInputBufferPtr pib = xmlAllocParserInputBuffer(enc);
649                if (pib == NULL) {
650                    php_libxml_ctx_error(context, "Could not allocate parser "
651                            "input buffer");
652                } else {
653                    /* make stream not being closed when the zval is freed */
654                    ++GC_REFCOUNT(stream->res);
655                    pib->context = stream;
656                    pib->readcallback = php_libxml_streams_IO_read;
657                    pib->closecallback = php_libxml_streams_IO_close;
658
659                    ret = xmlNewIOInputStream(context, pib, enc);
660                    if (ret == NULL) {
661                        xmlFreeParserInputBuffer(pib);
662                    }
663                }
664            }
665        } else if (Z_TYPE(retval) != IS_NULL) {
666            /* retval not string nor resource nor null; convert to string */
667            SEPARATE_ZVAL(&retval);
668            convert_to_string(&retval);
669            goto is_string;
670        } /* else is null; don't try anything */
671    }
672
673    if (ret == NULL) {
674        if (resource == NULL) {
675            if (ID == NULL) {
676                ID = "NULL";
677            }
678            php_libxml_ctx_error(context,
679                    "Failed to load external entity \"%s\"\n", ID);
680        } else {
681            /* we got the resource in the form of a string; open it */
682            ret = xmlNewInputFromFile(context, resource);
683        }
684    }
685
686    zval_ptr_dtor(&params[0]);
687    zval_ptr_dtor(&params[1]);
688    zval_ptr_dtor(&params[2]);
689    zval_ptr_dtor(&retval);
690    return ret;
691}
692
693static xmlParserInputPtr _php_libxml_pre_ext_ent_loader(const char *URL,
694        const char *ID, xmlParserCtxtPtr context)
695{
696
697    /* Check whether we're running in a PHP context, since the entity loader
698     * we've defined is an application level (true global) setting.
699     * If we are, we also want to check whether we've finished activating
700     * the modules (RINIT phase). Using our external entity loader during a
701     * RINIT should not be problem per se (though during MINIT it is, because
702     * we don't even have a resource list by then), but then whether one
703     * extension would be using the custom external entity loader or not
704     * could depend on extension loading order
705     * (if _php_libxml_per_request_initialization */
706    if (xmlGenericError == php_libxml_error_handler && PG(modules_activated)) {
707        return _php_libxml_external_entity_loader(URL, ID, context);
708    } else {
709        return _php_libxml_default_entity_loader(URL, ID, context);
710    }
711}
712
713PHP_LIBXML_API void php_libxml_ctx_error(void *ctx, const char *msg, ...)
714{
715    va_list args;
716    va_start(args, msg);
717    php_libxml_internal_error_handler(PHP_LIBXML_CTX_ERROR, ctx, &msg, args);
718    va_end(args);
719}
720
721PHP_LIBXML_API void php_libxml_ctx_warning(void *ctx, const char *msg, ...)
722{
723    va_list args;
724    va_start(args, msg);
725    php_libxml_internal_error_handler(PHP_LIBXML_CTX_WARNING, ctx, &msg, args);
726    va_end(args);
727}
728
729PHP_LIBXML_API void php_libxml_structured_error_handler(void *userData, xmlErrorPtr error)
730{
731    _php_list_set_error_structure(error, NULL);
732
733    return;
734}
735
736PHP_LIBXML_API void php_libxml_error_handler(void *ctx, const char *msg, ...)
737{
738    va_list args;
739    va_start(args, msg);
740    php_libxml_internal_error_handler(PHP_LIBXML_ERROR, ctx, &msg, args);
741    va_end(args);
742}
743
744static void php_libxml_exports_dtor(zval *zv)
745{
746    free(Z_PTR_P(zv));
747}
748
749PHP_LIBXML_API void php_libxml_initialize(void)
750{
751    if (!_php_libxml_initialized) {
752        /* we should be the only one's to ever init!! */
753        xmlInitParser();
754
755        _php_libxml_default_entity_loader = xmlGetExternalEntityLoader();
756        xmlSetExternalEntityLoader(_php_libxml_pre_ext_ent_loader);
757
758        zend_hash_init(&php_libxml_exports, 0, NULL, php_libxml_exports_dtor, 1);
759
760        _php_libxml_initialized = 1;
761    }
762}
763
764PHP_LIBXML_API void php_libxml_shutdown(void)
765{
766    if (_php_libxml_initialized) {
767#if defined(LIBXML_SCHEMAS_ENABLED)
768        xmlRelaxNGCleanupTypes();
769#endif
770        /* xmlCleanupParser(); */
771        zend_hash_destroy(&php_libxml_exports);
772
773        xmlSetExternalEntityLoader(_php_libxml_default_entity_loader);
774        _php_libxml_initialized = 0;
775    }
776}
777
778PHP_LIBXML_API void php_libxml_switch_context(zval *context, zval *oldcontext)
779{
780    if (oldcontext) {
781        ZVAL_COPY_VALUE(oldcontext, &LIBXML(stream_context));
782    }
783    if (context) {
784        ZVAL_COPY_VALUE(&LIBXML(stream_context), context);
785    }
786}
787
788static PHP_MINIT_FUNCTION(libxml)
789{
790    zend_class_entry ce;
791
792    php_libxml_initialize();
793
794    REGISTER_LONG_CONSTANT("LIBXML_VERSION",            LIBXML_VERSION,         CONST_CS | CONST_PERSISTENT);
795    REGISTER_STRING_CONSTANT("LIBXML_DOTTED_VERSION",   LIBXML_DOTTED_VERSION,  CONST_CS | CONST_PERSISTENT);
796    REGISTER_STRING_CONSTANT("LIBXML_LOADED_VERSION",   (char *)xmlParserVersion,       CONST_CS | CONST_PERSISTENT);
797
798    /* For use with loading xml */
799    REGISTER_LONG_CONSTANT("LIBXML_NOENT",      XML_PARSE_NOENT,        CONST_CS | CONST_PERSISTENT);
800    REGISTER_LONG_CONSTANT("LIBXML_DTDLOAD",    XML_PARSE_DTDLOAD,      CONST_CS | CONST_PERSISTENT);
801    REGISTER_LONG_CONSTANT("LIBXML_DTDATTR",    XML_PARSE_DTDATTR,      CONST_CS | CONST_PERSISTENT);
802    REGISTER_LONG_CONSTANT("LIBXML_DTDVALID",   XML_PARSE_DTDVALID,     CONST_CS | CONST_PERSISTENT);
803    REGISTER_LONG_CONSTANT("LIBXML_NOERROR",    XML_PARSE_NOERROR,      CONST_CS | CONST_PERSISTENT);
804    REGISTER_LONG_CONSTANT("LIBXML_NOWARNING",  XML_PARSE_NOWARNING,    CONST_CS | CONST_PERSISTENT);
805    REGISTER_LONG_CONSTANT("LIBXML_NOBLANKS",   XML_PARSE_NOBLANKS,     CONST_CS | CONST_PERSISTENT);
806    REGISTER_LONG_CONSTANT("LIBXML_XINCLUDE",   XML_PARSE_XINCLUDE,     CONST_CS | CONST_PERSISTENT);
807    REGISTER_LONG_CONSTANT("LIBXML_NSCLEAN",    XML_PARSE_NSCLEAN,      CONST_CS | CONST_PERSISTENT);
808    REGISTER_LONG_CONSTANT("LIBXML_NOCDATA",    XML_PARSE_NOCDATA,      CONST_CS | CONST_PERSISTENT);
809    REGISTER_LONG_CONSTANT("LIBXML_NONET",      XML_PARSE_NONET,        CONST_CS | CONST_PERSISTENT);
810    REGISTER_LONG_CONSTANT("LIBXML_PEDANTIC",   XML_PARSE_PEDANTIC,     CONST_CS | CONST_PERSISTENT);
811#if LIBXML_VERSION >= 20621
812    REGISTER_LONG_CONSTANT("LIBXML_COMPACT",    XML_PARSE_COMPACT,      CONST_CS | CONST_PERSISTENT);
813    REGISTER_LONG_CONSTANT("LIBXML_NOXMLDECL",  XML_SAVE_NO_DECL,       CONST_CS | CONST_PERSISTENT);
814#endif
815#if LIBXML_VERSION >= 20703
816    REGISTER_LONG_CONSTANT("LIBXML_PARSEHUGE",  XML_PARSE_HUGE,         CONST_CS | CONST_PERSISTENT);
817#endif
818    REGISTER_LONG_CONSTANT("LIBXML_NOEMPTYTAG", LIBXML_SAVE_NOEMPTYTAG, CONST_CS | CONST_PERSISTENT);
819
820    /* Schema validation options */
821#if defined(LIBXML_SCHEMAS_ENABLED) && LIBXML_VERSION >= 20614
822    REGISTER_LONG_CONSTANT("LIBXML_SCHEMA_CREATE",  XML_SCHEMA_VAL_VC_I_CREATE, CONST_CS | CONST_PERSISTENT);
823#endif
824
825    /* Additional constants for use with loading html */
826#if LIBXML_VERSION >= 20707
827    REGISTER_LONG_CONSTANT("LIBXML_HTML_NOIMPLIED", HTML_PARSE_NOIMPLIED,       CONST_CS | CONST_PERSISTENT);
828#endif
829
830#if LIBXML_VERSION >= 20708
831    REGISTER_LONG_CONSTANT("LIBXML_HTML_NODEFDTD",  HTML_PARSE_NODEFDTD,        CONST_CS | CONST_PERSISTENT);
832#endif
833
834    /* Error levels */
835    REGISTER_LONG_CONSTANT("LIBXML_ERR_NONE",       XML_ERR_NONE,       CONST_CS | CONST_PERSISTENT);
836    REGISTER_LONG_CONSTANT("LIBXML_ERR_WARNING",    XML_ERR_WARNING,    CONST_CS | CONST_PERSISTENT);
837    REGISTER_LONG_CONSTANT("LIBXML_ERR_ERROR",      XML_ERR_ERROR,      CONST_CS | CONST_PERSISTENT);
838    REGISTER_LONG_CONSTANT("LIBXML_ERR_FATAL",      XML_ERR_FATAL,      CONST_CS | CONST_PERSISTENT);
839
840    INIT_CLASS_ENTRY(ce, "LibXMLError", NULL);
841    libxmlerror_class_entry = zend_register_internal_class(&ce);
842
843    if (sapi_module.name) {
844        static const char * const supported_sapis[] = {
845            "cgi-fcgi",
846            "fpm-fcgi",
847            "litespeed",
848            NULL
849        };
850        const char * const *sapi_name;
851
852        for (sapi_name = supported_sapis; *sapi_name; sapi_name++) {
853            if (strcmp(sapi_module.name, *sapi_name) == 0) {
854                _php_libxml_per_request_initialization = 0;
855                break;
856            }
857        }
858    }
859
860    if (!_php_libxml_per_request_initialization) {
861        /* report errors via handler rather than stderr */
862        xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
863        xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
864        xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
865    }
866
867    return SUCCESS;
868}
869
870
871static PHP_RINIT_FUNCTION(libxml)
872{
873    if (_php_libxml_per_request_initialization) {
874        /* report errors via handler rather than stderr */
875        xmlSetGenericErrorFunc(NULL, php_libxml_error_handler);
876        xmlParserInputBufferCreateFilenameDefault(php_libxml_input_buffer_create_filename);
877        xmlOutputBufferCreateFilenameDefault(php_libxml_output_buffer_create_filename);
878
879        /* Enable the entity loader by default. This ensures that
880         * other threads/requests that might have disabled the loader
881         * do not affect the current request.
882         */
883        LIBXML(entity_loader_disabled) = 0;
884    }
885    return SUCCESS;
886}
887
888static PHP_RSHUTDOWN_FUNCTION(libxml)
889{
890    _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
891
892    return SUCCESS;
893}
894
895static PHP_MSHUTDOWN_FUNCTION(libxml)
896{
897    if (!_php_libxml_per_request_initialization) {
898        xmlSetGenericErrorFunc(NULL, NULL);
899
900        xmlParserInputBufferCreateFilenameDefault(NULL);
901        xmlOutputBufferCreateFilenameDefault(NULL);
902    }
903    php_libxml_shutdown();
904
905    return SUCCESS;
906}
907
908static int php_libxml_post_deactivate(void)
909{
910    /* reset libxml generic error handling */
911    if (_php_libxml_per_request_initialization) {
912        xmlSetGenericErrorFunc(NULL, NULL);
913
914        xmlParserInputBufferCreateFilenameDefault(NULL);
915        xmlOutputBufferCreateFilenameDefault(NULL);
916    }
917    xmlSetStructuredErrorFunc(NULL, NULL);
918
919    /* the steam_context resource will be released by resource list destructor */
920    ZVAL_UNDEF(&LIBXML(stream_context));
921    smart_str_free(&LIBXML(error_buffer));
922    if (LIBXML(error_list)) {
923        zend_llist_destroy(LIBXML(error_list));
924        efree(LIBXML(error_list));
925        LIBXML(error_list) = NULL;
926    }
927    xmlResetLastError();
928
929    return SUCCESS;
930}
931
932
933static PHP_MINFO_FUNCTION(libxml)
934{
935    php_info_print_table_start();
936    php_info_print_table_row(2, "libXML support", "active");
937    php_info_print_table_row(2, "libXML Compiled Version", LIBXML_DOTTED_VERSION);
938    php_info_print_table_row(2, "libXML Loaded Version", (char *)xmlParserVersion);
939    php_info_print_table_row(2, "libXML streams", "enabled");
940    php_info_print_table_end();
941}
942/* }}} */
943
944/* {{{ proto void libxml_set_streams_context(resource streams_context)
945   Set the streams context for the next libxml document load or write */
946static PHP_FUNCTION(libxml_set_streams_context)
947{
948    zval *arg;
949
950    if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &arg) == FAILURE) {
951        return;
952    }
953    if (!Z_ISUNDEF(LIBXML(stream_context))) {
954        zval_ptr_dtor(&LIBXML(stream_context));
955        ZVAL_UNDEF(&LIBXML(stream_context));
956    }
957    ZVAL_COPY(&LIBXML(stream_context), arg);
958}
959/* }}} */
960
961/* {{{ proto bool libxml_use_internal_errors([boolean use_errors])
962   Disable libxml errors and allow user to fetch error information as needed */
963static PHP_FUNCTION(libxml_use_internal_errors)
964{
965    xmlStructuredErrorFunc current_handler;
966    zend_bool use_errors=0, retval;
967
968    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &use_errors) == FAILURE) {
969        return;
970    }
971
972    current_handler = xmlStructuredError;
973    if (current_handler && current_handler == php_libxml_structured_error_handler) {
974        retval = 1;
975    } else {
976        retval = 0;
977    }
978
979    if (ZEND_NUM_ARGS() == 0) {
980        RETURN_BOOL(retval);
981    }
982
983    if (use_errors == 0) {
984        xmlSetStructuredErrorFunc(NULL, NULL);
985        if (LIBXML(error_list)) {
986            zend_llist_destroy(LIBXML(error_list));
987            efree(LIBXML(error_list));
988            LIBXML(error_list) = NULL;
989        }
990    } else {
991        xmlSetStructuredErrorFunc(NULL, php_libxml_structured_error_handler);
992        if (LIBXML(error_list) == NULL) {
993            LIBXML(error_list) = (zend_llist *) emalloc(sizeof(zend_llist));
994            zend_llist_init(LIBXML(error_list), sizeof(xmlError), (llist_dtor_func_t) _php_libxml_free_error, 0);
995        }
996    }
997    RETURN_BOOL(retval);
998}
999/* }}} */
1000
1001/* {{{ proto object libxml_get_last_error()
1002   Retrieve last error from libxml */
1003static PHP_FUNCTION(libxml_get_last_error)
1004{
1005    xmlErrorPtr error;
1006
1007    error = xmlGetLastError();
1008
1009    if (error) {
1010        object_init_ex(return_value, libxmlerror_class_entry);
1011        add_property_long(return_value, "level", error->level);
1012        add_property_long(return_value, "code", error->code);
1013        add_property_long(return_value, "column", error->int2);
1014        if (error->message) {
1015            add_property_string(return_value, "message", error->message);
1016        } else {
1017            add_property_stringl(return_value, "message", "", 0);
1018        }
1019        if (error->file) {
1020            add_property_string(return_value, "file", error->file);
1021        } else {
1022            add_property_stringl(return_value, "file", "", 0);
1023        }
1024        add_property_long(return_value, "line", error->line);
1025    } else {
1026        RETURN_FALSE;
1027    }
1028}
1029/* }}} */
1030
1031/* {{{ proto object libxml_get_errors()
1032   Retrieve array of errors */
1033static PHP_FUNCTION(libxml_get_errors)
1034{
1035
1036    xmlErrorPtr error;
1037
1038    if (array_init(return_value) == FAILURE) {
1039        RETURN_FALSE;
1040    }
1041
1042    if (LIBXML(error_list)) {
1043
1044        error = zend_llist_get_first(LIBXML(error_list));
1045
1046        while (error != NULL) {
1047            zval z_error;
1048
1049            object_init_ex(&z_error, libxmlerror_class_entry);
1050            add_property_long_ex(&z_error, "level", sizeof("level") - 1, error->level);
1051            add_property_long_ex(&z_error, "code", sizeof("code") - 1, error->code);
1052            add_property_long_ex(&z_error, "column", sizeof("column") - 1, error->int2 );
1053            if (error->message) {
1054                add_property_string_ex(&z_error, "message", sizeof("message") - 1, error->message);
1055            } else {
1056                add_property_stringl_ex(&z_error, "message", sizeof("message") - 1, "", 0);
1057            }
1058            if (error->file) {
1059                add_property_string_ex(&z_error, "file", sizeof("file") - 1, error->file);
1060            } else {
1061                add_property_stringl_ex(&z_error, "file", sizeof("file") - 1, "", 0);
1062            }
1063            add_property_long_ex(&z_error, "line", sizeof("line") - 1, error->line);
1064            add_next_index_zval(return_value, &z_error);
1065
1066            error = zend_llist_get_next(LIBXML(error_list));
1067        }
1068    }
1069}
1070/* }}} */
1071
1072/* {{{ proto void libxml_clear_errors()
1073   Clear last error from libxml */
1074static PHP_FUNCTION(libxml_clear_errors)
1075{
1076    xmlResetLastError();
1077    if (LIBXML(error_list)) {
1078        zend_llist_clean(LIBXML(error_list));
1079    }
1080}
1081/* }}} */
1082
1083PHP_LIBXML_API zend_bool php_libxml_disable_entity_loader(zend_bool disable) /* {{{ */
1084{
1085    zend_bool old = LIBXML(entity_loader_disabled);
1086
1087    LIBXML(entity_loader_disabled) = disable;
1088    return old;
1089} /* }}} */
1090
1091/* {{{ proto bool libxml_disable_entity_loader([boolean disable])
1092   Disable/Enable ability to load external entities */
1093static PHP_FUNCTION(libxml_disable_entity_loader)
1094{
1095    zend_bool disable = 1;
1096
1097    if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &disable) == FAILURE) {
1098        return;
1099    }
1100
1101    RETURN_BOOL(php_libxml_disable_entity_loader(disable));
1102}
1103/* }}} */
1104
1105/* {{{ proto void libxml_set_external_entity_loader(callback resolver_function)
1106   Changes the default external entity loader */
1107static PHP_FUNCTION(libxml_set_external_entity_loader)
1108{
1109    zend_fcall_info         fci;
1110    zend_fcall_info_cache   fcc;
1111    if (zend_parse_parameters(ZEND_NUM_ARGS(), "f!", &fci, &fcc)
1112            == FAILURE) {
1113        return;
1114    }
1115
1116    _php_libxml_destroy_fci(&LIBXML(entity_loader).fci, &LIBXML(entity_loader).object);
1117
1118    if (fci.size > 0) { /* argument not null */
1119        LIBXML(entity_loader).fci = fci;
1120        Z_ADDREF(fci.function_name);
1121        if (fci.object != NULL) {
1122            ZVAL_OBJ(&LIBXML(entity_loader).object, fci.object);
1123            Z_ADDREF(LIBXML(entity_loader).object);
1124        }
1125        LIBXML(entity_loader).fcc = fcc;
1126    }
1127
1128    RETURN_TRUE;
1129}
1130/* }}} */
1131
1132/* {{{ Common functions shared by extensions */
1133int php_libxml_xmlCheckUTF8(const unsigned char *s)
1134{
1135    int i;
1136    unsigned char c;
1137
1138    for (i = 0; (c = s[i++]);) {
1139        if ((c & 0x80) == 0) {
1140        } else if ((c & 0xe0) == 0xc0) {
1141            if ((s[i++] & 0xc0) != 0x80) {
1142                return 0;
1143            }
1144        } else if ((c & 0xf0) == 0xe0) {
1145            if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1146                return 0;
1147            }
1148        } else if ((c & 0xf8) == 0xf0) {
1149            if ((s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80 || (s[i++] & 0xc0) != 0x80) {
1150                return 0;
1151            }
1152        } else {
1153            return 0;
1154        }
1155    }
1156    return 1;
1157}
1158
1159zval *php_libxml_register_export(zend_class_entry *ce, php_libxml_export_node export_function)
1160{
1161    php_libxml_func_handler export_hnd;
1162
1163    /* Initialize in case this module hasn't been loaded yet */
1164    php_libxml_initialize();
1165    export_hnd.export_func = export_function;
1166
1167    return zend_hash_add_mem(&php_libxml_exports, ce->name, &export_hnd, sizeof(export_hnd));
1168}
1169
1170PHP_LIBXML_API xmlNodePtr php_libxml_import_node(zval *object)
1171{
1172    zend_class_entry *ce = NULL;
1173    xmlNodePtr node = NULL;
1174    php_libxml_func_handler *export_hnd;
1175
1176    if (Z_TYPE_P(object) == IS_OBJECT) {
1177        ce = Z_OBJCE_P(object);
1178        while (ce->parent != NULL) {
1179            ce = ce->parent;
1180        }
1181        if ((export_hnd = zend_hash_find_ptr(&php_libxml_exports, ce->name))) {
1182            node = export_hnd->export_func(object);
1183        }
1184    }
1185    return node;
1186}
1187
1188PHP_LIBXML_API int php_libxml_increment_node_ptr(php_libxml_node_object *object, xmlNodePtr node, void *private_data)
1189{
1190    int ret_refcount = -1;
1191
1192    if (object != NULL && node != NULL) {
1193        if (object->node != NULL) {
1194            if (object->node->node == node) {
1195                return object->node->refcount;
1196            } else {
1197                php_libxml_decrement_node_ptr(object);
1198            }
1199        }
1200        if (node->_private != NULL) {
1201            object->node = node->_private;
1202            ret_refcount = ++object->node->refcount;
1203            /* Only dom uses _private */
1204            if (object->node->_private == NULL) {
1205                object->node->_private = private_data;
1206            }
1207        } else {
1208            ret_refcount = 1;
1209            object->node = emalloc(sizeof(php_libxml_node_ptr));
1210            object->node->node = node;
1211            object->node->refcount = 1;
1212            object->node->_private = private_data;
1213            node->_private = object->node;
1214        }
1215    }
1216
1217    return ret_refcount;
1218}
1219
1220PHP_LIBXML_API int php_libxml_decrement_node_ptr(php_libxml_node_object *object)
1221{
1222    int ret_refcount = -1;
1223    php_libxml_node_ptr *obj_node;
1224
1225    if (object != NULL && object->node != NULL) {
1226        obj_node = (php_libxml_node_ptr *) object->node;
1227        ret_refcount = --obj_node->refcount;
1228        if (ret_refcount == 0) {
1229            if (obj_node->node != NULL) {
1230                obj_node->node->_private = NULL;
1231            }
1232            efree(obj_node);
1233        }
1234        object->node = NULL;
1235    }
1236
1237    return ret_refcount;
1238}
1239
1240PHP_LIBXML_API int php_libxml_increment_doc_ref(php_libxml_node_object *object, xmlDocPtr docp)
1241{
1242    int ret_refcount = -1;
1243
1244    if (object->document != NULL) {
1245        object->document->refcount++;
1246        ret_refcount = object->document->refcount;
1247    } else if (docp != NULL) {
1248        ret_refcount = 1;
1249        object->document = emalloc(sizeof(php_libxml_ref_obj));
1250        object->document->ptr = docp;
1251        object->document->refcount = ret_refcount;
1252        object->document->doc_props = NULL;
1253    }
1254
1255    return ret_refcount;
1256}
1257
1258PHP_LIBXML_API int php_libxml_decrement_doc_ref(php_libxml_node_object *object)
1259{
1260    int ret_refcount = -1;
1261
1262    if (object != NULL && object->document != NULL) {
1263        ret_refcount = --object->document->refcount;
1264        if (ret_refcount == 0) {
1265            if (object->document->ptr != NULL) {
1266                xmlFreeDoc((xmlDoc *) object->document->ptr);
1267            }
1268            if (object->document->doc_props != NULL) {
1269                if (object->document->doc_props->classmap) {
1270                    zend_hash_destroy(object->document->doc_props->classmap);
1271                    FREE_HASHTABLE(object->document->doc_props->classmap);
1272                }
1273                efree(object->document->doc_props);
1274            }
1275            efree(object->document);
1276            object->document = NULL;
1277        }
1278    }
1279
1280    return ret_refcount;
1281}
1282
1283PHP_LIBXML_API void php_libxml_node_free_resource(xmlNodePtr node)
1284{
1285    if (!node) {
1286        return;
1287    }
1288
1289    switch (node->type) {
1290        case XML_DOCUMENT_NODE:
1291        case XML_HTML_DOCUMENT_NODE:
1292            break;
1293        default:
1294            if (node->parent == NULL || node->type == XML_NAMESPACE_DECL) {
1295                php_libxml_node_free_list((xmlNodePtr) node->children);
1296                switch (node->type) {
1297                    /* Skip property freeing for the following types */
1298                    case XML_ATTRIBUTE_DECL:
1299                    case XML_DTD_NODE:
1300                    case XML_DOCUMENT_TYPE_NODE:
1301                    case XML_ENTITY_DECL:
1302                    case XML_ATTRIBUTE_NODE:
1303                    case XML_NAMESPACE_DECL:
1304                    case XML_TEXT_NODE:
1305                        break;
1306                    default:
1307                        php_libxml_node_free_list((xmlNodePtr) node->properties);
1308                }
1309                if (php_libxml_unregister_node(node) == 0) {
1310                    node->doc = NULL;
1311                }
1312                php_libxml_node_free(node);
1313            } else {
1314                php_libxml_unregister_node(node);
1315            }
1316    }
1317}
1318
1319PHP_LIBXML_API void php_libxml_node_decrement_resource(php_libxml_node_object *object)
1320{
1321    int ret_refcount = -1;
1322    xmlNodePtr nodep;
1323    php_libxml_node_ptr *obj_node;
1324
1325    if (object != NULL && object->node != NULL) {
1326        obj_node = (php_libxml_node_ptr *) object->node;
1327        nodep = object->node->node;
1328        ret_refcount = php_libxml_decrement_node_ptr(object);
1329        if (ret_refcount == 0) {
1330            php_libxml_node_free_resource(nodep);
1331        } else {
1332            if (obj_node && object == obj_node->_private) {
1333                obj_node->_private = NULL;
1334            }
1335        }
1336    }
1337    if (object != NULL && object->document != NULL) {
1338        /* Safe to call as if the resource were freed then doc pointer is NULL */
1339        php_libxml_decrement_doc_ref(object);
1340    }
1341}
1342/* }}} */
1343
1344#ifdef PHP_WIN32
1345PHP_LIBXML_API BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
1346{
1347    return xmlDllMain(hinstDLL, fdwReason, lpvReserved);
1348}
1349#endif
1350
1351#endif
1352
1353/*
1354 * Local variables:
1355 * tab-width: 4
1356 * c-basic-offset: 4
1357 * End:
1358 * vim600: sw=4 ts=4 fdm=marker
1359 * vim<600: sw=4 ts=4
1360 */
1361