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