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