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