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