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