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